/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2010-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.vacuumd; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.opennms.core.fiber.Fiber; import org.opennms.core.fiber.PausableFiber; import org.opennms.core.test.OpenNMSJUnit4ClassRunner; import org.opennms.core.utils.BeanUtils; import org.opennms.netmgt.EventConstants; import org.opennms.netmgt.alarmd.Alarmd; import org.opennms.netmgt.config.DataSourceFactory; import org.opennms.netmgt.config.VacuumdConfigFactory; import org.opennms.netmgt.config.vacuumd.Automation; import org.opennms.netmgt.config.vacuumd.Trigger; import org.opennms.netmgt.dao.NodeDao; import org.opennms.netmgt.dao.db.JUnitConfigurationEnvironment; import org.opennms.netmgt.dao.db.JUnitTemporaryDatabase; import org.opennms.netmgt.dao.db.TemporaryDatabaseAware; import org.opennms.netmgt.mock.MockDatabase; import org.opennms.netmgt.mock.MockEventIpcManager; import org.opennms.netmgt.mock.MockNetwork; import org.opennms.netmgt.mock.MockNode; import org.opennms.netmgt.model.OnmsNode; import org.opennms.netmgt.model.OnmsSeverity; import org.opennms.netmgt.model.events.EventBuilder; import org.opennms.netmgt.xml.event.Event; import org.opennms.test.ConfigurationTestUtils; import org.opennms.test.mock.MockUtil; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; /** * Tests Vacuumd's execution of statements and automations * @author <a href=mailto:david@opennms.org>David Hustace</a> * @author <a href=mailto:brozow@opennms.org>Mathew Brozowski</a> * */ @RunWith(OpenNMSJUnit4ClassRunner.class) @ContextConfiguration(locations={ "classpath:/META-INF/opennms/applicationContext-soa.xml", "classpath:/META-INF/opennms/applicationContext-dao.xml", "classpath*:/META-INF/opennms/component-dao.xml", "classpath:/META-INF/opennms/applicationContext-daemon.xml", "classpath:/META-INF/opennms/mockEventIpcManager.xml", "classpath:/META-INF/opennms/applicationContext-alarmd.xml", "classpath:/META-INF/opennms/applicationContext-minimal-conf.xml" }) @JUnitConfigurationEnvironment @JUnitTemporaryDatabase(dirtiesContext=false,tempDbClass=MockDatabase.class) public class VacuumdTest implements TemporaryDatabaseAware<MockDatabase>, InitializingBean { private static final long TEAR_DOWN_WAIT_MILLIS = 1000; private Vacuumd m_vacuumd; @Autowired private Alarmd m_alarmd; @Autowired private NodeDao m_nodeDao; @Autowired private JdbcTemplate m_jdbcTemplate; @Autowired private MockEventIpcManager m_eventdIpcMgr; private MockNetwork m_network = new MockNetwork(); private MockDatabase m_database; public void setTemporaryDatabase(MockDatabase database) { m_database = database; } @Override public void afterPropertiesSet() throws Exception { BeanUtils.assertAutowiring(this); } @Before public void setUp() throws Exception { m_network.createStandardNetwork(); InputStream is = ConfigurationTestUtils.getInputStreamForResource(this, "/org/opennms/netmgt/vacuumd/vacuumd-configuration.xml"); try { VacuumdConfigFactory.setInstance(new VacuumdConfigFactory(is)); } finally { IOUtils.closeQuietly(is); } m_eventdIpcMgr.setEventWriter(m_database); m_vacuumd = Vacuumd.getSingleton(); m_vacuumd.setEventManager(m_eventdIpcMgr); m_vacuumd.init(); // Insert some empty nodes to avoid foreign-key violations on subsequent events/alarms OnmsNode node = new OnmsNode(); node.setId(1); node.setLabel("default-1"); m_nodeDao.save(node); node = new OnmsNode(); node.setId(2); node.setLabel("default-2"); m_nodeDao.save(node); MockUtil.println("------------ Finished setup for: "+ this.getClass().getName() +" --------------------------"); } @After public void tearDown() throws Exception { m_alarmd.destroy(); MockUtil.println("Sleeping for "+TEAR_DOWN_WAIT_MILLIS+" millis in tearDown..."); Thread.sleep(TEAR_DOWN_WAIT_MILLIS); } /** * Test for running statments */ @Test public final void testRunStatements() { m_vacuumd.executeStatements(); } /** * This is an attempt at testing scheduled automations. * @throws InterruptedException */ @Test public final void testConcurrency() throws InterruptedException { try { /* * Test status of threads */ assertEquals(Fiber.START_PENDING, m_vacuumd.getStatus()); assertEquals(Fiber.START_PENDING, m_vacuumd.getScheduler().getStatus()); /* * Testing the start */ m_vacuumd.start(); assertTrue(m_vacuumd.getStatus() >= 1); Thread.sleep(200); assertEquals(Fiber.RUNNING, m_vacuumd.getStatus()); assertEquals(Fiber.RUNNING, m_vacuumd.getScheduler().getStatus()); /* * Testing the pause */ m_vacuumd.pause(); Thread.sleep(200); assertEquals(PausableFiber.PAUSED, m_vacuumd.getStatus()); assertEquals(PausableFiber.PAUSED, m_vacuumd.getScheduler().getStatus()); m_vacuumd.resume(); Thread.sleep(200); assertEquals(PausableFiber.RUNNING, m_vacuumd.getStatus()); assertEquals(PausableFiber.RUNNING, m_vacuumd.getScheduler().getStatus()); // Get an alarm in the DB bringNodeDownCreatingEvent(1); // There should be one node down alarm assertEquals("count of nodeDown events", 1, m_jdbcTemplate.queryForInt("select count(*) from events where eventuei = '" + EventConstants.NODE_DOWN_EVENT_UEI + "'")); assertEquals("alarm count", 1, countAlarms()); assertEquals("counter in the alarm", 1, m_jdbcTemplate.queryForInt("select counter from alarms where eventuei = '" + EventConstants.NODE_DOWN_EVENT_UEI + "'")); // Fetch the initial severity of the alarm int currentSeverity = m_jdbcTemplate.queryForInt("select severity from alarms"); // Create another node down event bringNodeDownCreatingEvent(1); assertEquals("count of nodeDown events", 2, m_jdbcTemplate.queryForInt("select count(*) from events where eventuei = '" + EventConstants.NODE_DOWN_EVENT_UEI + "'")); // Make sure there's still one alarm... assertEquals("alarm count", 1, countAlarms()); // ... with a counter value of 2 assertEquals("counter in the alarm", 2, m_jdbcTemplate.queryForInt("select counter from alarms")); // Sleep long enough for the escalation automation to run, then check that it was escalated Thread.sleep(VacuumdConfigFactory.getInstance().getAutomation("autoEscalate").getInterval() + 500); assertEquals("alarm severity wrong, should have been escalated", currentSeverity+1, verifyAlarmEscalated()); } finally { // Stop what you start m_vacuumd.stop(); } } public final void testConfigReload() { // TODO: Check configuration before and after EventBuilder builder = new EventBuilder(EventConstants.RELOAD_VACUUMD_CONFIG_UEI, "test"); Event e = builder.getEvent(); m_eventdIpcMgr.sendNow(e); } /** * Test resultSetHasRequiredActionColumns method * @throws SQLException */ @Test public final void testResultSetHasRequiredActionColumns() throws SQLException { Connection conn = null; try { conn = DataSourceFactory.getInstance().getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from events"); Collection<String> columns = new ArrayList<String>(); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); assertTrue(ap.getAction().resultSetHasRequiredActionColumns(rs, columns)); } finally { conn.close(); } } /** * Simple test on a helper method. */ @Test public final void testGetAutomations() { assertEquals(19, VacuumdConfigFactory.getInstance().getAutomations().size()); } @Test public final void testGetAutoEvents() { assertEquals(2, VacuumdConfigFactory.getInstance().getAutoEvents().size()); } /** * Simple test on a helper method. */ @Test public final void testGetTriggers() { assertEquals(14,VacuumdConfigFactory.getInstance().getTriggers().size()); } /** * Simple test on a helper method. */ @Test public final void testGetActions() { AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); assertEquals(18,VacuumdConfigFactory.getInstance().getActions().size()); assertEquals(2, ap.getAction().getTokenCount(VacuumdConfigFactory.getInstance().getAction("delete").getStatement().getContent())); } /** * Simple test on a helper method. */ @Test public final void testGetTrigger() { assertNotNull(VacuumdConfigFactory.getInstance().getTrigger("selectAll")); assertEquals(1, VacuumdConfigFactory.getInstance().getTrigger("selectAll").getRowCount()); assertEquals(">=", VacuumdConfigFactory.getInstance().getTrigger("selectAll").getOperator()); assertNotNull(VacuumdConfigFactory.getInstance().getTrigger("selectWithCounter")); assertNull(VacuumdConfigFactory.getInstance().getTrigger("selectWithCounter").getOperator()); assertEquals(0,VacuumdConfigFactory.getInstance().getTrigger("selectWithCounter").getRowCount()); } /** * Simple test on a helper method. */ @Test public final void testGetAction() { assertNotNull(VacuumdConfigFactory.getInstance().getAction("clear")); assertNotNull(VacuumdConfigFactory.getInstance().getAction("escalate")); assertNotNull(VacuumdConfigFactory.getInstance().getAction("delete")); } /** * Simple test on a helper method. */ @Test public final void testGetAutomation() { assertNotNull(VacuumdConfigFactory.getInstance().getAutomation("autoEscalate")); } /** * Simple test running a trigger. */ @Test @JUnitTemporaryDatabase(tempDbClass=MockDatabase.class) public final void testRunTrigger() throws InterruptedException { Trigger trigger = VacuumdConfigFactory.getInstance().getTrigger("selectAll"); String triggerSql = trigger.getStatement().getContent(); MockUtil.println("Running trigger query: "+triggerSql); int count = m_jdbcTemplate.queryForList(triggerSql).size(); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); assertFalse("Testing the result rows:"+count+" with the trigger operator "+trigger.getOperator()+" against the required rows:"+trigger.getRowCount(), ap.getTrigger().triggerRowCheck(trigger.getRowCount(), trigger.getOperator(), count)); assertEquals(0, count); } /** * This tests the running of automations directly as if they were scheduled. * @throws SQLException * @throws InterruptedException */ @Test @JUnitTemporaryDatabase(tempDbClass=MockDatabase.class) // Relies on records created in @Before so we need a fresh database public final void testRunAutomation() throws SQLException, InterruptedException { final int major = OnmsSeverity.MAJOR.getId(); bringNodeDownCreatingEvent(1); Thread.sleep(1000); assertEquals(1, countAlarms()); assertEquals(major, getSingleResultSeverity()); assertEquals("counter in the alarm", 1, m_jdbcTemplate.queryForInt("select counter from alarms")); bringNodeDownCreatingEvent(1); Thread.sleep(1000); assertEquals(1, countAlarms()); assertEquals(major, getSingleResultSeverity()); assertEquals("counter in the alarm", 2, m_jdbcTemplate.queryForInt("select counter from alarms")); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("autoEscalate")); assertTrue(ap.runAutomation()); Thread.sleep(1000); assertEquals(major+1, getSingleResultSeverity()); } @Test public final void testRunAutomationWithNoTrigger() throws InterruptedException, SQLException { bringNodeDownCreatingEvent(1); Thread.sleep(1000); assertEquals(1, countAlarms()); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cleanUpAlarms")); Thread.sleep(1000); assertTrue(ap.runAutomation()); } @Test public final void testRunAutomationWithZeroResultsFromTrigger() throws InterruptedException, SQLException { bringNodeDownCreatingEvent(1); Thread.sleep(1000); assertEquals(1, countAlarms()); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("testZeroResults")); Thread.sleep(1000); assertTrue(ap.runAutomation()); } /** * This tests the capabilities of the cosmicClear automation as shipped in the standard build. * @throws InterruptedException */ @Test public final void testCosmicClearAutomation() throws InterruptedException { // create node down events with severity 6 bringNodeDownCreatingEvent(1); bringNodeDownCreatingEvent(2); Thread.sleep(1000); // create node up event with severity 3 bringNodeUpCreatingEvent(1); Thread.sleep(1000); // should have three alarms, one for each event assertEquals("should have one alarm for each event", 3, m_jdbcTemplate.queryForLong("select count(*) from alarms")); AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); ap.run(); Thread.sleep(1000); // the automation should have cleared the nodeDown for node 1 so it should now have severity CLEARED == 2 assertEquals("alarms with severity == 2", 1, m_jdbcTemplate.queryForLong("select count(*) from alarms where severity = 2")); // There should still be a nodeUp alarm and an uncleared nodeDown alarm assertEquals("alarms with severity > 2", 2, m_jdbcTemplate.queryForLong("select count(*) from alarms where severity > 2")); // run this automation again and make sure nothing happens since we've already processed the clear ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); ap.run(); Thread.sleep(1000); // same as above assertEquals("alarms with severity == 2", 1, m_jdbcTemplate.queryForLong("select count(*) from alarms where severity = 2")); // save as above assertEquals("alarms with severity > 2", 2, m_jdbcTemplate.queryForLong("select count(*) from alarms where severity > 2")); } /** * @throws InterruptedException */ @Test @JUnitTemporaryDatabase(tempDbClass=MockDatabase.class) // Relies on records created in @Before so we need a fresh database public final void testSendEventWithParms() throws InterruptedException { // create node down events with severity 6 bringNodeDownCreatingEventWithReason(1, "Testing node1"); Thread.sleep(1000); new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("escalate")).run(); Thread.sleep(1000); Map<String, Object> queryResult = m_jdbcTemplate.queryForMap("SELECT eventuei, eventparms FROM events WHERE eventuei = 'uei.opennms.org/vacuumd/alarmEscalated'"); // If the add-all-parms="true" is set on the action-event, the parms will turn out like this // assertEquals("Parameter list sent from action event doesn't match", "eventReason=Testing node1(string,text);alarmId=1(string,text);alarmEventUei=uei.opennms.org/nodes/nodeDown(string,text)", queryResult.get("eventParms")); assertEquals("Parameter list sent from action event doesn't match", "alarmId=1(string,text);alarmEventUei=uei.opennms.org/nodes/nodeDown(string,text)", queryResult.get("eventParms")); } /** * Test the ability to find tokens in a statement. */ @Test public void testGetTokenizedColumns() { AutomationProcessor ap = new AutomationProcessor(VacuumdConfigFactory.getInstance().getAutomation("cosmicClear")); Collection<String> tokens = ap.getAction().getActionColumns(); //just this for now assertFalse(tokens.isEmpty()); } /** * Why not. */ @Test public final void testGetName() { assertEquals("OpenNMS.Vacuumd", m_vacuumd.getName()); } /** * */ @Test public final void testRunUpdate() { //TODO Implement runUpdate(). } @Test public final void testGetTriggerSqlWithNoTriggerDefined() { Automation auto = VacuumdConfigFactory.getInstance().getAutomation("cleanUpAlarms"); AutomationProcessor ap = new AutomationProcessor(auto); assertEquals(null, ap.getTrigger().getTriggerSQL()); } private int countAlarms() { return (int) m_jdbcTemplate.queryForLong("select count(*) from alarms"); } /** * Verifies for the concurrency test that the alarm escalated. * @return */ private int verifyAlarmEscalated() { return m_jdbcTemplate.queryForInt("select severity from alarms"); } /** * Returns the severity of the alarm in the alarms table. * @return */ private int getSingleResultSeverity() { return m_jdbcTemplate.queryForInt("select severity from alarms"); } private void bringNodeDownCreatingEvent(int nodeid) { MockNode node = m_network.getNode(nodeid); m_eventdIpcMgr.sendNow(node.createDownEvent()); } private void bringNodeDownCreatingEventWithReason(int nodeid, String reason) { MockNode node = m_network.getNode(nodeid); m_eventdIpcMgr.sendNow(node.createDownEventWithReason(reason)); } private void bringNodeUpCreatingEvent(int nodeid) { MockNode node = m_network.getNode(nodeid); m_eventdIpcMgr.sendNow(node.createUpEvent()); } }