Skip to content

Commit

Permalink
Issue #424: Add CLI options for specific query execution
Browse files Browse the repository at this point in the history
* Added IQuery interface to handle protocols queries.
* Developed SnmpCli executable which enables to execute SNMP get, getNext & walk commands through CLI.
* Tested the generated SNMP.exe with different commands.
  • Loading branch information
CherfaElyes committed Nov 28, 2024
1 parent 4a3c9aa commit 8f12edd
Show file tree
Hide file tree
Showing 23 changed files with 494 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public Integer call() throws Exception {
validate();

// Setup Log4j
setLogLevel();
setLogLevel(verbose);

final HostConfiguration hostConfiguration = HostConfiguration
.builder()
Expand Down Expand Up @@ -527,8 +527,10 @@ private void validate() {

/**
* Set Log4j logging level according to the verbose flags
*
* @param verbose array of boolean values specifying logger level.
*/
void setLogLevel() {
public static void setLogLevel(final boolean[] verbose) {
// Disable ANSI in the logging if we don't have a console
ThreadContext.put("disableAnsi", Boolean.toString(!ConsoleService.hasConsole()));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package org.sentrysoftware.metricshub.cli.snmp;

/*-
* ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
* MetricsHub Agent
* ჻჻჻჻჻჻
* Copyright 2023 - 2024 Sentry Software
* ჻჻჻჻჻჻
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
*/

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.stream.Stream;
import org.fusesource.jansi.AnsiConsole;
import org.sentrysoftware.metricshub.cli.service.CliExtensionManager;
import org.sentrysoftware.metricshub.cli.service.PrintExceptionMessageHandlerService;
import org.sentrysoftware.metricshub.cli.service.protocol.SnmpConfigCli;
import org.sentrysoftware.metricshub.engine.common.IQuery;
import org.sentrysoftware.metricshub.engine.configuration.IConfiguration;
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.Spec;

public class SnmpCli implements IQuery, Callable<Integer> {

@Parameters(index = "0", paramLabel = "HOSTNAME", description = "Hostname or IP address of the host to monitor")
String hostname;

@Spec
CommandSpec spec;

@Option(names = "--snmp-get", order = 1, paramLabel = "OID", description = "SNMP Get request")
private String get;

@Option(names = "--snmp-getnext", order = 2, paramLabel = "OID", description = "SNMP Get Next request")
private String getNext;

@Option(names = "--snmp-walk", order = 3, paramLabel = "OID", description = "SNMP Walk request")
private String walk;

@ArgGroup(exclusive = false, heading = "%n@|bold,underline SNMP Options|@:%n")
SnmpConfigCli snmpConfigCli;

@Option(names = { "-h", "-?", "--help" }, usageHelp = true, description = "Shows this help message and exits")
boolean usageHelpRequested;

@Option(names = "-v", order = 7, description = "Verbose mode (repeat the option to increase verbosity)")
boolean[] verbose;

public JsonNode getQuery() {
final ObjectNode queryNode = JsonNodeFactory.instance.objectNode();
String action;
String oid;

if (get != null) {
action = "get";
oid = get;
} else if (getNext != null) {
action = "getNext";
oid = getNext;
} else {
action = "walk";
oid = walk;
}

queryNode.set("action", new TextNode(action));
queryNode.set("oid", new TextNode(oid));

return queryNode;
}

void validate() throws ParameterException {

if (snmpConfigCli == null) {
throw new ParameterException(
spec.commandLine(),
"SNMP protocol must be configured: --snmp."
);
}

final boolean queriesNotConfigured = Stream.of(get, getNext, walk).allMatch(Objects::isNull);

if (queriesNotConfigured) {
throw new ParameterException(
spec.commandLine(),
"At least one SNMP query must be specified: --snmp-get, --snmp-getnext, --snmp-walk."
);
}
}

public static void main(String[] args) {
System.setProperty("log4j2.configurationFile", "log4j2-cli.xml");

// Enable colors on Windows terminal
AnsiConsole.systemInstall();

final CommandLine cli = new CommandLine(new SnmpCli());

// Keep the below line commented for future reference
// Using JAnsi on Windows breaks the output of Unicode (UTF-8) chars
// It can be fixed using the below line... when running in Windows Terminal
// and not CMD.EXE.
// As this is poorly documented, we keep this for future improvement.
// cli.setOut(new PrintWriter(AnsiConsole.out(), true, StandardCharsets.UTF_8)); // NOSONAR on commented code

// Set the exception handler
cli.setExecutionExceptionHandler(new PrintExceptionMessageHandlerService());

// Allow case insensitive enum values
cli.setCaseInsensitiveEnumValuesAllowed(true);

// Execute the command
final int exitCode = cli.execute(args);

// Cleanup Windows terminal settings
AnsiConsole.systemUninstall();

System.exit(exitCode);
}

@Override
public Integer call() throws Exception {
final PrintWriter printWriter = spec.commandLine().getOut();
validate();
CliExtensionManager
.getExtensionManagerSingleton()
.findExtensionByType("snmp")
.ifPresent(extension -> {
try {
IConfiguration protocol = snmpConfigCli.toProtocol(null, null);
protocol.setHostname(hostname);
extension.executeQuery(protocol, getQuery(), printWriter);
} catch (Exception e) {
printWriter.print("Invalid configuration detected");
printWriter.flush();
throw new IllegalStateException("Invalid configuration detected.", e);
}
});
return CommandLine.ExitCode.OK;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,20 @@ void testListAllConnectors() {

@Test
void testSetLogLevel() {
final MetricsHubCliService metricsHubCliService = new MetricsHubCliService();

assertDoesNotThrow(() -> {
metricsHubCliService.verbose = new boolean[] {};
metricsHubCliService.setLogLevel();
MetricsHubCliService.setLogLevel(new boolean[] {});
});
assertDoesNotThrow(() -> {
metricsHubCliService.verbose = new boolean[] { true };
metricsHubCliService.setLogLevel();
MetricsHubCliService.setLogLevel(new boolean[] { true });
});
assertDoesNotThrow(() -> {
metricsHubCliService.verbose = new boolean[] { true, true };
metricsHubCliService.setLogLevel();
MetricsHubCliService.setLogLevel(new boolean[] { true, true });
});
assertDoesNotThrow(() -> {
metricsHubCliService.verbose = new boolean[] { true, true, true };
metricsHubCliService.setLogLevel();
MetricsHubCliService.setLogLevel(new boolean[] { true, true, true });
});
assertDoesNotThrow(() -> {
metricsHubCliService.verbose = new boolean[] { true, true, true, true };
metricsHubCliService.setLogLevel();
MetricsHubCliService.setLogLevel(new boolean[] { true, true, true, true });
});
}

Expand Down
2 changes: 1 addition & 1 deletion metricshub-doc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<plugin>
<groupId>org.sentrysoftware.maven</groupId>
<artifactId>metricshub-connector-maven-plugin</artifactId>
<version>1.0.05</version>
<version>1.0.07-SNAPSHOT</version>
<configuration>
<sourceDirectory>${project.basedir}/../metricshub-agent/target/connectors</sourceDirectory>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.sentrysoftware.metricshub.engine.common;

/*-
* ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
* MetricsHub Engine
* ჻჻჻჻჻჻
* Copyright 2023 - 2024 Sentry Software
* ჻჻჻჻჻჻
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
*/
public interface IQuery {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

import com.fasterxml.jackson.databind.JsonNode;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -149,4 +150,14 @@ IConfiguration buildConfiguration(String configurationType, JsonNode jsonNode, U
* @return The protocol identifier as a string.
*/
String getIdentifier();

/**
* Executes a query based on the provided configuration and query parameters.
*
* @param configuration the IConfiguration object containing the configuration details.
* @param query a JsonNode representing the query to be executed.
* @param printWriter the PrintWriter used to display the result.
* @throws Exception if the query execution fails due to an error or unexpected condition.
*/
String executeQuery(IConfiguration configuration, JsonNode query, PrintWriter printWriter) throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -217,4 +218,10 @@ public static JsonMapper newObjectMapper() {
public String getIdentifier() {
return IDENTIFIER;
}

@Override
public String executeQuery(IConfiguration configuration, JsonNode query, PrintWriter printWriter) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -259,4 +260,10 @@ public static JsonMapper newObjectMapper() {
public String getIdentifier() {
return IDENTIFIER;
}

@Override
public String executeQuery(IConfiguration configuration, JsonNode query, PrintWriter printWriter) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
2 changes: 2 additions & 0 deletions metricshub-linux/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@
<argument>metricshub-encrypt=metricshub-encrypt.properties</argument>
<argument>--add-launcher</argument>
<argument>service=metricshub-agent.properties</argument>
<argument>--add-launcher</argument>
<argument>snmp=metricshub-snmp.properties</argument>
<argument>@jpackage.txt</argument>
</arguments>
<workingDirectory>${project.build.directory}/jpackage</workingDirectory>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
main-class=org.sentrysoftware.metricshub.cli.snmp.SnmpCli
java-options=-javaagent:$APPDIR/../extensions/metricshub-classloader-agent-${project.version}.jar --add-exports java.xml/com.sun.org.apache.xerces.internal.parsers=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/sun.net.www.protocol.http=ALL-UNNAMED --add-opens java.base/sun.security.ssl=ALL-UNNAMED
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -311,4 +312,10 @@ public static JsonMapper newObjectMapper() {
public String getIdentifier() {
return "ssh";
}

@Override
public String executeQuery(IConfiguration configuration, JsonNode query, PrintWriter printWriter) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -167,4 +168,10 @@ public static JsonMapper newObjectMapper() {
public String getIdentifier() {
return IDENTIFIER;
}

@Override
public String executeQuery(IConfiguration configuration, JsonNode query, PrintWriter printWriter) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Loading

0 comments on commit 8f12edd

Please sign in to comment.