/* * 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; private Configuration pluginConfiguration; @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); 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,pluginConfiguration).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; } } }