-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #424: Add CLI options for specific query execution
* 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
1 parent
4a3c9aa
commit c6440e5
Showing
25 changed files
with
635 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
metricshub-agent/src/main/java/org/sentrysoftware/metricshub/cli/snmp/SnmpCli.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
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 lombok.Data; | ||
|
||
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; | ||
|
||
@Data | ||
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") | ||
String get; | ||
|
||
@Option(names = "--snmp-getnext", order = 2, paramLabel = "OID", description = "SNMP Get Next request") | ||
String getNext; | ||
|
||
@Option(names = "--snmp-walk", order = 3, paramLabel = "OID", description = "SNMP Walk request") | ||
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."); | ||
} | ||
|
||
Stream.of(get, getNext, walk) | ||
.filter(Objects::nonNull) | ||
.reduce((a, b) -> { | ||
throw new ParameterException( | ||
spec.commandLine(), | ||
"Only one SNMP query can be specified at a time: --snmp-get, --snmp-getnext, --snmp-walk." | ||
); | ||
}) | ||
.orElseThrow(() -> 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
metricshub-agent/src/test/java/org/sentrysoftware/metricshub/cli/snmp/SnmpCliTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package org.sentrysoftware.metricshub.cli.snmp; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.sentrysoftware.metricshub.cli.service.protocol.SnmpConfigCli; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import picocli.CommandLine; | ||
import picocli.CommandLine.ParameterException; | ||
|
||
class SnmpCliTest { | ||
|
||
SnmpCli snmpCli; | ||
CommandLine commandLine; | ||
|
||
static String SNMP_OID = "1.3.6.1.4.1.674.10892.5.5.1.20.130.4"; | ||
static String SNMP_VERSION = "v2c"; | ||
static String SNMP_COMMUNITY = "public"; | ||
|
||
void initCli() { | ||
snmpCli = new SnmpCli(); | ||
commandLine = new CommandLine(snmpCli); | ||
} | ||
|
||
void initSnmpGet() { | ||
initCli(); | ||
|
||
commandLine.execute( | ||
"hostname", | ||
"--snmp-get", | ||
SNMP_OID, | ||
"--snmp", | ||
SNMP_VERSION, | ||
"--community", | ||
SNMP_COMMUNITY, | ||
"--retry", | ||
"1000", | ||
"--retry", | ||
"6000" | ||
); | ||
} | ||
|
||
void initSnmpGetNext() { | ||
initCli(); | ||
|
||
commandLine.execute( | ||
"hostname", | ||
"--snmp-getnext", | ||
SNMP_OID, | ||
"--snmp", | ||
SNMP_VERSION, | ||
"--community", | ||
SNMP_COMMUNITY, | ||
"--retry", | ||
"1000", | ||
"--retry", | ||
"6000" | ||
); | ||
} | ||
|
||
void initSnmpWalk() { | ||
initCli(); | ||
|
||
commandLine.execute( | ||
"hostname", | ||
"--snmp-walk", | ||
SNMP_OID, | ||
"--snmp", | ||
SNMP_VERSION, | ||
"--community", | ||
SNMP_COMMUNITY, | ||
"--retry", | ||
"1000", | ||
"--retry", | ||
"6000" | ||
); | ||
} | ||
@Test | ||
void testExecute() { | ||
initSnmpGet(); | ||
assertEquals(SNMP_OID, snmpCli.get); | ||
initSnmpGetNext(); | ||
assertEquals(SNMP_OID, snmpCli.getNext); | ||
initSnmpWalk(); | ||
assertEquals(SNMP_OID, snmpCli.walk); | ||
} | ||
|
||
@Test | ||
void testGetQuery() { | ||
initSnmpGet(); | ||
JsonNode snmpQuery = snmpCli.getQuery(); | ||
assertEquals("get", snmpQuery.get("action").asText()); | ||
assertEquals(SNMP_OID, snmpQuery.get("oid").asText()); | ||
|
||
initSnmpGetNext(); | ||
snmpQuery = snmpCli.getQuery(); | ||
assertEquals("getNext", snmpQuery.get("action").asText()); | ||
assertEquals(SNMP_OID, snmpQuery.get("oid").asText()); | ||
|
||
initSnmpWalk(); | ||
snmpQuery = snmpCli.getQuery(); | ||
assertEquals("walk", snmpQuery.get("action").asText()); | ||
assertEquals(SNMP_OID, snmpQuery.get("oid").asText()); | ||
} | ||
|
||
@Test | ||
void testValidate() { | ||
initCli(); | ||
ParameterException snmpConfigException = assertThrows(ParameterException.class, () -> snmpCli.validate()); | ||
assertEquals("SNMP protocol must be configured: --snmp.", snmpConfigException.getMessage()); | ||
snmpCli.setSnmpConfigCli(new SnmpConfigCli()); | ||
ParameterException noQueriesException = assertThrows(ParameterException.class, () -> snmpCli.validate()); | ||
assertEquals("At least one SNMP query must be specified: --snmp-get, --snmp-getnext, --snmp-walk.", noQueriesException.getMessage()); | ||
snmpCli.setGet(SNMP_OID); | ||
assertDoesNotThrow(() -> snmpCli.validate()); | ||
snmpCli.setGetNext(SNMP_OID); | ||
ParameterException manyQueriesException = assertThrows(ParameterException.class, () -> snmpCli.validate()); | ||
assertEquals("Only one SNMP query can be specified at a time: --snmp-get, --snmp-getnext, --snmp-walk.", manyQueriesException.getMessage()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.