modules/enterprise/server/plugins/alert-snmp/pom.xml
| 32
modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfo.java
| 59 +
modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java
| 32
modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpTrapSender.java
| 38 -
modules/enterprise/server/plugins/alert-snmp/src/main/resources/META-INF/rhq-serverplugin.xml
| 36
modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfoTest.java
| 106 ++
modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSenderTest.java
| 378 ++++++++++
modules/enterprise/server/plugins/alert-snmp/src/test/resources/log4j.xml
| 32
8 files changed, 671 insertions(+), 42 deletions(-)
New commits:
commit 00be4f6ba09fd8f6a1cf55a99a4fd1f6f25130d6
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Apr 16 22:04:50 2013 +0200
Bug 593932 - (PRODMGT-355) Allow Trap OID for SNMP notification to be overridden
per-alert definition
Snmp trap OID is set on v2c and v3 traps as the second variable binding.
In the past, users had been confused by the name of the SNMP notification parameter
'oid'. This parameter actually defines a variable binding prefix for the RHQ alert
bindings.
Added tests for this server plugin
Added documentation to the plugin descriptor
diff --git a/modules/enterprise/server/plugins/alert-snmp/pom.xml
b/modules/enterprise/server/plugins/alert-snmp/pom.xml
index 909053a..b09ed2a 100644
--- a/modules/enterprise/server/plugins/alert-snmp/pom.xml
+++ b/modules/enterprise/server/plugins/alert-snmp/pom.xml
@@ -13,12 +13,32 @@
<name>RHQ Enterprise Server SNMP Alert Plugin</name>
+ <properties>
+ <alert.snmp.test.port>35162</alert.snmp.test.port>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>1.8.2</version>
</dependency>
+
+ <!-- Test dependencies -->
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>test-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ <version>1.9.5</version>
+ </dependency>
+
</dependencies>
<build>
@@ -27,10 +47,14 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
-
<excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
- <!--
- <argLine>${jacoco.unit-test.args} -Xdebug -Xnoagent
-Djava.compiler=NONE
-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y</argLine>
- -->
+ <properties>
+ <property>
+ <name>listener</name>
+ <value>org.rhq.test.testng.StdoutReporter</value>
+ </property>
+ </properties>
+ <useSystemClassLoader>false</useSystemClassLoader>
+ <argLine>${jacoco.unit-test.args}</argLine>
</configuration>
</plugin>
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfo.java
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfo.java
index 082e706..c92daae 100644
---
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfo.java
+++
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfo.java
@@ -1,19 +1,55 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.enterprise.server.plugins.alertSnmp;
import org.rhq.core.domain.configuration.Configuration;
+/**
+ * Utility class giving a view of SNMP alert notification parameters.
+ *
+ * @deprecated as of 4.7. It's an internal utility class and should not have been
made public.
+ */
+@Deprecated
public class SnmpInfo {
+ // SNMP Alert Notification parameter names
+ static final String PARAM_HOST = "host";
+ static final String PARAM_PORT = "port";
+ static final String PARAM_VARIABLE_BINDING_PREFIX = "oid";
+ static final String PARAM_TRAP_OID = "trapOid";
+
+ // Default remote port for SNMP trap
+ static final String DEFAULT_PORT = "162";
+
final public String host;
final public String port;
final public String oid;
+ final public String trapOid;
final public String error;
- private SnmpInfo(String host, String port, String oid) {
+ private SnmpInfo(String host, String port, String oid, String trapOid) {
this.host = host;
this.port = port;
this.oid = oid;
+ this.trapOid = trapOid;
String error = null;
if (oid == null) {
@@ -29,17 +65,26 @@ public class SnmpInfo {
this.error = error;
}
+ /**
+ * Creates an instance of {@link SnmpInfo} from alert definition parameters.
+ *
+ * @param configuration SNMP notification parameters in a {@link Configuration}
object
+ * @return instance of {@link SnmpInfo}
+ */
public static SnmpInfo load(Configuration configuration) {
- String host = configuration.getSimpleValue("host", null); // required
- String port = configuration.getSimpleValue("port", "162");
- String oid = configuration.getSimpleValue("oid", null); // required
-
- return new SnmpInfo(host, port, oid);
+ String host = configuration.getSimpleValue(PARAM_HOST, null); // required
+ String port = configuration.getSimpleValue(PARAM_PORT, DEFAULT_PORT);
+ String oid = configuration.getSimpleValue(PARAM_VARIABLE_BINDING_PREFIX, null);
// required
+ String trapOid = configuration.getSimpleValue(PARAM_TRAP_OID, null);
+ return new SnmpInfo(host, port, oid, trapOid);
}
+ @Override
public String toString() {
String hostString = (host == null ? "UnknownHost" : host);
String oidString = (oid == null ? "UnknownOID" : oid);
- return hostString + ":" + port + " (" + oidString +
")";
+ String trapOidString = (trapOid == null ? "DefaultTrapOID" : trapOid);
+ return hostString + ":" + port + " (" + oidString + ")
(" + trapOidString + ")";
}
+
}
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java
index 1ea5e0b..5c6d1e7 100644
---
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java
+++
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2009 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.server.plugins.alertSnmp;
@@ -38,7 +38,23 @@ import org.rhq.enterprise.server.util.LookupUtil;
*/
public class SnmpSender extends AlertSender {
- private final Log log = LogFactory.getLog(SnmpSender.class);
+ private static final Log LOG = LogFactory.getLog(SnmpSender.class);
+
+ private ResourceManagerLocal resourceManager;
+
+ private AlertManagerLocal alertManager;
+
+ /**
+ * Default constructor needed for instanciation by server plugin container
+ */
+ public SnmpSender() {
+ this(LookupUtil.getResourceManager(), LookupUtil.getAlertManager());
+ }
+
+ public SnmpSender(ResourceManagerLocal resourceManager, AlertManagerLocal
alertManager) {
+ this.resourceManager = resourceManager;
+ this.alertManager = alertManager;
+ }
@Override
public SenderResult send(Alert alert) {
@@ -47,12 +63,11 @@ public class SnmpSender extends AlertSender {
if (info.error != null) {
return SenderResult.getSimpleFailure(info.error);
}
- log.debug("Sending SNMP trap to: " + info);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Sending SNMP trap to: " + info);
+ }
try {
- AlertManagerLocal alertManager = LookupUtil.getAlertManager();
- ResourceManagerLocal resourceManager = LookupUtil.getResourceManager();
-
SnmpTrapSender snmpTrapSender = new SnmpTrapSender(preferences);
List<Resource> lineage =
resourceManager.getResourceLineage(alert.getAlertDefinition().getResource()
@@ -69,6 +84,7 @@ public class SnmpSender extends AlertSender {
alertUrl, hierarchy);
return SenderResult.getSimpleSuccess(result);
} catch (Throwable t) {
+ LOG.error("Could not send SNMP trap to " + info, t);
return SenderResult.getSimpleFailure("failed - cause: " + t);
}
}
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpTrapSender.java
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpTrapSender.java
index 92fc838..2556b38 100644
---
a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpTrapSender.java
+++
b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpTrapSender.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.server.plugins.alertSnmp;
@@ -72,6 +72,7 @@ import org.snmp4j.util.PDUFactory;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.util.StringUtil;
/**
* @author Ian Springer
@@ -135,12 +136,13 @@ public class SnmpTrapSender implements PDUFactory {
}
private void checkTrapVariables(List<VariableBinding> vbs) {
-
+ // Only on SNMP v2c or v3 trap
if ((pduType == PDU.INFORM) || (pduType == PDU.TRAP)) {
+ // Insert sysUpTime OID if not already present
if ((vbs.size() == 0) || ((vbs.size() >= 1) &&
(!(vbs.get(0)).getOid().equals(SnmpConstants.sysUpTime)))) {
vbs.add(0, new VariableBinding(SnmpConstants.sysUpTime, sysUpTime));
}
-
+ // Insert trap OID if not already present
if ((vbs.size() == 1) || ((vbs.size() >= 2) &&
(!(vbs.get(1)).getOid().equals(SnmpConstants.snmpTrapOID)))) {
vbs.add(1, new VariableBinding(SnmpConstants.snmpTrapOID, trapOID));
}
@@ -469,27 +471,28 @@ public class SnmpTrapSender implements PDUFactory {
return "SNMP is not enabled.";
}
- String baseOid = alertParameters.getSimpleValue("oid",null);
+ String variableBindingPrefix =
alertParameters.getSimpleValue(SnmpInfo.PARAM_VARIABLE_BINDING_PREFIX, null);
// TODO add a request id and a timestamp
this.address = createAddress(alertParameters);
// bind the alert definitions name on the oid set in the alert
- getVariableBindings(baseOid + ".1" + "={s}" +
alert.getAlertDefinition().getName());
+ getVariableBindings(variableBindingPrefix + ".1" + "={s}" +
alert.getAlertDefinition().getName());
// the resource the alert was defined on
- getVariableBindings(baseOid + ".2" + "={s}" +
alert.getAlertDefinition().getResource().getName());
+ getVariableBindings(variableBindingPrefix + ".2" + "={s}" +
alert.getAlertDefinition().getResource().getName());
// the platform this resource is on
- getVariableBindings(baseOid + ".3" + "={s}" + platformName);
+ getVariableBindings(variableBindingPrefix + ".3" + "={s}" +
platformName);
// the conditions of this alert
- getVariableBindings(baseOid + ".4" + "={s}" + conditions);
+ getVariableBindings(variableBindingPrefix + ".4" + "={s}" +
conditions);
// severity of the alert
- getVariableBindings(baseOid + ".5" + "={s}" +
alert.getAlertDefinition().getPriority().toString().toLowerCase());
+ getVariableBindings(variableBindingPrefix + ".5" + "={s}" +
alert.getAlertDefinition().getPriority().toString().toLowerCase());
// url of the alert detail
- getVariableBindings(baseOid + ".6" + "={s}" + alertUrl);
+ getVariableBindings(variableBindingPrefix + ".6" + "={s}" +
alertUrl);
// hierarchy of the resource on alert
- getVariableBindings(baseOid + ".7" + "={s}" + hierarchy);
+ getVariableBindings(variableBindingPrefix + ".7" + "={s}" +
hierarchy);
setSysUpTimeFromBootTime(bootTime); // needs to be called before
checkTrapVariables();
+ setTrapOIDFromAlertParameters(alertParameters); // needs to be called before
checkTrapVariables();
checkTrapVariables(this.vbs);
try {
PDU response = send();
@@ -533,6 +536,13 @@ public class SnmpTrapSender implements PDUFactory {
}
+ private void setTrapOIDFromAlertParameters(Configuration alertParameters) {
+ String trapOid = alertParameters.getSimpleValue(SnmpInfo.PARAM_TRAP_OID, null);
+ if (StringUtil.isNotBlank(trapOid)) {
+ setTrapOID(new OID(trapOid));
+ }
+ }
+
private boolean init() {
String snmpVersion = systemConfig.getSimpleValue("snmpVersion",null);
@@ -795,4 +805,4 @@ public class SnmpTrapSender implements PDUFactory {
public void setPduType(int pduType) {
this.pduType = pduType;
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/main/resources/META-INF/rhq-serverplugin.xml
b/modules/enterprise/server/plugins/alert-snmp/src/main/resources/META-INF/rhq-serverplugin.xml
index 32ec521..83a55e9 100644
---
a/modules/enterprise/server/plugins/alert-snmp/src/main/resources/META-INF/rhq-serverplugin.xml
+++
b/modules/enterprise/server/plugins/alert-snmp/src/main/resources/META-INF/rhq-serverplugin.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<alert-plugin
+<a:alert-plugin
name="alert-snmp"
displayName="Alert:SNMP"
- xmlns="urn:xmlns:rhq-serverplugin.alert"
+ xmlns:a="urn:xmlns:rhq-serverplugin.alert"
xmlns:c="urn:xmlns:rhq-configuration"
xmlns:serverplugin="urn:xmlns:rhq-serverplugin"
package="org.rhq.enterprise.server.plugins.alertSnmp"
@@ -65,15 +65,33 @@
</serverplugin:plugin-configuration>
<!-- How does this sender show up in drop downs etc -->
- <short-name>SNMP Traps</short-name>
+ <a:short-name>SNMP Traps</a:short-name>
<!-- Class that does the actual sending -->
- <plugin-class>SnmpSender</plugin-class>
+ <a:plugin-class>SnmpSender</a:plugin-class>
- <alert-configuration>
+ <a:alert-configuration>
<c:simple-property name="host" type="string"
required="true" description="Trap target host"/>
<c:simple-property name="port" type="integer"
required="false" default="162" description="Trap target
port"/>
- <c:simple-property name="oid" type="string"
required="true" description="OID of the trap sent"/>
-
- </alert-configuration>
-</alert-plugin>
+ <c:simple-property name="oid" displayName="Variable bindings
prefix" type="string" required="true">
+ <c:description>
+ <![CDATA[
+ <p>RHQ will send alert notification details as a list of variable
bindings in the
+ SNMP trap PDU.</p>
+ <p><strong>Do not confuse this paramater with 'Trap
Oid'.</strong></p>
+ ]]>
+ </c:description>
+ </c:simple-property>
+ <c:simple-property name="trapOid" required="false">
+ <c:description>
+ <![CDATA[
+ <p>If set, the value of this paramater will override the value of the
Trap Oid parameter defined globally
+ in the server plugin configuration. The Trap OID is sent as the second
variable binding in the SNMP trap
+ PDU.</p>
+ <p><strong>Only applicable to v2c and v3
traps.</strong></p>
+ <p><strong>Do not confuse this paramater with 'Variable
bindings prefix'.</strong></p>
+ ]]>
+ </c:description>
+ </c:simple-property>
+ </a:alert-configuration>
+</a:alert-plugin>
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfoTest.java
b/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfoTest.java
new file mode 100644
index 0000000..c46c8b6
--- /dev/null
+++
b/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpInfoTest.java
@@ -0,0 +1,106 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.enterprise.server.plugins.alertSnmp;
+
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.DEFAULT_PORT;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_HOST;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_PORT;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_TRAP_OID;
+import static
org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_VARIABLE_BINDING_PREFIX;
+import static org.testng.Assert.*;
+
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.configuration.Configuration;
+
+/**
+ * @author Thomas Segismont
+ */
+public class SnmpInfoTest {
+
+ @Test
+ public void shouldDetectMissingParams() throws Exception {
+ Configuration configuration = new Configuration();
+
+ SnmpInfo snmpInfo = SnmpInfo.load(configuration);
+ assertNotNull(snmpInfo.error, "SnmpInfo#load should have detected an
error");
+ assertEquals(snmpInfo.error, "Missing: OID, host");
+
+ configuration.setSimpleValue(PARAM_HOST, "pipo");
+ snmpInfo = SnmpInfo.load(configuration);
+ assertNotNull(snmpInfo.error, "SnmpInfo#load should have detected an
error");
+ assertEquals(snmpInfo.error, "Missing: OID");
+
+ configuration.remove(PARAM_HOST);
+ configuration.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX, "molo");
+ snmpInfo = SnmpInfo.load(configuration);
+ assertNotNull(snmpInfo.error, "SnmpInfo#load should have detected an
error");
+ assertEquals(snmpInfo.error, "Missing: host");
+ }
+
+ @Test
+ public void shouldAssumeDefaultValues() throws Exception {
+ Configuration configuration = new Configuration();
+ configuration.setSimpleValue(PARAM_HOST, "pipo");
+ configuration.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX, "molo");
+
+ SnmpInfo snmpInfo = SnmpInfo.load(configuration);
+ assertNull(snmpInfo.error, "SnmpInfo#load should not have detected an
error");
+ assertEquals(snmpInfo.host, "pipo");
+ assertEquals(snmpInfo.oid, "molo");
+ assertEquals(snmpInfo.port, SnmpInfo.DEFAULT_PORT);
+ }
+
+ @Test
+ public void shouldExposeAllParams() throws Exception {
+ Configuration configuration = new Configuration();
+ configuration.setSimpleValue(PARAM_HOST, "pipo");
+ configuration.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX, "molo");
+ configuration.setSimpleValue(PARAM_PORT, "123");
+ configuration.setSimpleValue(PARAM_TRAP_OID, "logo");
+
+ SnmpInfo snmpInfo = SnmpInfo.load(configuration);
+ assertNull(snmpInfo.error, "SnmpInfo#load should not have detected an
error");
+ assertEquals(snmpInfo.host, "pipo");
+ assertEquals(snmpInfo.port, "123");
+ assertEquals(snmpInfo.oid, "molo");
+ assertEquals(snmpInfo.trapOid, "logo");
+ }
+
+ @Test
+ public void testStringRepresentation() {
+ // We are not testing something really tricky here...
+ // But the String representation of an SnmpInfo instance is used in the UI as a
description in the alert
+ // definition notification list. So this test is here to remind us in the future
that the format of the
+ // String representation is constrained (even not strongly).
+ Configuration configuration = new Configuration();
+ assertEquals(SnmpInfo.load(configuration).toString(), "UnknownHost:" +
DEFAULT_PORT
+ + " (UnknownOID) (DefaultTrapOID)");
+ configuration.setSimpleValue(PARAM_HOST, "pipo");
+ assertEquals(SnmpInfo.load(configuration).toString(), "pipo:" +
DEFAULT_PORT + " (UnknownOID) (DefaultTrapOID)");
+ configuration.setSimpleValue(PARAM_PORT, "35162");
+ assertEquals(SnmpInfo.load(configuration).toString(), "pipo:35162
(UnknownOID) (DefaultTrapOID)");
+ configuration.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX, "molo");
+ assertEquals(SnmpInfo.load(configuration).toString(), "pipo:35162 (molo)
(DefaultTrapOID)");
+ configuration.setSimpleValue(PARAM_TRAP_OID, "logo");
+ assertEquals(SnmpInfo.load(configuration).toString(), "pipo:35162 (molo)
(logo)");
+ }
+
+}
diff --git
a/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSenderTest.java
b/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSenderTest.java
new file mode 100644
index 0000000..38b36d7
--- /dev/null
+++
b/modules/enterprise/server/plugins/alert-snmp/src/test/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSenderTest.java
@@ -0,0 +1,378 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.enterprise.server.plugins.alertSnmp;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+import static org.rhq.core.domain.alert.AlertPriority.HIGH;
+import static org.rhq.core.domain.alert.notification.ResultState.FAILURE;
+import static org.rhq.core.domain.alert.notification.ResultState.SUCCESS;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_HOST;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_PORT;
+import static org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_TRAP_OID;
+import static
org.rhq.enterprise.server.plugins.alertSnmp.SnmpInfo.PARAM_VARIABLE_BINDING_PREFIX;
+import static org.testng.Assert.*;
+
+import java.util.Arrays;
+import java.util.ListIterator;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.snmp4j.CommandResponder;
+import org.snmp4j.CommandResponderEvent;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.SMIConstants;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.alert.Alert;
+import org.rhq.core.domain.alert.AlertDefinition;
+import org.rhq.core.domain.alert.AlertPriority;
+import org.rhq.core.domain.alert.notification.SenderResult;
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.util.StringUtil;
+import org.rhq.enterprise.server.alert.AlertManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+
+/**
+ * @author Thomas Segismont
+ */
+public class SnmpSenderTest {
+
+ private static final Log LOG = LogFactory.getLog(SnmpSenderTest.class);
+
+ private static final String TEST_TRAP_OID_PLUGIN_CONFIG =
"1.3.6.1.4.1.18017";
+
+ private static final String TEST_TRAP_OID_ALERT_PARAM =
"1.3.6.1.4.1.18018";
+
+ private static final String TEST_VARIABLE_BINDING_PREFIX =
"1.3.6.1.4.1.18019";
+
+ private static final String TEST_HOST = "127.0.0.1";
+
+ private static final int TEST_PORT = 35162;
+
+ private static final String TEST_PORT_VARIABLE = "alert.snmp.test.port";
+
+ private ConcurrentLinkedQueue<PDU> receivedTraps;
+
+ private Snmp snmp;
+
+ @Mock
+ private ResourceManagerLocal resourceManager;
+
+ @Mock
+ private AlertManagerLocal alertManager;
+
+ private TestSnmpSender snmpSender;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ receivedTraps = new ConcurrentLinkedQueue<PDU>();
+
+ snmp = new Snmp(new DefaultUdpTransportMapping());
+ Address targetAddress = new UdpAddress(getTestPort());
+ boolean installedTrapListener = snmp.addNotificationListener(targetAddress, new
CommandResponder() {
+ @Override
+ public void processPdu(CommandResponderEvent event) {
+ receivedTraps.offer(event.getPDU());
+ }
+ });
+ if (!installedTrapListener) {
+ throw new RuntimeException("Could not install trap listener");
+ }
+
+ MockitoAnnotations.initMocks(this);
+
+ Configuration pluginConfiguration = new Configuration();
+ pluginConfiguration.setSimpleValue("snmpVersion", "2c");
+ pluginConfiguration.setSimpleValue("trapOid",
TEST_TRAP_OID_PLUGIN_CONFIG);
+ pluginConfiguration.setSimpleValue("community", "public");
+ snmpSender = new TestSnmpSender(resourceManager, alertManager,
pluginConfiguration);
+ }
+
+ private static int getTestPort() {
+ String testPortVariable = System.getProperty(TEST_PORT_VARIABLE);
+ if (StringUtil.isNotBlank(testPortVariable)) {
+ try {
+ int port = Integer.parseInt(testPortVariable);
+ LOG.info("Using port " + testPortVariable + " for SNMP
traps");
+ return port;
+ } catch (NumberFormatException e) {
+ LOG.warn("Invalid port variable: " + testPortVariable);
+ }
+ }
+ LOG.info("Using default port " + TEST_PORT + " for SNMP
traps");
+ return TEST_PORT;
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ if (snmp != null) {
+ snmp.close();
+ }
+ }
+
+ @Test
+ public void shouldReturnSimpleFailureForInvalidNotificationParameters() {
+ Configuration alertParameters = new Configuration();
+ snmpSender.setAlertParameters(alertParameters);
+
+ SenderResult result = snmpSender.send(createAlertForResourceWithId(13004,
"", "", HIGH));
+
+ assertNotNull(result);
+ assertEquals(result.getState(), FAILURE);
+ assertEquals(result.getFailureMessages().size(), 1);
+ String expectedError = SnmpInfo.load(alertParameters).error;
+ assertNotNull(expectedError);
+ assertEquals(result.getFailureMessages().get(0), expectedError);
+ }
+
+ @Test
+ public void shouldReturnSimpleFailureWhenErrorOccurs() {
+ Configuration alertParameters = new Configuration();
+ alertParameters.setSimpleValue(PARAM_HOST, TEST_HOST);
+ alertParameters.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX,
TEST_VARIABLE_BINDING_PREFIX);
+ alertParameters.setSimpleValue(PARAM_PORT, String.valueOf(getTestPort()));
+ snmpSender.setAlertParameters(alertParameters);
+
+ int resourceId = 13004;
+ Alert alert = createAlertForResourceWithId(resourceId, "",
"", HIGH);
+ String exceptionMessage = "Test Error";
+ when(resourceManager.getResourceLineage(eq(resourceId))).thenThrow(new
RuntimeException(exceptionMessage));
+
+ SenderResult result = snmpSender.send(alert);
+
+ assertNotNull(result);
+ assertEquals(result.getState(), FAILURE);
+ assertEquals(result.getFailureMessages().size(), 1);
+ String actualErrorMessage = result.getFailureMessages().get(0);
+ assertTrue(actualErrorMessage.endsWith(exceptionMessage), "Unexpected error
message: " + actualErrorMessage);
+ }
+
+ @Test(timeOut = 1000 * 60)
+ public void testSendWithDefaultSnmpTrapOid() throws Exception {
+ Configuration alertParameters = new Configuration();
+ alertParameters.setSimpleValue(PARAM_HOST, TEST_HOST);
+ alertParameters.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX,
TEST_VARIABLE_BINDING_PREFIX);
+ alertParameters.setSimpleValue(PARAM_PORT, String.valueOf(getTestPort()));
+ snmpSender.setAlertParameters(alertParameters);
+
+ testSendWithSnmpTrapOid(new OID(TEST_TRAP_OID_PLUGIN_CONFIG));
+ }
+
+ @Test(timeOut = 1000 * 60)
+ public void testSendWithSpecificSnmpTrapOid() throws Exception {
+ Configuration alertParameters = new Configuration();
+ alertParameters.setSimpleValue(PARAM_HOST, TEST_HOST);
+ alertParameters.setSimpleValue(PARAM_VARIABLE_BINDING_PREFIX,
TEST_VARIABLE_BINDING_PREFIX);
+ alertParameters.setSimpleValue(PARAM_PORT, String.valueOf(getTestPort()));
+ alertParameters.setSimpleValue(PARAM_TRAP_OID, TEST_TRAP_OID_ALERT_PARAM);
+ snmpSender.setAlertParameters(alertParameters);
+
+ testSendWithSnmpTrapOid(new OID(TEST_TRAP_OID_ALERT_PARAM));
+ }
+
+ private void testSendWithSnmpTrapOid(OID snmpTrapOid) throws InterruptedException {
+ int resourceId = 13004;
+ String resourceName = "Resource " + resourceId;
+ String alertDefinitionName = "Alert Definition " + resourceId;
+ AlertPriority alertPriority = HIGH;
+ Alert alert = createAlertForResourceWithId(resourceId, resourceName,
alertDefinitionName, alertPriority);
+ Resource platformResouce = new Resource();
+ String platformName = "Platform Resource " + resourceId;
+ platformResouce.setName(platformName);
+
+ when(resourceManager.getResourceLineage(eq(resourceId))).thenReturn(
+ Arrays.asList(platformResouce, alert.getAlertDefinition().getResource()));
+ String alertConditions = "Alert Conditions " + resourceId;
+ when(alertManager.prettyPrintAlertConditions(eq(alert),
eq(false))).thenReturn(alertConditions);
+ String alertUrl = "https://www.rhq.com/alert/" + resourceId;
+ when(alertManager.prettyPrintAlertURL(eq(alert))).thenReturn(alertUrl);
+
+ assertNull(receivedTraps.peek(), "Something sent a trap before on our test
port");
+
+ SenderResult result = snmpSender.send(alert);
+
+ assertEquals(result.getState(), SUCCESS,
result.getFailureMessages().toString());
+ while (receivedTraps.peek() == null) {
+ Thread.sleep(1000);
+ }
+ PDU pdu = receivedTraps.poll();
+ assertNull(receivedTraps.peek(), "Only one trap should have been
received");
+
+ assertExpectedPdu(pdu, new PduExpectedValues(snmpTrapOid, resourceName,
alertDefinitionName, alertPriority,
+ platformName, alertConditions, alertUrl));
+ }
+
+ private void assertExpectedPdu(PDU pdu, PduExpectedValues expectedValues) {
+ Vector variableBindings = pdu.getVariableBindings();
+
+ assertTrue(variableBindings.size() == 9, "Variable bindings should contain 9
variable bindings and not "
+ + variableBindings.size() + ": " + variableBindings);
+
+ @SuppressWarnings("unchecked")
+ ListIterator<VariableBinding> variableBindingsIterator =
variableBindings.listIterator();
+
+ VariableBinding variableBinding = variableBindingsIterator.next();
+ assertEquals(variableBinding.getOid(), SnmpConstants.sysUpTime);
+
+ variableBinding = variableBindingsIterator.next();
+ assertEquals(variableBinding.getOid(), SnmpConstants.snmpTrapOID);
+ assertEquals(variableBinding.getVariable(), expectedValues.getSnmpTrapOid());
+
+ OID oidPrefix = new OID(TEST_VARIABLE_BINDING_PREFIX);
+ while (variableBindingsIterator.hasNext()) {
+ variableBinding = variableBindingsIterator.next();
+
+ assertVariableBindingIsPrefixed(variableBinding, oidPrefix);
+ assertVariableBindingHasStringValue(variableBinding);
+
+ switch (variableBindingsIterator.previousIndex()) {
+ case 2:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getAlertDefinitionName()));
+ break;
+ case 3:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getResourceName()));
+ break;
+ case 4:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getPlatformName()));
+ break;
+ case 5:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getAlertConditions()));
+ break;
+ case 6:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getAlertPriority()
+ .toString().toLowerCase()));
+ break;
+ case 7:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getAlertUrl()));
+ break;
+ case 8:
+ assertEquals(variableBinding.getVariable(), new
OctetString(expectedValues.getPlatformName() + "::"
+ + expectedValues.getResourceName() + "::"));
+ break;
+ default:
+ throw new RuntimeException("Unexpected index: " +
variableBindingsIterator.previousIndex());
+ }
+ }
+ }
+
+ private void assertVariableBindingHasStringValue(VariableBinding variableBinding) {
+ assertEquals(variableBinding.getVariable().getSyntax(),
SMIConstants.SYNTAX_OCTET_STRING,
+ "Variable binding value [" + variableBinding.getVariable() +
"] has wrong type");
+ }
+
+ private void assertVariableBindingIsPrefixed(VariableBinding variableBinding, OID
oidPrefix) {
+ assertTrue(variableBinding.getOid().startsWith(oidPrefix), "Variable binding
OID [" + variableBinding.getOid()
+ + "] has wrong prefix");
+ }
+
+ private Alert createAlertForResourceWithId(int resourceId, String resourceName,
String alertDefinitionName,
+ AlertPriority alertPriority) {
+ Resource resource = new Resource();
+ resource.setId(resourceId);
+ resource.setName(resourceName);
+ AlertDefinition alertDefinition = new AlertDefinition();
+ alertDefinition.setName(alertDefinitionName);
+ alertDefinition.setResource(resource);
+ alertDefinition.setPriority(alertPriority);
+ Alert alert = new Alert();
+ alert.setAlertDefinition(alertDefinition);
+ return alert;
+ }
+
+ private static final class TestSnmpSender extends SnmpSender {
+
+ TestSnmpSender(ResourceManagerLocal resourceManager, AlertManagerLocal
alertManager,
+ Configuration pluginConfiguration) {
+ super(resourceManager, alertManager);
+ this.preferences = pluginConfiguration;
+ }
+
+ void setAlertParameters(Configuration alertParameters) {
+ this.alertParameters = alertParameters;
+ }
+
+ }
+
+ private static final class PduExpectedValues {
+
+ private final OID snmpTrapOid;
+ private final String resourceName;
+ private final String alertDefinitionName;
+ private final AlertPriority alertPriority;
+ private final String platformName;
+ private final String alertConditions;
+ private final String alertUrl;
+
+ private PduExpectedValues(OID snmpTrapOid, String resourceName, String
alertDefinitionName,
+ AlertPriority alertPriority, String platformName, String alertConditions,
String alertUrl) {
+ this.snmpTrapOid = snmpTrapOid;
+ this.resourceName = resourceName;
+ this.alertDefinitionName = alertDefinitionName;
+ this.alertPriority = alertPriority;
+ this.platformName = platformName;
+ this.alertConditions = alertConditions;
+ this.alertUrl = alertUrl;
+ }
+
+ public OID getSnmpTrapOid() {
+ return snmpTrapOid;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public String getAlertDefinitionName() {
+ return alertDefinitionName;
+ }
+
+ public AlertPriority getAlertPriority() {
+ return alertPriority;
+ }
+
+ public String getPlatformName() {
+ return platformName;
+ }
+
+ public String getAlertConditions() {
+ return alertConditions;
+ }
+
+ public String getAlertUrl() {
+ return alertUrl;
+ }
+ }
+}
diff --git a/modules/enterprise/server/plugins/alert-snmp/src/test/resources/log4j.xml
b/modules/enterprise/server/plugins/alert-snmp/src/test/resources/log4j.xml
new file mode 100644
index 0000000..73b2ae5
--- /dev/null
+++ b/modules/enterprise/server/plugins/alert-snmp/src/test/resources/log4j.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- | For more configuration information and examples, see the Jakarta Log4j |
website:
http://jakarta.apache.org/log4j -->
+
+<log4j:configuration
xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <appender name="CONSOLE"
class="org.apache.log4j.ConsoleAppender">
+ <param name="Target" value="System.out"/>
+ <param name="Threshold" value="ERROR"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%-5p %d{dd-MM
HH:mm:ss,SSS} (%F:%M:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="FILE"
class="org.apache.log4j.RollingFileAppender">
+ <param name="File" value="target/test.log"/>
+ <param name="Append" value="false"/>
+ <param name="Threshold" value="DEBUG"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%-5p %d{dd-MM
HH:mm:ss,SSS} (%F:%M:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <root>
+ <level value="DEBUG"/>
+ <appender-ref ref="CONSOLE"/>
+ <appender-ref ref="FILE"/>
+ </root>
+
+</log4j:configuration>