/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2008-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) 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, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.trapd;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.opennms.core.utils.InetAddressUtils.addr;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.opennms.core.test.MockLogAppender;
import org.opennms.core.test.OpenNMSJUnit4ClassRunner;
import org.opennms.core.utils.Base64;
import org.opennms.core.utils.BeanUtils;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.dao.db.JUnitConfigurationEnvironment;
import org.opennms.netmgt.mock.EventAnticipator;
import org.opennms.netmgt.mock.MockEventIpcManager;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.snmp.SnmpInstId;
import org.opennms.netmgt.snmp.SnmpObjId;
import org.opennms.netmgt.snmp.SnmpTrapBuilder;
import org.opennms.netmgt.snmp.SnmpUtils;
import org.opennms.netmgt.snmp.SnmpV1TrapBuilder;
import org.opennms.netmgt.snmp.SnmpValue;
import org.opennms.netmgt.snmp.SnmpValueFactory;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
@RunWith(OpenNMSJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:META-INF/opennms/mockEventIpcManager.xml",
"classpath:META-INF/opennms/applicationContext-daemon.xml",
"classpath:META-INF/opennms/applicationContext-trapDaemon.xml",
"classpath:org/opennms/netmgt/trapd/applicationContext-trapDaemonTest.xml"}
)
@JUnitConfigurationEnvironment
public class TrapHandlerTestCase implements InitializingBean {
@Autowired
private Trapd m_trapd = null;
@Autowired
private MockEventIpcManager m_eventMgr;
@Autowired
private MockTrapdIpMgr m_trapdIpMgr;
@Autowired
private TrapQueueProcessorFactory m_processorFactory;
private EventAnticipator m_anticipator;
private InetAddress m_localhost = null;
@Resource(name="snmpTrapPort")
private Integer m_snmpTrapPort;
private boolean m_doStop = false;
private static final String m_ip = "127.0.0.1";
private static final long m_nodeId = 1;
@BeforeClass
public static void setUpLogging() {
MockLogAppender.setupLogging();
}
@Override
public void afterPropertiesSet() throws Exception {
BeanUtils.assertAutowiring(this);
}
@Before
public void setUp() throws Exception {
m_anticipator = new EventAnticipator();
m_eventMgr.setEventAnticipator(m_anticipator);
m_localhost = InetAddressUtils.addr(m_ip);
m_trapdIpMgr.clearKnownIpsMap();
m_trapdIpMgr.setNodeId(m_ip, m_nodeId);
m_trapd.start();
m_doStop = true;
}
public Collection<Event> finishUp() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
m_eventMgr.finishProcessingEvents();
m_anticipator.verifyAnticipated(1000, 0, 0, 0, 0);
return m_anticipator.getAnticipatedEventsRecieved();
}
@After
public void tearDown() throws Exception {
if (m_trapd != null && m_doStop) {
m_trapd.stop();
m_trapd = null;
}
}
@Test
@DirtiesContext
public void testV1TrapNoNewSuspect() throws Exception {
m_trapdIpMgr.clearKnownIpsMap();
anticipateAndSend(false, false, "uei.opennms.org/default/trap", "v1",
null, 6, 1);
}
@Test
@DirtiesContext
public void testV2TrapNoNewSuspect() throws Exception {
m_trapdIpMgr.clearKnownIpsMap();
anticipateAndSend(false, false, "uei.opennms.org/default/trap",
"v2c", null, 6, 1);
}
@Test
@DirtiesContext
public void testV1TrapNewSuspect() throws Exception {
m_trapdIpMgr.clearKnownIpsMap();
anticipateAndSend(true, false, "uei.opennms.org/default/trap",
"v1", null, 6, 1);
}
@Test
@DirtiesContext
public void testV2TrapNewSuspect() throws Exception {
m_trapdIpMgr.clearKnownIpsMap();
anticipateAndSend(true, false, "uei.opennms.org/default/trap",
"v2c", null, 6, 1);
}
@Test
@DirtiesContext
public void testV1EnterpriseIdAndGenericMatch() throws Exception {
anticipateAndSend(false, true,
"uei.opennms.org/IETF/BGP/traps/bgpEstablished",
"v1", ".1.3.6.1.2.1.15.7", 6, 1);
}
@Test
@DirtiesContext
public void testV2EnterpriseIdAndGenericAndSpecificMatch()
throws Exception {
anticipateAndSend(false, true,
"uei.opennms.org/IETF/BGP/traps/bgpEstablished",
"v2c", ".1.3.6.1.2.1.15.7", 6, 1);
}
@Test
@DirtiesContext
public void testV1EnterpriseIdAndGenericAndSpecificAndMatchWithVarbinds()
throws Exception {
SnmpValueFactory valueFactory = SnmpUtils.getValueFactory();
LinkedHashMap<String, SnmpValue> varbinds = new LinkedHashMap <String, SnmpValue>();
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.4.2404", valueFactory.getInt32(3));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.5.2404", valueFactory.getInt32(2));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.6.2404", valueFactory.getInt32(5));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.3.0.2404", valueFactory.getOctetString("http://a.b.c.d/cgi/fDetail?index=2404".getBytes()));
anticipateAndSend(false, true,
"uei.opennms.org/vendor/HP/traps/hpicfFaultFinderTrap",
"v1", ".1.3.6.1.4.1.11.2.14.12.1", 6, 5, varbinds);
}
@Test
@DirtiesContext
public void testV2EnterpriseIdAndGenericAndSpecificAndMatchWithVarbinds()
throws Exception {
SnmpValueFactory valueFactory = SnmpUtils.getValueFactory();
LinkedHashMap<String, SnmpValue> varbinds = new LinkedHashMap <String, SnmpValue>();
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.4.2404", valueFactory.getInt32(3));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.5.2404", valueFactory.getInt32(2));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.2.1.6.2404", valueFactory.getInt32(5));
varbinds.put(".1.3.6.1.4.1.11.2.14.11.1.7.3.0.2404", valueFactory.getOctetString("http://a.b.c.d/cgi/fDetail?index=2404".getBytes()));
anticipateAndSend(false, true,
"uei.opennms.org/vendor/HP/traps/hpicfFaultFinderTrap",
"v2c", ".1.3.6.1.4.1.11.2.14.12.1", 6, 5, varbinds);
}
// These exist to provide testing for the new Textual Convention feature
// See EventConfDataTest for the other part of this testing
@Test
@DirtiesContext
public void testV1EnterpriseIdAndGenericAndSpecificAndMatchWithVarbindsAndTC()
throws Exception {
SnmpValueFactory valueFactory = SnmpUtils.getValueFactory();
LinkedHashMap<String, SnmpValue> varbinds = new LinkedHashMap <String, SnmpValue>();
varbinds.put(".1.3.6.1.4.1.14179.2.6.2.20.0", valueFactory.getOctetString(new byte[]{(byte)0x00,(byte)0x14,(byte)0xf1,(byte)0xad,(byte)0xa7,(byte)0x50}));
Collection<Event> events = anticipateAndSend(false, true,
"uei.opennms.org/vendor/cisco/bsnAPNoiseProfileUpdatedToPass",
"v1", ".1.3.6.1.4.1.14179.2.6.3", 6, 38, varbinds);
boolean foundMacAddress = false;
// Assert that the MAC address varbind has been formatted into a colon-separated octet string
for (Event event : events) {
for (Parm parm : event.getParmCollection()) {
if (".1.3.6.1.4.1.14179.2.6.2.20.0".equals(parm.getParmName())) {
assertEquals("MAC address does not match", "00:14:F1:AD:A7:50", parm.getValue().getContent());
foundMacAddress = true;
}
}
}
assertTrue("Did not find expected MAC address parm", foundMacAddress);
}
// FIXME: these exist to provide testing for the new Textual Convention feature
@Test
@DirtiesContext
public void testV2EnterpriseIdAndGenericAndSpecificAndMatchWithVarbindsAndTC()
throws Exception {
SnmpValueFactory valueFactory = SnmpUtils.getValueFactory();
byte[] macAddr = new byte[]{(byte)0x00,(byte)0x14,(byte)0xf1,(byte)0xad,(byte)0xa7,(byte)0x50};
String encoded = new String(Base64.encodeBase64(macAddr));
byte[] decodeBytes = Base64.decodeBase64(encoded.toCharArray());
assertByteArrayEquals(macAddr, decodeBytes);
LinkedHashMap<String, SnmpValue> varbinds = new LinkedHashMap <String, SnmpValue>();
varbinds.put(".1.3.6.1.4.1.14179.2.6.2.20.0", valueFactory.getOctetString(macAddr));
Collection<Event> events = anticipateAndSend(false, true,
"uei.opennms.org/vendor/cisco/bsnAPNoiseProfileUpdatedToPass",
"v2c", ".1.3.6.1.4.1.14179.2.6.3", 6, 38, varbinds);
boolean foundMacAddress = false;
// Assert that the MAC address varbind has been formatted into a colon-separated octet string
for (Event event : events) {
for (Parm parm : event.getParmCollection()) {
if (".1.3.6.1.4.1.14179.2.6.2.20.0".equals(parm.getParmName())) {
assertEquals("MAC address does not match", "00:14:F1:AD:A7:50", parm.getValue().getContent());
foundMacAddress = true;
}
}
}
assertTrue("Did not find expected MAC address parm", foundMacAddress);
}
private void assertByteArrayEquals(byte[] macAddr, byte[] bytes) {
assertEquals("expect length: "+macAddr.length, macAddr.length, bytes.length);
for (int i = 0; i < macAddr.length; i++) {
assertEquals("Expected byte "+i+" to match", macAddr[i], bytes[i]);
}
}
@Test
@DirtiesContext
public void testV2EnterpriseIdAndGenericAndSpecificMatchWithZero()
throws Exception {
anticipateAndSend(false, true,
"uei.opennms.org/IETF/BGP/traps/bgpEstablished",
"v2c", ".1.3.6.1.2.1.15.7.0", 6, 1);
}
@Test
@DirtiesContext
public void testV2EnterpriseIdAndGenericAndSpecificMissWithExtraZeros()
throws Exception {
anticipateAndSend(false, true, "uei.opennms.org/default/trap", "v2c",
".1.3.6.1.2.1.15.7.0.0", 6, 1);
}
@Test
@DirtiesContext
public void testV1EnterpriseIdAndGenericAndSpecificMissWithWrongGeneric()
throws Exception {
anticipateAndSend(false, true, "uei.opennms.org/default/trap", "v1",
".1.3.6.1.2.1.15.7", 5, 1);
}
@Test
@DirtiesContext
public void testV1EnterpriseIdAndGenericAndSpecificMissWithWrongSpecific()
throws Exception {
anticipateAndSend(false, true, "uei.opennms.org/default/trap", "v1",
".1.3.6.1.2.1.15.7", 6, 50);
}
@Test
@DirtiesContext
public void testV1GenericMatch() throws Exception {
anticipateAndSend(false, true,
"uei.opennms.org/generic/traps/SNMP_Cold_Start",
"v1", null, 0, 0);
}
@Test
@DirtiesContext
public void testV2GenericMatch() throws Exception {
anticipateAndSend(false, true,
"uei.opennms.org/generic/traps/SNMP_Cold_Start",
"v2c", ".1.3.6.1.6.3.1.1.5.1", 0, 0);
}
@Test
@DirtiesContext
public void testV1TrapDroppedEvent() throws Exception {
anticipateAndSend(false, true, null, "v1", ".1.3.6.1.2.1.15.7", 6, 2);
}
@Test
@DirtiesContext
public void testV2TrapDroppedEvent() throws Exception {
anticipateAndSend(false, true, null, "v2c", ".1.3.6.1.2.1.15.7", 6, 2);
}
@Test
@DirtiesContext
public void testV1TrapDefaultEvent() throws Exception {
anticipateAndSend(false, true, "uei.opennms.org/default/trap",
"v1", null, 6, 1);
}
@Test
@DirtiesContext
public void testV2TrapDefaultEvent() throws Exception {
anticipateAndSend(false, true, "uei.opennms.org/default/trap",
"v2c", null, 6, 1);
}
@Test
@DirtiesContext
public void testNodeGainedModifiesIpMgr() throws Exception {
long nodeId = 2;
m_processorFactory.setNewSuspect(true);
anticipateEvent("uei.opennms.org/default/trap", m_ip, nodeId);
Event event =
anticipateEvent(EventConstants.NODE_GAINED_INTERFACE_EVENT_UEI,
m_ip, nodeId);
m_eventMgr.sendNow(event);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// do nothing
}
sendTrap("v1", null, 6, 1);
finishUp();
}
@Test
@DirtiesContext
public void testInterfaceReparentedModifiesIpMgr() throws Exception {
long nodeId = 2;
m_processorFactory.setNewSuspect(true);
anticipateEvent("uei.opennms.org/default/trap", m_ip, nodeId);
Event event =
anticipateEvent(EventConstants.INTERFACE_REPARENTED_EVENT_UEI,
m_ip, nodeId);
m_eventMgr.sendNow(event);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// do nothing
}
sendTrap("v1", null, 6, 1);
finishUp();
}
@Test
@DirtiesContext
public void testInterfaceDeletedModifiesIpMgr() throws Exception {
long nodeId = 0;
m_processorFactory.setNewSuspect(true);
anticipateEvent("uei.opennms.org/default/trap", m_ip, nodeId);
Event event =
anticipateEvent(EventConstants.INTERFACE_DELETED_EVENT_UEI,
m_ip, nodeId);
m_eventMgr.sendNow(event);
anticipateEvent(EventConstants.NEW_SUSPECT_INTERFACE_EVENT_UEI, m_ip, nodeId);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// do nothing
}
sendTrap("v1", null, 6, 1);
finishUp();
}
public Event anticipateEvent(String uei) {
return anticipateEvent(uei, m_ip, m_nodeId);
}
public Event anticipateEvent(String uei, String ip, long nodeId) {
EventBuilder bldr = new EventBuilder(uei, "TrapHandlerTestCase");
bldr.setNodeid(nodeId);
bldr.setInterface(addr(ip));
m_anticipator.anticipateEvent(bldr.getEvent());
return bldr.getEvent();
}
public Collection<Event> anticipateAndSend(boolean newSuspectOnTrap, boolean nodeKnown,
String event,
String version, String enterprise,
int generic, int specific) throws Exception {
return anticipateAndSend(newSuspectOnTrap, nodeKnown, event, version, enterprise, generic, specific, null);
}
/**
* @param newSuspectOnTrap Will a new suspect event be triggered by the trap?
* @param nodeKnown Is the node in the database?
* @param event Event that is anticipated to result when the trap is processed
* @param snmpTrapVersion SNMP version of trap, valid values: <code>v1</code>, <code>v2c</code>
* @param enterprise Enterprise ID of the trap
* @param varbinds Varbinds attached to the trap
*/
public Collection<Event> anticipateAndSend(boolean newSuspectOnTrap, boolean nodeKnown,
String event,
String snmpTrapVersion, String enterprise,
int generic, int specific, LinkedHashMap<String, SnmpValue> varbinds) throws Exception {
m_processorFactory.setNewSuspect(newSuspectOnTrap);
if (newSuspectOnTrap) {
// Note: the nodeId will be zero because the node is not known
anticipateEvent(EventConstants.NEW_SUSPECT_INTERFACE_EVENT_UEI, m_ip, 0);
}
if (event != null) {
if (nodeKnown) {
anticipateEvent(event);
} else {
/*
* If the node is unknown, the nodeId on the trap event
* will be zero.
*/
anticipateEvent(event, m_ip, 0);
}
}
if (varbinds == null) {
sendTrap(snmpTrapVersion, enterprise, generic, specific);
} else {
sendTrap(snmpTrapVersion, enterprise, generic, specific, varbinds);
}
return finishUp();
}
public void sendTrap(String version, String enterprise, int generic,
int specific) throws Exception {
if (enterprise == null) {
enterprise = ".0.0";
}
if (version.equals("v1")) {
sendV1Trap(enterprise, generic, specific);
} else if (version.equals("v2c")) {
sendV2Trap(enterprise, specific);
} else {
throw new Exception("unsupported SNMP version for test: "
+ version);
}
}
private void sendTrap(String version, String enterprise, int generic,
int specific, LinkedHashMap<String, SnmpValue> varbinds) throws Exception {
if (enterprise == null) {
enterprise = ".0.0";
}
if (version.equals("v1")) {
sendV1Trap(enterprise, generic, specific, varbinds);
} else if (version.equals("v2c")) {
sendV2Trap(enterprise, specific, varbinds);
} else {
throw new Exception("unsupported SNMP version for test: "
+ version);
}
}
public void sendV1Trap(String enterprise, int generic, int specific)
throws Exception {
SnmpV1TrapBuilder pdu = SnmpUtils.getV1TrapBuilder();
pdu.setEnterprise(SnmpObjId.get(enterprise));
pdu.setGeneric(generic);
pdu.setSpecific(specific);
pdu.setTimeStamp(0);
pdu.setAgentAddress(m_localhost);
pdu.send(getHostAddress(), m_snmpTrapPort, "public");
}
private String getHostAddress() {
return InetAddressUtils.str(m_localhost);
}
public void sendV1Trap(String enterprise, int generic, int specific, LinkedHashMap<String, SnmpValue> varbinds)
throws Exception {
SnmpV1TrapBuilder pdu = SnmpUtils.getV1TrapBuilder();
pdu.setEnterprise(SnmpObjId.get(enterprise));
pdu.setGeneric(generic);
pdu.setSpecific(specific);
pdu.setTimeStamp(0);
pdu.setAgentAddress(m_localhost);
Iterator<Map.Entry<String,SnmpValue>> it = varbinds.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,SnmpValue> pairs = it.next();
pdu.addVarBind(SnmpObjId.get(pairs.getKey()), pairs.getValue());
}
pdu.send(getHostAddress(), m_snmpTrapPort, "public");
}
public void sendV2Trap(String enterprise, int specific) throws Exception {
SnmpObjId enterpriseId = SnmpObjId.get(enterprise);
boolean isGeneric = false;
SnmpObjId trapOID;
if (SnmpObjId.get(".1.3.6.1.6.3.1.1.5").isPrefixOf(enterpriseId)) {
isGeneric = true;
trapOID = enterpriseId;
} else {
trapOID = SnmpObjId.get(enterpriseId, new SnmpInstId(specific));
// XXX or should it be this
// trap OID = enterprise + ".0." + specific;
}
SnmpTrapBuilder pdu = SnmpUtils.getV2TrapBuilder();
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.2.1.1.3.0"),
SnmpUtils.getValueFactory().getTimeTicks(0));
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.6.3.1.1.4.1.0"),
SnmpUtils.getValueFactory().getObjectId(trapOID));
if (isGeneric) {
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.6.3.1.1.4.3.0"),
SnmpUtils.getValueFactory().getObjectId(enterpriseId));
}
pdu.send(getHostAddress(), m_snmpTrapPort, "public");
}
public void sendV2Trap(String enterprise, int specific, LinkedHashMap<String, SnmpValue> varbinds) throws Exception {
SnmpObjId enterpriseId = SnmpObjId.get(enterprise);
boolean isGeneric = false;
SnmpObjId trapOID;
if (SnmpObjId.get(".1.3.6.1.6.3.1.1.5").isPrefixOf(enterpriseId)) {
isGeneric = true;
trapOID = enterpriseId;
} else {
trapOID = SnmpObjId.get(enterpriseId, new SnmpInstId(specific));
// XXX or should it be this
// trap OID = enterprise + ".0." + specific;
}
SnmpTrapBuilder pdu = SnmpUtils.getV2TrapBuilder();
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.2.1.1.3.0"),
SnmpUtils.getValueFactory().getTimeTicks(0));
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.6.3.1.1.4.1.0"),
SnmpUtils.getValueFactory().getObjectId(trapOID));
if (isGeneric) {
pdu.addVarBind(SnmpObjId.get(".1.3.6.1.6.3.1.1.4.3.0"),
SnmpUtils.getValueFactory().getObjectId(enterpriseId));
}
for (Map.Entry<String, SnmpValue> entry : varbinds.entrySet()) {
pdu.addVarBind(SnmpObjId.get(entry.getKey()), entry.getValue());
}
pdu.send(getHostAddress(), m_snmpTrapPort, "public");
}
}