/***************************************************************************** * * Copyright (C) Zenoss, Inc. 2010, all rights reserved. * * This content is made available according to terms specified in * License.zenoss under the directory where your Zenoss product is installed. * ****************************************************************************/ package org.zenoss.zep.impl; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; import org.zenoss.protobufs.model.Model.ModelElementType; import org.zenoss.protobufs.zep.Zep; import org.zenoss.protobufs.zep.Zep.Event; import org.zenoss.protobufs.zep.Zep.EventActor; import org.zenoss.protobufs.zep.Zep.EventDetail; import org.zenoss.protobufs.zep.Zep.EventSummary; import org.zenoss.protobufs.zep.Zep.SyslogPriority; import org.zenoss.zep.ZepConstants; import org.zenoss.zep.ZepException; import org.zenoss.zep.dao.EventSignalSpool; import org.zenoss.zep.dao.EventSignalSpoolDao; import org.zenoss.zep.impl.TriggerPlugin.RuleContext; import org.zenoss.zep.impl.TriggerPlugin.TriggerRuleCache; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ScheduledFuture; import static org.easymock.EasyMock.*; import static org.junit.Assert.*; public class TriggerPluginTest { public TriggerPlugin triggerPlugin = null; private EventSignalSpoolDao spoolDaoMock; private TaskScheduler schedulerMock; private ScheduledFuture futureMock; @Before public void testInit() throws IOException, ZepException { Map<String,String> props = new HashMap<String,String>(); this.triggerPlugin = new TriggerPlugin(); triggerPlugin.setTriggerRuleCacheSize(10); this.spoolDaoMock = createMock(EventSignalSpoolDao.class); expect(spoolDaoMock.findAllDue()).andReturn(Collections.<EventSignalSpool> emptyList()).anyTimes(); this.schedulerMock = createMock(TaskScheduler.class); this.futureMock = createNiceMock(ScheduledFuture.class); expect(schedulerMock.schedule(isA(Runnable.class), isA(Trigger.class))).andReturn(futureMock); replay(spoolDaoMock, schedulerMock, futureMock); this.triggerPlugin.setSignalSpoolDao(this.spoolDaoMock); this.triggerPlugin.setTaskScheduler(this.schedulerMock); this.triggerPlugin.start(props); } @After public void shutdown() throws InterruptedException { this.triggerPlugin.stop(); verify(this.spoolDaoMock, this.schedulerMock, this.futureMock); } private EventActor.Builder createActor() { EventActor.Builder actorBuilder = EventActor.newBuilder(); actorBuilder.setElementTypeId(ModelElementType.DEVICE); actorBuilder.setElementIdentifier("BHM1000"); actorBuilder.setElementTitle("BHM TITLE"); actorBuilder.setElementSubTypeId(ModelElementType.COMPONENT); actorBuilder.setElementSubIdentifier("Fuse-10A"); actorBuilder.setElementSubTitle("Fuse-10A Title"); return actorBuilder; } private Event.Builder createEventOccurrence(EventActor actor) { Event.Builder evtBuilder = Event.newBuilder(); evtBuilder.setActor(actor); evtBuilder.setMessage("TEST - 1-2-check"); evtBuilder.setEventClass("/Defcon/1"); evtBuilder.setSeverity(Zep.EventSeverity.SEVERITY_WARNING); evtBuilder.setSyslogPriority(SyslogPriority.SYSLOG_PRIORITY_DEBUG); EventDetail.Builder groupBuilder = evtBuilder.addDetailsBuilder().setName(ZepConstants.DETAIL_DEVICE_GROUPS); groupBuilder.addValue("/US/Texas/Austin"); EventDetail.Builder systemsBuilder = evtBuilder.addDetailsBuilder().setName(ZepConstants.DETAIL_DEVICE_SYSTEMS); systemsBuilder.addValue("/Production/Infrastructure"); return evtBuilder; } private EventSummary.Builder createEvent(Event event) { EventSummary.Builder evtSumBuilder = EventSummary.newBuilder(); evtSumBuilder.setCount(10); evtSumBuilder.setStatus(Zep.EventStatus.STATUS_NEW); evtSumBuilder.addOccurrence(event); return evtSumBuilder; } @Test public void testTriggerRules() throws IOException { EventActor.Builder actorBuilder = createActor(); // build test Event to add to EventSummary as occurrence[0] Event.Builder eventBuilder = createEventOccurrence(actorBuilder.build()); // build test EventSummary EventSummary evtSummary = createEvent(eventBuilder.build()).build(); // test various rules String[] true_rules = { "1 == 1", "evt.message.startswith('TEST')", "evt.severity == 3", "evt.event_class == '/Defcon/1'", "evt.count > 5", "dev.name == 'BHM TITLE'", "elem.name == 'BHM TITLE'", "elem.type == 'DEVICE'", "sub_elem.type == 'COMPONENT'", "sub_elem.name.lower().startswith('fuse')", "evt.syslog_priority == 7", "\"/US\" in dev.groups", "\"/US/Texas\" in dev.groups", "\"/US/Texas/Austin\" in dev.groups", "\"/Production\" in dev.systems", "\"/Production/Infrastructure\" in dev.systems" }; String[] false_rules = { "1 = 0", // try a syntax error "", // try empty string "evt.msg == 'fail!'", // nonexistent attribute "1 == 0", "evt.message.startswith('BEST')", "evt.count > 15", "evt.severity = 'critical'", "dev.name == 'BHM1001'", "evt.syslog_priority == 5", "\"/US/Tex\" in dev.groups", "\"/US/TexasTheLoneStarState\" in dev.groups", "\"/US/Texas/Austin/Bridge Point\" in dev.groups", "\"/Texas\" in dev.groups", "\"/Austin\" in dev.groups", "\"/Prod\" in dev.systems", "\"/Infrastructure\" in dev.systems" }; RuleContext ctx = RuleContext.createContext(triggerPlugin.pythonHelper.getToObject(), evtSummary); for (String rule : true_rules) { String triggerUuid = UUID.randomUUID().toString(); assertTrue(rule + " (should evaluate True)", this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); } for (String rule : false_rules) { String triggerUuid = UUID.randomUUID().toString(); assertFalse(rule + " (should evaluate False)", this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); } } @Test public void testEmptyGroups() { EventSummary.Builder evtSummary = createEvent(createEventOccurrence(createActor().build()).build()); // Remove the group detail evtSummary.getOccurrenceBuilder(0).removeDetails(0); String triggerUuid = UUID.randomUUID().toString(); String rule = "\"/Production/Infrastructure\" not in dev.groups"; RuleContext ctx = RuleContext.createContext(triggerPlugin.pythonHelper.getToObject(), evtSummary.build()); assertEquals(0, ctx.device.__getattr__("groups").__len__()); assertTrue(rule + " (should evaluate True)", this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); } @Test public void testEmptySystems() { EventSummary.Builder evtSummary = createEvent(createEventOccurrence(createActor().build()).build()); // Remove the systems detail evtSummary.getOccurrenceBuilder(0).removeDetails(1); String triggerUuid = UUID.randomUUID().toString(); String rule = "\"/Production/Infrastructure\" not in dev.systems"; RuleContext ctx = RuleContext.createContext(triggerPlugin.pythonHelper.getToObject(), evtSummary.build()); assertEquals(0, ctx.device.__getattr__("systems").__len__()); assertTrue(rule + " (should evaluate True)", this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); } @Test public void testEmptyComponent() { // do not set a sub element but have a rule referencing it EventActor.Builder actorBuilder = EventActor.newBuilder(); actorBuilder.setElementTypeId(ModelElementType.DEVICE); actorBuilder.setElementIdentifier("BHM1000"); actorBuilder.setElementTitle("BHM TITLE"); EventSummary.Builder evtSummary = createEvent(createEventOccurrence(actorBuilder.build()).build()); String triggerUuid = UUID.randomUUID().toString(); String rule = "(\"chassis-12\" not in sub_elem.name) and (\"ucs-12\" not in elem.name)"; RuleContext ctx = RuleContext.createContext(triggerPlugin.pythonHelper.getToObject(), evtSummary.build()); assertTrue(rule + " (should evalutate True)", this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); } @Test public void testCacheInvalidSyntax() { EventActor.Builder actorBuilder = createActor(); Event.Builder eventBuilder = createEventOccurrence(actorBuilder.build()); EventSummary evtSummary = createEvent(eventBuilder.build()).build(); // Validate that we cache an invalid rule - prevents compiling the same rule over and over again String triggerUuid = UUID.randomUUID().toString(); String rule = "THIS IS INVALID PYTHON"; RuleContext ctx = RuleContext.createContext(triggerPlugin.pythonHelper.getToObject(), evtSummary); assertFalse(this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); TriggerRuleCache ruleCache = this.triggerPlugin.triggerRuleCache.get(triggerUuid); assertNotNull(ruleCache); assertEquals(rule, ruleCache.getRuleSource()); assertNull(ruleCache.getPyFunction()); // Run it again and validate that the ruleCache didn't change. assertFalse(this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); assertEquals(ruleCache, this.triggerPlugin.triggerRuleCache.get(triggerUuid)); // Validate that changing trigger rule to valid stores new value in cache rule = "evt.status == " + evtSummary.getStatus().getNumber(); assertTrue(this.triggerPlugin.eventSatisfiesRule(ctx, triggerUuid, rule)); ruleCache = this.triggerPlugin.triggerRuleCache.get(triggerUuid); assertNotNull(ruleCache); assertEquals(rule, ruleCache.getRuleSource()); assertNotNull(ruleCache.getPyFunction()); } }