From 7c83e4bcb24ee1573f3ae1fc1409671ca36bde75 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Wed, 25 Sep 2024 15:17:37 -0500 Subject: [PATCH 1/4] Mock MetricReporter to prevent sending events for real. --- Products/ZenEvents/tests/testTransforms.py | 211 +++++++++++++-------- 1 file changed, 129 insertions(+), 82 deletions(-) diff --git a/Products/ZenEvents/tests/testTransforms.py b/Products/ZenEvents/tests/testTransforms.py index 63bc52e7dc..12f0090eb6 100644 --- a/Products/ZenEvents/tests/testTransforms.py +++ b/Products/ZenEvents/tests/testTransforms.py @@ -1,19 +1,26 @@ ############################################################################## -# +# # Copyright (C) Zenoss, Inc. 2009, 2011, 2012, all rights reserved. -# +# # This content is made available according to terms specified in # License.zenoss under the directory where your Zenoss product is installed. -# +# ############################################################################## +from mock import patch from Products.ZenTestCase.BaseTestCase import BaseTestCase from Products.ZenEvents.zeneventd import EventPipelineProcessor from Products.ZenEvents.events2.processing import DropEvent from Products.ZenEvents.events2.proxy import EventProxy -from zenoss.protocols.protobufs.zep_pb2 import Event, STATUS_CLOSED, STATUS_SUPPRESSED, SEVERITY_ERROR,\ - SEVERITY_WARNING, SEVERITY_CLEAR +from zenoss.protocols.protobufs.zep_pb2 import ( + Event, + STATUS_CLOSED, + STATUS_SUPPRESSED, + SEVERITY_ERROR, + SEVERITY_WARNING, + SEVERITY_CLEAR, +) from zenoss.protocols.protobufs.model_pb2 import DEVICE, COMPONENT from Products.ZenUtils.guid.interfaces import IGlobalIdentifier @@ -24,11 +31,15 @@ # Extract the used blocks from the event's message import re - m = re.search("threshold of [^:]+: current value ([\d\.]+)", evt.message) + m = re.search( + "threshold of [^:]+: current value ([\d\.]+)", evt.message + ) if not m: continue # Get the total blocks from the model. Adjust by specified offset. - totalBlocks = f.totalBlocks * getattr(device, "zFileSystemSizeOffset", 1.0) + totalBlocks = f.totalBlocks * getattr( + device, "zFileSystemSizeOffset", 1.0 + ) totalBytes = totalBlocks * f.blockSize usedBytes = None @@ -47,41 +58,52 @@ free = convToUnits(totalBytes - usedBytes) # Make a nicer summary - evt.summary = "disk space threshold: %3.1f%% used (%s free)" % (p, free) + evt.summary = ( + "disk space threshold: %3.1f%% used (%s free)" + ) % (p, free) evt.message = evt.summary break """ -class testTransforms(BaseTestCase): +PATH = {"src": "Products.ZenEvents.zeneventd"} +class TestTransforms(BaseTestCase): def afterSetUp(self): - super(testTransforms, self).afterSetUp() + super(TestTransforms, self).afterSetUp() class MockConnection(object): def sync(self): pass + self.dmd._p_jar = MockConnection() - self.dmd.Events.createOrganizer('/Perf/Filesystem') + self.dmd.Events.createOrganizer("/Perf/Filesystem") self.dmd.Events.Perf.Filesystem.transform = perfFilesystemTransform + self.MetricReporter_patcher = patch( + "{src}.MetricReporter".format(**PATH) + ) + self.MetricReporter_mock = self.MetricReporter_patcher.start() + self.addCleanup(self.MetricReporter_patcher.stop) + self.processor = EventPipelineProcessor(self.dmd) self.processor.reporter.stop() def _processEvent(self, event): - # Don't return a sub-message from a C++ protobuf class - can crash as the parent is GC'd + # Don't return a sub-message from a C++ protobuf class - can + # crash as the parent is GC'd. return self.processor.processMessage(event) - + def testPerfFileSystemTransformPerfFS(self): """ Test to make sure that the standard transform on the /Perf/Filesystem event class works properly for stock performance templates. """ - + # Test an example event from a standard SNMP device. - device = self.dmd.Devices.createInstance('snmpdevice') - device.os.addFileSystem('/', False) + device = self.dmd.Devices.createInstance("snmpdevice") + device.os.addFileSystem("/", False) fs = device.os.filesystems()[0] - fs.mount = '/' + fs.mount = "/" fs.blockSize = 4096 fs.totalBlocks = 29221228 @@ -91,19 +113,24 @@ def testPerfFileSystemTransformPerfFS(self): event.actor.element_sub_identifier = fs.name() event.actor.element_sub_type_id = COMPONENT event.severity = SEVERITY_WARNING - event.event_key = 'usedBlocks_usedBlocks|high disk usage' - event.event_class = '/Perf/Filesystem' - event.summary = 'threshold of high disk usage exceeded: current value 23476882.00' + event.event_key = "usedBlocks_usedBlocks|high disk usage" + event.event_class = "/Perf/Filesystem" + event.summary = ( + "threshold of high disk usage exceeded: current value 23476882.00" + ) processed = self._processEvent(event) - self.assertEquals(processed.event.summary, 'disk space threshold: 80.3% used (21.9GB free)') - + self.assertEquals( + processed.event.summary, + "disk space threshold: 80.3% used (21.9GB free)", + ) + def testPerfFileSystemTransformPerfmon(self): # Test an example event from a standard Perfmon device. - device = self.dmd.Devices.createInstance('perfmondevice') - device.os.addFileSystem('C', False) + device = self.dmd.Devices.createInstance("perfmondevice") + device.os.addFileSystem("C", False) fs = device.os.filesystems()[0] - fs.mount = ' Label:C: Serial Number: 1471843B' + fs.mount = " Label:C: Serial Number: 1471843B" fs.blockSize = 8192 fs.totalBlocks = 1047233 @@ -113,19 +140,24 @@ def testPerfFileSystemTransformPerfmon(self): event.actor.element_sub_identifier = fs.name() event.actor.element_sub_type_id = COMPONENT event.severity = SEVERITY_WARNING - event.event_key = 'FreeMegabytes_FreeMegabytes' - event.event_class = '/Perf/Filesystem' - event.summary = 'threshold of low disk space not met: current value 4156.00' - + event.event_key = "FreeMegabytes_FreeMegabytes" + event.event_class = "/Perf/Filesystem" + event.summary = ( + "threshold of low disk space not met: current value 4156.00" + ) + processed = self._processEvent(event) - self.assertEquals(processed.event.summary, 'disk space threshold: 49.2% used (4.1GB free)') - + self.assertEquals( + processed.event.summary, + "disk space threshold: 49.2% used (4.1GB free)", + ) + def testPerfFileSystemTransformSSH(self): # Test an example event from a standard SSH device. - device = self.dmd.Devices.createInstance('sshdevice') - device.os.addFileSystem('/', False) + device = self.dmd.Devices.createInstance("sshdevice") + device.os.addFileSystem("/", False) fs = device.os.filesystems()[0] - fs.mount = '/' + fs.mount = "/" fs.blockSize = 1024 fs.totalBlocks = 149496116 @@ -135,27 +167,32 @@ def testPerfFileSystemTransformSSH(self): event.actor.element_sub_identifier = fs.id event.actor.element_sub_type_id = COMPONENT event.severity = SEVERITY_WARNING - event.event_key = 'disk|disk_usedBlocks|Free Space 90 Percent' - event.event_class = '/Perf/Filesystem' - event.summary = 'threshold of Free Space 90 Percent exceeded: current value 73400348.00' + event.event_key = "disk|disk_usedBlocks|Free Space 90 Percent" + event.event_class = "/Perf/Filesystem" + event.summary = ( + "threshold of Free Space 90 Percent exceeded: " + "current value 73400348.00" + ) processed = self._processEvent(event) - self.assertEquals(processed.event.summary, 'disk space threshold: 49.1% used (72.6GB free)') - + self.assertEquals( + processed.event.summary, + "disk space threshold: 49.1% used (72.6GB free)", + ) + def testActorReidentificationFromEventClassKeyTransform(self): """ - Verify that changing the device in a transform properly reidentifies the device - when matching an event by eventClassKey. + Verify that changing the device in a transform properly reidentifies + the device when matching an event by eventClassKey. """ - device_a = self.dmd.Devices.createInstance("transform_device_a") # Related: ZEN-1419 # If you change a device from within a transform like so: - # + # # evt.device = 'my_new_device' # - # The processing pipeline will recognize this and re-run the + # The processing pipeline will recognize this and re-run the # identification pipes. Before it re-runs these pipes though, it will # clear several properties related to the device, one of which is the # device/element UUID. During the Identification pipe, if the UUID @@ -167,11 +204,11 @@ def testActorReidentificationFromEventClassKeyTransform(self): device_b = self.dmd.Devices.createInstance("transform_device_b") - _transform_key = 'transform_test_key' + _transform_key = "transform_test_key" _transform = """ evt.device = '%s' """ - self.dmd.Events.createOrganizer('/transform_test') + self.dmd.Events.createOrganizer("/transform_test") self.dmd.Events.transform_test.transform = _transform % device_b.id # the organizer above contains the transform, no create an instance @@ -182,7 +219,7 @@ def testActorReidentificationFromEventClassKeyTransform(self): event.actor.element_identifier = device_a.id event.actor.element_type_id = DEVICE event.severity = SEVERITY_WARNING - event.summary = 'Testing transforms.' + event.summary = "Testing transforms." detail = event.details.add() detail.name = EventProxy.DEVICE_IP_ADDRESS_DETAIL_KEY @@ -192,10 +229,14 @@ def testActorReidentificationFromEventClassKeyTransform(self): event.event_class_key = _transform_key processed = self._processEvent(event) - self.assertEquals(device_b.id, processed.event.actor.element_identifier) - self.assertEquals(IGlobalIdentifier(device_b).getGUID(), - processed.event.actor.element_uuid) - + self.assertEquals( + device_b.id, processed.event.actor.element_identifier + ) + self.assertEquals( + IGlobalIdentifier(device_b).getGUID(), + processed.event.actor.element_uuid, + ) + def testActorReidentificationFromEventClassKeyTransformWithComponent(self): """ Verify that changing the device in a transform properly reidentifies @@ -210,16 +251,18 @@ def testActorReidentificationFromEventClassKeyTransformWithComponent(self): devB.os.addFileSystem("component", False) devB.setManageIp("192.168.100.101") - _transform_key = 'transform_test_key' - self.dmd.Events.createOrganizer('/transform_test') - self.dmd.Events.transform_test.transform = "evt.device = '%s'" % devB.id + _transform_key = "transform_test_key" + self.dmd.Events.createOrganizer("/transform_test") + self.dmd.Events.transform_test.transform = ( + "evt.device = '%s'" % devB.id + ) self.dmd.Events.transform_test.createInstance(_transform_key) event = Event() event.actor.element_identifier = devA.id event.actor.element_type_id = DEVICE event.severity = SEVERITY_WARNING - event.summary = 'Testing transforms on component.' + event.summary = "Testing transforms on component." event.actor.element_sub_type_id = COMPONENT event.actor.element_sub_identifier = devA.getDeviceComponents()[0].id @@ -230,81 +273,85 @@ def testActorReidentificationFromEventClassKeyTransformWithComponent(self): # Match the transform by event_class_key event.event_class_key = _transform_key processed = self._processEvent(event) - self.assertEquals(IGlobalIdentifier(devB.getDeviceComponents()[0]).getGUID(), - processed.event.actor.element_sub_uuid) - + self.assertEquals( + IGlobalIdentifier(devB.getDeviceComponents()[0]).getGUID(), + processed.event.actor.element_sub_uuid, + ) + def testIntSeverityTransform(self): """ Transform the event severity to a string and see if it evaluates. """ transform = 'evt.severity="0"; evt.summary="transformed"' - self.dmd.Events.createOrganizer('/Perf/Filesystem') + self.dmd.Events.createOrganizer("/Perf/Filesystem") self.dmd.Events.Perf.Filesystem.transform = transform event = Event() - event.actor.element_identifier = 'localhost' + event.actor.element_identifier = "localhost" event.actor.element_type_id = DEVICE event.severity = SEVERITY_ERROR - event.event_class = '/Perf/Filesystem' - event.summary = 'bad thingy' + event.event_class = "/Perf/Filesystem" + event.summary = "bad thingy" processed = self._processEvent(event) self.assertEqual(SEVERITY_CLEAR, processed.event.severity) - self.assertEqual('transformed', processed.event.summary) + self.assertEqual("transformed", processed.event.summary) self.assert_(isinstance(processed.event.severity, int)) def testActionDropped(self): transform = 'evt._action="drop"' - self.dmd.Events.createOrganizer('/Perf/Filesystem') + self.dmd.Events.createOrganizer("/Perf/Filesystem") self.dmd.Events.Perf.Filesystem.transform = transform event = Event() - event.actor.element_identifier = 'localhost' + event.actor.element_identifier = "localhost" event.actor.element_type_id = DEVICE event.severity = SEVERITY_ERROR - event.event_class = '/Perf/Filesystem' - event.summary = 'should be dropped' + event.event_class = "/Perf/Filesystem" + event.summary = "should be dropped" self.assertRaises(DropEvent, self._processEvent, event) def testActionHistory(self): transform = 'evt._action="history"' - self.dmd.Events.createOrganizer('/Perf/Filesystem') + self.dmd.Events.createOrganizer("/Perf/Filesystem") self.dmd.Events.Perf.Filesystem.transform = transform event = Event() - event.actor.element_identifier = 'localhost' + event.actor.element_identifier = "localhost" event.actor.element_type_id = DEVICE event.severity = SEVERITY_ERROR - event.event_class = '/Perf/Filesystem' - event.summary = 'should be closed' + event.event_class = "/Perf/Filesystem" + event.summary = "should be closed" processed = self._processEvent(event) self.assertEqual(STATUS_CLOSED, processed.event.status) def testActionStatusDoesntChangeSuppressed(self): """ - If an event comes in as suppressed and the _action says to keep it in _status (the default), - make sure that we don't accidentally change the status of the event back to STATUS_NEW. + If an event comes in as suppressed and the _action says to keep it in + _status (the default), make sure that we don't accidentally change + the status of the event back to STATUS_NEW. """ transform = 'evt._action="status"' - self.dmd.Events.createOrganizer('/Perf/Filesystem') + self.dmd.Events.createOrganizer("/Perf/Filesystem") self.dmd.Events.Perf.Filesystem.transform = transform event = Event() - event.actor.element_identifier = 'localhost' + event.actor.element_identifier = "localhost" event.actor.element_type_id = DEVICE event.severity = SEVERITY_ERROR event.status = STATUS_SUPPRESSED - event.event_class = '/Perf/Filesystem' - event.summary = 'should be suppressed' + event.event_class = "/Perf/Filesystem" + event.summary = "should be suppressed" processed = self._processEvent(event) self.assertEqual(STATUS_SUPPRESSED, processed.event.status) -def test_suite(): - from unittest import TestSuite, makeSuite - suite = TestSuite() - suite.addTest(makeSuite(testTransforms)) - return suite +# def test_suite(): +# from unittest import TestSuite, makeSuite +# +# suite = TestSuite() +# suite.addTest(makeSuite(testTransforms)) +# return suite From ba1d275338579bfd6c84608154bdf7552d65c0ee Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Wed, 25 Sep 2024 15:18:59 -0500 Subject: [PATCH 2/4] Refactored TrapFilter implementation. --- .../zentrap/tests/test_trapfilter.py | 172 ++++++------ Products/ZenEvents/zentrap/trapfilter.py | 244 +++++++++++------- 2 files changed, 237 insertions(+), 179 deletions(-) diff --git a/Products/ZenEvents/zentrap/tests/test_trapfilter.py b/Products/ZenEvents/zentrap/tests/test_trapfilter.py index 496273a176..d509408fe7 100644 --- a/Products/ZenEvents/zentrap/tests/test_trapfilter.py +++ b/Products/ZenEvents/zentrap/tests/test_trapfilter.py @@ -44,7 +44,7 @@ def testDropV1EventForGenericTrapInclusion(t): t.filter._filterspec._v1Traps[genericTrap] = filterDef event = {"snmpVersion": "1", "snmpV1GenericTrapType": genericTrap} - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV1EventForGenericTrapForExclusion(t): genericTrap = 1 @@ -52,7 +52,7 @@ def testDropV1EventForGenericTrapForExclusion(t): t.filter._filterspec._v1Traps[genericTrap] = filterDef event = {"snmpVersion": "1", "snmpV1GenericTrapType": genericTrap} - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForGenericTrapForNoMatch(t): genericTrap = 1 @@ -60,7 +60,7 @@ def testDropV1EventForGenericTrapForNoMatch(t): t.filter._filterspec._v1Traps[genericTrap] = filterDef event = {"snmpVersion": "1", "snmpV1GenericTrapType": 2} - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForEnterpriseSimpleGlobMatch(t): filterDef = V1FilterDefinition(99, "exclude", "1.2.3.*") @@ -72,10 +72,10 @@ def testDropV1EventForEnterpriseSimpleGlobMatch(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1.2.3.4", } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) filterDef.action = "include" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) # This test uses 1 filters for each of two OID levels where the filter # specifies a glob match @@ -93,34 +93,34 @@ def testDropV1EventForSimpleGlobMatches(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1.2.3.4", } - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.99" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.99.5" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4.99" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4.5" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4.5.99" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.99.4" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.99.4.5.6" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventIncludeAll(t): filterDef = V1FilterDefinition(99, "include", "*") @@ -132,13 +132,13 @@ def testDropV1EventIncludeAll(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1", } - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1." - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV1EventExcludeAll(t): filterDef = V1FilterDefinition(99, "exclude", "*") @@ -150,10 +150,10 @@ def testDropV1EventExcludeAll(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1", } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventExcludeAllBut(t): filterDef = V1FilterDefinition(99, "exclude", "*") @@ -174,29 +174,29 @@ def testDropV1EventExcludeAllBut(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1", } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.4.5.1" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.4.5" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.4.5" event["snmpV1SpecificTrap"] = 23 - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4.5" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV1EventIncludeAllBut(t): filterDef = V1FilterDefinition(99, "include", "*") @@ -217,25 +217,25 @@ def testDropV1EventIncludeAllBut(t): "snmpV1GenericTrapType": 6, "snmpV1Enterprise": "1", } - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.4.5.1" - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.4.5" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4.5" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForInvalidGenericTrap(t): filterDef = V1FilterDefinition(99, "include", "*") @@ -247,7 +247,7 @@ def testDropV1EventForInvalidGenericTrap(t): "snmpV1GenericTrapType": 9, "snmpV1Enterprise": "1.2", } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForMissingGenericTrap(t): filterDef = V1FilterDefinition(99, "include", "*") @@ -255,7 +255,7 @@ def testDropV1EventForMissingGenericTrap(t): t.filter._filterspec._v1Filters[1] = filtersByLevel event = {"snmpVersion": "1", "snmpV1Enterprise": "1.2"} - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForMissingEnterpriseOID(t): filterDef = V1FilterDefinition(99, "include", "*") @@ -266,7 +266,7 @@ def testDropV1EventForMissingEnterpriseOID(t): "snmpVersion": "1", "snmpV1GenericTrapType": 6, } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForEnterpriseAllExcept(t): filterDef = V1FilterDefinition(99, "include", "1.2.3") @@ -284,16 +284,16 @@ def testDropV1EventForEnterpriseAllExcept(t): "snmpV1Enterprise": "1.2.3", "snmpV1SpecificTrap": 59, } - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1SpecificTrap"] = 99 - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV1EventForEnterpriseSpecific(t): filterDef = V1FilterDefinition(99, "include", "1.2.3") @@ -311,19 +311,19 @@ def testDropV1EventForEnterpriseSpecific(t): "snmpV1Enterprise": "1.2.3", "snmpV1SpecificTrap": 59, } - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1SpecificTrap"] = 60 - t.assertFalse(t.filter._dropV1Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["snmpV1SpecificTrap"] = 1 - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2.3.4" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["snmpV1Enterprise"] = "1.2" - t.assertTrue(t.filter._dropV1Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV2EventForSimpleExactMatch(t): filterDef = V2FilterDefinition(99, "exclude", "1.2.3.4") @@ -331,10 +331,10 @@ def testDropV2EventForSimpleExactMatch(t): t.filter._filterspec._v2Filters[4] = filtersByLevel event = {"snmpVersion": "2", "oid": "1.2.3.4"} - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) filterDef.action = "include" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV2EventForSimpleGlobMatch(t): filterDef = V2FilterDefinition(99, "exclude", "1.2.3.*") @@ -342,10 +342,10 @@ def testDropV2EventForSimpleGlobMatch(t): t.filter._filterspec._v2Filters[4] = filtersByLevel event = {"snmpVersion": "2", "oid": "1.2.3.4"} - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) filterDef.action = "include" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) # This test uses 1 filters for each of two OID levels where the filter # specifies an exact match @@ -359,23 +359,23 @@ def testDropV2EventForSimpleExactMatches(t): t.filter._filterspec._v2Filters[4] = filtersByLevel event = {"snmpVersion": "2", "oid": "1.2.3"} - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) # OIDs with fewer or more levels than the existing filters # should NOT match event["oid"] = "1.2" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.9" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) # OIDs that differ only in the last level should NOT match event["oid"] = "1.2.9" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3.9" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) # This test uses 1 filters for each of two OID levels where the filter # specifies a glob match @@ -389,34 +389,34 @@ def testDropV2EventForSimpleGlobMatches(t): t.filter._filterspec._v2Filters[6] = filtersByLevel event = {"snmpVersion": "2", "oid": "1.2.3.4"} - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.99" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.99.5" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.99" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.5" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.5.99" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.99.4" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.99.4.5.6" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV2EventIncludeAll(t): filterDef = V2FilterDefinition(99, "include", "*") @@ -424,13 +424,13 @@ def testDropV2EventIncludeAll(t): t.filter._filterspec._v2Filters[1] = filtersByLevel event = {"snmpVersion": "2", "oid": "1"} - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1." - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV2EventExcludeAll(t): filterDef = V2FilterDefinition(99, "exclude", "*") @@ -438,10 +438,10 @@ def testDropV2EventExcludeAll(t): t.filter._filterspec._v2Filters[1] = filtersByLevel event = {"snmpVersion": "2", "oid": "1"} - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropV2EventExcludeAllBut(t): filterDef = V2FilterDefinition(99, "exclude", "*") @@ -457,25 +457,25 @@ def testDropV2EventExcludeAllBut(t): t.filter._filterspec._v2Filters[3] = filtersByLevel event = {"snmpVersion": "2", "oid": "1"} - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.4.5.1" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.4.5" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.5" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) def testDropV2EventIncludeAllBut(t): filterDef = V2FilterDefinition(99, "include", "*") @@ -491,25 +491,25 @@ def testDropV2EventIncludeAllBut(t): t.filter._filterspec._v2Filters[3] = filtersByLevel event = {"snmpVersion": "2", "oid": "1"} - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.2.3" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.4.5.1" - t.assertFalse(t.filter._dropV2Event(event)) + t.assertFalse(t.filter._dropEvent(event)) event["oid"] = "1.4.5" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) event["oid"] = "1.2.3.4.5" - t.assertTrue(t.filter._dropV2Event(event)) + t.assertTrue(t.filter._dropEvent(event)) def testDropEvent(t): filterDef = V1FilterDefinition(99, "include", "*") diff --git a/Products/ZenEvents/zentrap/trapfilter.py b/Products/ZenEvents/zentrap/trapfilter.py index 9ad76e47d0..e657b6f76a 100644 --- a/Products/ZenEvents/zentrap/trapfilter.py +++ b/Products/ZenEvents/zentrap/trapfilter.py @@ -44,7 +44,11 @@ def __init__(self, app, spec): self._app = app self._filterspec = spec self._checksum = None - self._genericTraps = frozenset([0, 1, 2, 3, 4, 5]) + self._filters = ( + GenericV1Predicate(self._filterspec.v1traps), + EnterpriseV1Predicate(self._filterspec.v1filters), + SnmpV2Predicate(self._filterspec.v2filters), + ) # implements ICollectorEventTransformer def transform(self, event): @@ -62,9 +66,9 @@ def transform(self, event): result = TRANSFORM_CONTINUE snmpVersion = event.get("snmpVersion", None) if snmpVersion and self._filterspec.defined: - log.debug("Filtering V%s event %s", snmpVersion, event) + log.debug("filtering V%s event %s", snmpVersion, event) if self._dropEvent(event): - log.debug("Dropping event %s", event) + log.debug("dropping event %s", event) self._app.counters["eventFilterDroppedCount"] += 1 result = TRANSFORM_DROP else: @@ -111,117 +115,171 @@ def _dropEvent(self, event): False if the event be kept. @rtype: boolean """ - result = True - snmpVersion = event.get("snmpVersion", None) + trapfilter = next( + (tf for tf in self._filters if tf.is_valid(event)), None + ) + if trapfilter: + return trapfilter(event) + log.error("dropping unknown trap event=%r", event) + return True - if snmpVersion == "1": - result = self._dropV1Event(event) - elif snmpVersion == "2" or snmpVersion == "3": - result = self._dropV2Event(event) - else: - log.error( - "Unknown snmp version %s, Dropping event:%r", - snmpVersion, - event, - ) - return result +class TrapFilterPredicate(object): + """ + Base class for predicates that determine whether a trap is ignored. - def _dropV1Event(self, event): - genericTrap = event.get("snmpV1GenericTrapType", None) - if genericTrap is not None and genericTrap in self._genericTraps: - filterDefinition = self._filterspec.v1traps.get(genericTrap, None) - if filterDefinition is None: - return True - return filterDefinition.action == "exclude" + Predicate implementations will return True indicating that the + event should be ignored. + """ - if genericTrap != 6: - log.error( - "Generic trap '%s' is invalid for V1 event: %s", - genericTrap, - event, - ) - return True + def __init__(self, definitions): + self._definitions = definitions + + def is_valid(self, event): + return False + + def __call__(self, event): + return False + + +class GenericV1Predicate(TrapFilterPredicate): + def __init__(self, *args): + super(GenericV1Predicate, self).__init__(*args) + self._genericTraps = frozenset([0, 1, 2, 3, 4, 5]) - enterpriseOID = event.get("snmpV1Enterprise", None) - if enterpriseOID is None: + def is_valid(self, event): + if event.get("snmpVersion", None) != "1": + return False + if event.get("snmpV1GenericTrapType", None) not in self._genericTraps: + return False + return True + + def __call__(self, event): + traptype = event.get("snmpV1GenericTrapType", None) + definition = self._definitions.get(traptype, None) + if definition and definition.action != "exclude": + return False + return True + + +class EnterpriseV1Predicate(TrapFilterPredicate): + def is_valid(self, event): + if event.get("snmpVersion", None) != "1": + return False + if event.get("snmpV1GenericTrapType", None) != 6: + return False + return True + + def __call__(self, event): + oid = event.get("snmpV1Enterprise", None) + if oid is None: log.error( "No OID found for enterprise-specific trap for V1 event: %s", event, ) return True - specificTrap = event.get("snmpV1SpecificTrap", None) - if specificTrap is not None: - key = "".join([enterpriseOID, "-", str(specificTrap)]) - filterDefinition = self._findFilterByLevel( - key, self._filterspec.v1filters - ) - if filterDefinition is not None: - log.debug( - "_dropV1Event: matched definition %s", filterDefinition + result = _check_definitions( + ( + getter() + for getter in ( + # order is important! + lambda: self._getSpecificTrapDefinition(event, oid), + lambda: self._getWildCardDefinition(oid), + lambda: self._getGlobMatchDefinition(oid), ) - return filterDefinition.action == "exclude" + ) + ) + if result: + log.debug("drop specific v1 trap trap=%s", event) + return result + def _getSpecificTrapDefinition(self, event, enterpriseOID): + specificTrap = event.get("snmpV1SpecificTrap", None) + if specificTrap is None: + return None + key = "".join([enterpriseOID, "-", str(specificTrap)]) + definition = _findFilterByLevel(key, self._definitions) + if definition: + log.debug("matched definition %s", definition) + return definition + + def _getWildCardDefinition(self, enterpriseOID): key = "".join([enterpriseOID, "-", "*"]) - filterDefinition = self._findFilterByLevel( - key, self._filterspec.v1filters + definition = _findFilterByLevel(key, self._definitions) + if definition: + log.debug("matched definition %s", definition) + return definition + + def _getGlobMatchDefinition(self, enterpriseOID): + definition = _findClosestGlobbedFilter( + enterpriseOID, self._definitions ) - if filterDefinition is not None: - log.debug("_dropV1Event: matched definition %s", filterDefinition) - return filterDefinition.action == "exclude" + if definition: + log.debug("matched definition %s", definition) + return definition - filterDefinition = self._findClosestGlobbedFilter( - enterpriseOID, self._filterspec.v1filters - ) - if filterDefinition is None: - log.debug("_dropV1Event: no matching definitions found") - return True - log.debug("_dropV1Event: matched definition %s", filterDefinition) - return filterDefinition.action == "exclude" +class SnmpV2Predicate(TrapFilterPredicate): + def is_valid(self, event): + return event.get("snmpVersion", None) in ("2", "3") - def _dropV2Event(self, event): + def __call__(self, event): oid = event["oid"] - - # First, try an exact match on the OID - filterDefinition = self._findFilterByLevel( - oid, self._filterspec.v2filters - ) - if filterDefinition is not None: - log.debug("_dropV2Event: matched definition %s", filterDefinition) - return filterDefinition.action == "exclude" - - # Convert the OID to its globbed equivalent and try that - filterDefinition = self._findClosestGlobbedFilter( - oid, self._filterspec.v2filters + result = _check_definitions( + ( + getter() + for getter in ( + # order is important! + lambda: self._getExactMatchDefinition(oid), + lambda: self._getGlobMatchDefinition(oid), + ) + ) ) - if filterDefinition is None: - log.debug("_dropV2Event: no matching definitions found") - return True - - log.debug("_dropV2Event: matched definition %s", filterDefinition) - return filterDefinition.action == "exclude" + if result: + log.debug("drop v2 trap trap=%s", event) + return result - def _findClosestGlobbedFilter(self, oid, filtersByLevel): - filterDefinition = None - globbedValue = oid - while globbedValue != "*": - globbedValue = getNextHigherGlobbedOid(globbedValue) - filterDefinition = self._findFilterByLevel( - globbedValue, filtersByLevel - ) - if filterDefinition: - break - return filterDefinition - - def _findFilterByLevel(self, oid, filtersByLevel): - filterDefinition = None - oidLevels = countOidLevels(oid) - filtersByOid = filtersByLevel.get(oidLevels, None) - if filtersByOid is not None and len(filtersByOid) > 0: - filterDefinition = filtersByOid.get(oid, None) - return filterDefinition + def _getExactMatchDefinition(self, oid): + # First, try an exact match on the OID + definition = _findFilterByLevel(oid, self._definitions) + if definition: + log.debug("matched definition %s", definition) + return definition + + def _getGlobMatchDefinition(self, oid): + definition = _findClosestGlobbedFilter(oid, self._definitions) + if definition: + log.debug("matched definition %s", definition) + return definition + + +def _check_definitions(definitions): + definition = next((defn for defn in definitions if defn is not None), None) + if definition is None: + log.debug("no matching definitions found") + return True + return definition.action == "exclude" + + +def _findClosestGlobbedFilter(oid, filtersByLevel): + filterDefinition = None + globbedValue = oid + while globbedValue != "*": + globbedValue = getNextHigherGlobbedOid(globbedValue) + filterDefinition = _findFilterByLevel(globbedValue, filtersByLevel) + if filterDefinition: + break + return filterDefinition + + +def _findFilterByLevel(oid, filtersByLevel): + filterDefinition = None + oidLevels = countOidLevels(oid) + filtersByOid = filtersByLevel.get(oidLevels, None) + if filtersByOid is not None and len(filtersByOid) > 0: + filterDefinition = filtersByOid.get(oid, None) + return filterDefinition def getNextHigherGlobbedOid(oid): From f1cc3e0eb4ae52903c45196af296384285abdaab Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Thu, 26 Sep 2024 16:01:07 -0500 Subject: [PATCH 3/4] Move IEventService definition to ZenHub. Moved the interface because PBDaemon is the class that actually implements the IEventService. --- Products/DataCollector/SnmpClient.py | 2 +- Products/DataCollector/SshClient.py | 2 +- Products/DataCollector/zenmodeler.py | 2 +- Products/ZenCollector/cyberark.py | 2 +- Products/ZenCollector/daemon.py | 5 +---- Products/ZenCollector/interfaces.py | 11 ++--------- Products/ZenCollector/utils/maintenance.py | 6 +++--- Products/ZenEvents/SyslogMsgFilter.py | 3 ++- Products/ZenHub/PBDaemon.py | 15 ++++++++++++--- Products/ZenHub/events/client.py | 3 +++ Products/ZenHub/events/queue/manager.py | 8 +++++--- Products/ZenHub/interfaces.py | 15 +++++++++++++++ Products/ZenHub/tests/test_PBDaemon.py | 4 +--- 13 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Products/DataCollector/SnmpClient.py b/Products/DataCollector/SnmpClient.py index f5397dbd66..2cc63f3c81 100644 --- a/Products/DataCollector/SnmpClient.py +++ b/Products/DataCollector/SnmpClient.py @@ -17,9 +17,9 @@ from twisted.internet.error import TimeoutError from pynetsnmp.twistedsnmp import snmpprotocol, Snmpv3Error -from Products.ZenCollector.interfaces import IEventService from Products.ZenEvents import Event from Products.ZenEvents.ZenEventClasses import Status_Snmp +from Products.ZenHub.interfaces import IEventService from Products.ZenUtils.Driver import drive from Products.ZenUtils.snmp import ( SnmpAgentDiscoverer, diff --git a/Products/DataCollector/SshClient.py b/Products/DataCollector/SshClient.py index d1bb89fbec..0dd4de0cbc 100644 --- a/Products/DataCollector/SshClient.py +++ b/Products/DataCollector/SshClient.py @@ -33,8 +33,8 @@ from Products.DataCollector import CollectorClient from Products.DataCollector.Exceptions import LoginFailed -from Products.ZenCollector.interfaces import IEventService from Products.ZenEvents import Event +from Products.ZenHub.interfaces import IEventService from Products.ZenUtils.IpUtil import getHostByName from Products.ZenUtils.Utils import getExitMessage diff --git a/Products/DataCollector/zenmodeler.py b/Products/DataCollector/zenmodeler.py index fe592f578a..12d68dbad9 100755 --- a/Products/DataCollector/zenmodeler.py +++ b/Products/DataCollector/zenmodeler.py @@ -53,8 +53,8 @@ ) from Products.ZenCollector.cyberark import get_cyberark from Products.ZenCollector.daemon import parseWorkerOptions, addWorkerOptions -from Products.ZenCollector.interfaces import IEventService from Products.ZenEvents.ZenEventClasses import Heartbeat, Error +from Products.ZenHub.interfaces import IEventService from Products.ZenHub.PBDaemon import FakeRemote, PBDaemon, HubDown from Products.ZenUtils.Driver import drive, driveLater from Products.ZenUtils.Utils import unused, zenPath, wait diff --git a/Products/ZenCollector/cyberark.py b/Products/ZenCollector/cyberark.py index 83d409d426..0791220011 100644 --- a/Products/ZenCollector/cyberark.py +++ b/Products/ZenCollector/cyberark.py @@ -23,9 +23,9 @@ from zope.component import queryUtility from Products.ZenEvents import Event +from Products.ZenHub.interfaces import IEventService from Products.ZenUtils.GlobalConfig import getGlobalConfiguration -from .interfaces import IEventService from .ExpiringCache import ExpiringCache _CFG_URL = "cyberark-url" diff --git a/Products/ZenCollector/daemon.py b/Products/ZenCollector/daemon.py index 67f76721ac..e5e5223d84 100644 --- a/Products/ZenCollector/daemon.py +++ b/Products/ZenCollector/daemon.py @@ -47,7 +47,6 @@ IConfigurationDispatchingFilter, IConfigurationListener, IDataService, - IEventService, IFrameworkFactory, IStatisticsService, ITaskSplitter, @@ -56,7 +55,7 @@ from .utils.maintenance import MaintenanceCycle, ZenHubHeartbeatSender -@implementer(ICollector, IDataService, IEventService) +@implementer(ICollector, IDataService) class CollectorDaemon(RRDDaemon): """The daemon class for the entire ZenCollector framework.""" @@ -110,7 +109,6 @@ def __init__( # that collector implementors can easily retrieve a reference back here # if needed provideUtility(self, ICollector) - provideUtility(self, IEventService) provideUtility(self, IDataService) # Register the collector's own preferences object so it may be easily @@ -394,7 +392,6 @@ def _startMaintenance(self): self.options.monitor, self.name, self.options.heartbeatTimeout, - self._eventqueue, ) else: heartbeatSender = None diff --git a/Products/ZenCollector/interfaces.py b/Products/ZenCollector/interfaces.py index 3930057da5..331305e175 100644 --- a/Products/ZenCollector/interfaces.py +++ b/Products/ZenCollector/interfaces.py @@ -9,6 +9,8 @@ import zope.interface +# IEventService imported here for ZenPack compability +from Products.ZenHub.interfaces import IEventService # noqa: F401 from Products.ZenUtils.observable import IObservable @@ -549,15 +551,6 @@ def writeRRD( """ -class IEventService(zope.interface.Interface): - """ - A service that allows the sending of an event. - """ - - def sendEvent(event, **kw): - pass - - class IFrameworkFactory(zope.interface.Interface): """ An abstract factory object that allows the collector framework to be diff --git a/Products/ZenCollector/utils/maintenance.py b/Products/ZenCollector/utils/maintenance.py index 61b63488c7..d4e29aad00 100644 --- a/Products/ZenCollector/utils/maintenance.py +++ b/Products/ZenCollector/utils/maintenance.py @@ -15,6 +15,7 @@ from zope.component import getUtility from Products.ZenEvents.ZenEventClasses import Heartbeat +from Products.ZenHub.interfaces import IEventService from Products.ZenMessaging.queuemessaging.interfaces import IQueuePublisher log = logging.getLogger("zen.maintenance") @@ -65,17 +66,16 @@ class ZenHubHeartbeatSender(object): Default heartbeat sender for CollectorDaemon. """ - def __init__(self, monitor, daemon, timeout, queue): + def __init__(self, monitor, daemon, timeout): self.__event = { "eventClass": Heartbeat, "device": monitor, "component": daemon, "timeout": timeout } - self.__queue = queue def heartbeat(self): - self.__queue.addHeartbeatEvent(self.__event) + getUtility(IEventService).sendHeartbeat(self.__event) class MaintenanceCycle(object): diff --git a/Products/ZenEvents/SyslogMsgFilter.py b/Products/ZenEvents/SyslogMsgFilter.py index 5f65d1c7e2..8885a8caac 100644 --- a/Products/ZenEvents/SyslogMsgFilter.py +++ b/Products/ZenEvents/SyslogMsgFilter.py @@ -24,8 +24,9 @@ from zope.interface import implements -from Products.ZenCollector.interfaces import ICollector, IEventService +from Products.ZenCollector.interfaces import ICollector from Products.ZenHub.interfaces import ICollectorEventTransformer, \ + IEventService, \ TRANSFORM_CONTINUE, \ TRANSFORM_DROP from Products.ZenUtils.Utils import unused, zenPath diff --git a/Products/ZenHub/PBDaemon.py b/Products/ZenHub/PBDaemon.py index aa4b0d0e49..0ebbb718f5 100644 --- a/Products/ZenHub/PBDaemon.py +++ b/Products/ZenHub/PBDaemon.py @@ -21,6 +21,8 @@ from twisted.internet import defer, reactor, task from twisted.internet.error import ReactorNotRunning from twisted.spread import pb +from zope.component import provideUtility +from zope.interface import implementer from Products.ZenEvents.ZenEventClasses import ( App_Start, @@ -43,6 +45,7 @@ from .errors import HubDown, translateError from .events import EventClient, EventQueueManager +from .interfaces import IEventService from .localserver import LocalServer, ZenHubStatus from .metricpublisher import publisher from .pinger import PingZenHub @@ -78,6 +81,7 @@ def callRemote(self, *args, **kwargs): return defer.fail(HubDown()) +@implementer(IEventService) class PBDaemon(ZenDaemon, pb.Referenceable): """Base class for services that connect to ZenHub.""" @@ -100,6 +104,8 @@ def __init__( if name is not None: self.name = self.mname = name + provideUtility(self, IEventService) + super(PBDaemon, self).__init__(noopts, keeproot) # Configure/initialize the ZenHub client @@ -124,7 +130,7 @@ def __init__( for evt in self.startEvent, self.stopEvent: evt.update(details) - self._eventqueue = EventQueueManager(self.options, self.log) + self.__eventqueue = EventQueueManager(self.options, self.log) self._metrologyReporter = None self.__publisher = publisher @@ -134,7 +140,7 @@ def __init__( self.__eventclient = EventClient( self.options, - self._eventqueue, + self.__eventqueue, self.generateEvent, lambda: self.getService("EventService"), ) @@ -181,7 +187,7 @@ def services(self): def __record_queued_events_count(self): if self.rrdStats.name: - self.rrdStats.gauge("eventQueueLength", len(self._eventqueue)) + self.rrdStats.gauge("eventQueueLength", len(self.__eventqueue)) def generateEvent(self, event, **kw): """ @@ -269,6 +275,9 @@ def eventService(self): def sendEvents(self, events): return self.__eventclient.sendEvents(events) + def sendHeartbeat(self, event): + self.__eventclient.sendHeartbeat(event) + @defer.inlineCallbacks def sendEvent(self, event, **kw): yield self.__eventclient.sendEvent(event, **kw) diff --git a/Products/ZenHub/events/client.py b/Products/ZenHub/events/client.py index 6865627db1..4ca1a08d9d 100644 --- a/Products/ZenHub/events/client.py +++ b/Products/ZenHub/events/client.py @@ -84,6 +84,9 @@ def sendEvent(self, event, **kw): self.__queue.addEvent(built_event) self.counters["eventCount"] += 1 + def sendHeartbeat(self, event): + self.__queue.addHeartbeatEvent(event) + @defer.inlineCallbacks def _last_push(self, task): yield self._push() diff --git a/Products/ZenHub/events/queue/manager.py b/Products/ZenHub/events/queue/manager.py index ec7d78d388..43054c324f 100644 --- a/Products/ZenHub/events/queue/manager.py +++ b/Products/ZenHub/events/queue/manager.py @@ -12,6 +12,8 @@ from collections import deque from itertools import chain +import six + from metrology import Metrology from metrology.instruments import Gauge from metrology.registry import registry @@ -192,7 +194,7 @@ def chunk_events(): num_heartbeat_events = min( chunk_remaining, len(prev_heartbeat_event_queue) ) - for i in xrange(num_heartbeat_events): + for _ in six.moves.range(num_heartbeat_events): heartbeat_events.append( prev_heartbeat_event_queue.popleft() ) @@ -202,13 +204,13 @@ def chunk_events(): num_perf_events = min( chunk_remaining, len(prev_perf_event_queue) ) - for i in xrange(num_perf_events): + for _ in six.moves.range(num_perf_events): perf_events.append(prev_perf_event_queue.popleft()) chunk_remaining -= num_perf_events events = [] num_events = min(chunk_remaining, len(prev_event_queue)) - for i in xrange(num_events): + for _ in six.moves.range(num_events): events.append(prev_event_queue.popleft()) return heartbeat_events, perf_events, events diff --git a/Products/ZenHub/interfaces.py b/Products/ZenHub/interfaces.py index 7a3740c502..e975401e4b 100644 --- a/Products/ZenHub/interfaces.py +++ b/Products/ZenHub/interfaces.py @@ -198,6 +198,21 @@ def generate(event): """ +class IEventService(Interface): + """ + A service that allows the sending of an event. + """ + + def sendEvents(events): + pass + + def sendEvent(event, **kw): + pass + + def sendHeartbeat(event): + pass + + TRANSFORM_CONTINUE = 0 TRANSFORM_STOP = 1 TRANSFORM_DROP = 2 diff --git a/Products/ZenHub/tests/test_PBDaemon.py b/Products/ZenHub/tests/test_PBDaemon.py index 20b7a916ff..b52045751d 100644 --- a/Products/ZenHub/tests/test_PBDaemon.py +++ b/Products/ZenHub/tests/test_PBDaemon.py @@ -9,7 +9,6 @@ from Products.ZenHub.PBDaemon import ( collections, defer, - # EventQueueManager, PBDaemon, publisher, ) @@ -91,7 +90,6 @@ def test___init__( t.assertEqual(pbd.rrdStats, DaemonStats.return_value) t.assertEqual(pbd.lastStats, 0) t.assertEqual(pbd.services, _getZenHubClient.return_value.services) - t.assertEqual(pbd._eventqueue, EventQueueManager.return_value) t.assertEqual(pbd.startEvent, startEvent.copy()) t.assertEqual(pbd.stopEvent, stopEvent.copy()) @@ -198,7 +196,7 @@ def setUp(t): ] for target in patches: - patcher = patch("{src}.{}".format(target, **PATH), autospec=True) + patcher = patch("{src}.{}".format(target, **PATH), spec=True) setattr(t, target, patcher.start()) t.addCleanup(patcher.stop) From 08a03e712224d20e7faef7f15dc89e536c9dec35 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Thu, 26 Sep 2024 16:03:27 -0500 Subject: [PATCH 4/4] Defer creating the event client and queue. The event queue initializer looks up event transformers too early so defer creating the queue until after connecting to ZenHub. ZEN-35073 --- Products/ZenEvents/zentrap/app.py | 3 +- Products/ZenEvents/zentrap/filterspec.py | 34 +- Products/ZenEvents/zentrap/handlers.py | 7 +- Products/ZenEvents/zentrap/receiver.py | 2 +- .../zentrap/tests/test_filterspec.py | 436 +++++++++++------- Products/ZenEvents/zentrap/trapfilter.py | 21 +- Products/ZenHub/PBDaemon.py | 28 +- Products/ZenHub/tests/test_PBDaemon.py | 5 +- 8 files changed, 326 insertions(+), 210 deletions(-) diff --git a/Products/ZenEvents/zentrap/app.py b/Products/ZenEvents/zentrap/app.py index f20ad7821d..a28d54a112 100644 --- a/Products/ZenEvents/zentrap/app.py +++ b/Products/ZenEvents/zentrap/app.py @@ -57,7 +57,7 @@ class TrapDaemon(PBDaemon): def __init__(self, *args, **kwargs): super(TrapDaemon, self).__init__(*args, **kwargs) - self.configCycleInterval = 20 * 60 # seconds + self.configCycleInterval = 2 * 60 # seconds self.cycleInterval = 5 * 60 # seconds self.__lastCounterEventTime = time.time() @@ -233,7 +233,6 @@ def _start_receiver(self): reactor.addSystemEventTrigger( "before", "shutdown", self._receiver.stop ) - reactor.addSystemEventTrigger( "after", "shutdown", self._displayStatistics ) diff --git a/Products/ZenEvents/zentrap/filterspec.py b/Products/ZenEvents/zentrap/filterspec.py index 038b54e035..4b7172d3ba 100644 --- a/Products/ZenEvents/zentrap/filterspec.py +++ b/Products/ZenEvents/zentrap/filterspec.py @@ -100,7 +100,7 @@ def update_from_string(self, trapFilters, reset=True): self._filtersDefined = 0 != numFiltersDefined if self._filtersDefined: log.debug( - "Finished reading filter configuration. Lines parsed:%s, " + "finished reading filter configuration. Lines parsed:%s, " "Filters defined:%s [v1Traps:%d, v1Filters:%d, " "v2Filters:%d]", lineNumber, @@ -110,13 +110,13 @@ def update_from_string(self, trapFilters, reset=True): len(self._v2Filters), ) else: - log.warn("No zentrap filters defined.") + log.warn("no zentrap filters defined.") return events def _reset(self): - self._v1Traps = {} - self._v1Filters = {} - self._v2Filters = {} + self._v1Traps.clear() + self._v1Filters.clear() + self._v2Filters.clear() self._filtersDefined = False def _parseFilterDefinition(self, line, lineNumber): @@ -165,7 +165,7 @@ def _parseFilterDefinition(self, line, lineNumber): return None except Exception: errorMessage = ( - "Could not compile collector expression {!r} on " + "could not compile collector expression {!r} on " "line {}".format(collectorRegex, lineNumber) ) log.error(errorMessage) @@ -439,13 +439,14 @@ def __init__( self.genericTrap = genericTrap def __eq__(self, other): - if isinstance(other, GenericTrapFilterDefinition): - return self.genericTrap == other.genericTrap - else: - return False + if not isinstance(other, GenericTrapFilterDefinition): + return NotImplemented + return self.genericTrap == other.genericTrap def __ne__(self, other): - return not self.__eq__(other) + if not isinstance(other, GenericTrapFilterDefinition): + return NotImplemented + return self.genericTrap != other.genericTrap def __hash__(self): return hash(self.genericTrap) @@ -462,13 +463,14 @@ def levels(self): return countOidLevels(self.oid) def __eq__(self, other): - if isinstance(other, OIDBasedFilterDefinition): - return self.oid == other.oid - else: - return False + if not isinstance(other, OIDBasedFilterDefinition): + return NotImplemented + return self.oid == other.oid def __ne__(self, other): - return not self.__eq__(other) + if not isinstance(other, OIDBasedFilterDefinition): + return NotImplemented + return self.oid != other.oid def __hash__(self): return hash(self.oid) diff --git a/Products/ZenEvents/zentrap/handlers.py b/Products/ZenEvents/zentrap/handlers.py index 212db876f0..bb2212a1af 100644 --- a/Products/ZenEvents/zentrap/handlers.py +++ b/Products/ZenEvents/zentrap/handlers.py @@ -86,16 +86,15 @@ def __call__(self, addr, pdu, starttime): ) result["zenoss.trap_source_ip"] = addr[0] + community = self.getCommunity(pdu) + self.sendTrapEvent(result, community, eventType, starttime) log.debug( - "asyncHandleTrap: eventType=%s oid=%s snmpVersion=%s", + "handled trap event-type=%s oid=%s snmp-version=%s", eventType, result["oid"], result["snmpVersion"], ) - community = self.getCommunity(pdu) - self.sendTrapEvent(result, community, eventType, starttime) - def sendTrapEvent(self, result, community, eventType, starttime): summary = "snmp trap %s" % eventType log.debug(summary) diff --git a/Products/ZenEvents/zentrap/receiver.py b/Products/ZenEvents/zentrap/receiver.py index 3b1130d577..e3bdc907ca 100644 --- a/Products/ZenEvents/zentrap/receiver.py +++ b/Products/ZenEvents/zentrap/receiver.py @@ -25,7 +25,7 @@ sockaddr_in6, ) -log = logging.getLogger("zen.zentrap.server") +log = logging.getLogger("zen.zentrap.receiver") class Receiver(object): diff --git a/Products/ZenEvents/zentrap/tests/test_filterspec.py b/Products/ZenEvents/zentrap/tests/test_filterspec.py index db472a894f..a87af606e4 100644 --- a/Products/ZenEvents/zentrap/tests/test_filterspec.py +++ b/Products/ZenEvents/zentrap/tests/test_filterspec.py @@ -7,6 +7,8 @@ # ############################################################################## +from __future__ import print_function + # runtests -v -t unit Products.ZenEvents -m testTrapFilter import logging @@ -25,74 +27,74 @@ class OIDBasedFilterDefinitionTest(TestCase): def testEQByOID(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") - self.assert_(base1 == base2) + self.assertEqual(base1, base2) def testEQByOIDFails(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "include", "5.4.3.2.1") - self.assert_(base1 != base2) + self.assertNotEqual(base1, base2) def testEQByOIDIgnoresAction(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "exclude", "1.2.3.4.5") - self.assert_(base1 == base2) + self.assertEqual(base1, base2) def testEQByOIDFailsForDifferentClass(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = BaseFilterDefinition(0, "include") - self.assert_(base1 != base2) + self.assertNotEqual(base1, base2) def testHash(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") - self.assert_(base1.__hash__() == base2.__hash__()) + self.assertEqual(hash(base1), hash(base2)) def testHashFails(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "include", "5.4.3.2.1") - self.assert_(base1.__hash__() != base2.__hash__()) + self.assertNotEqual(hash(base1), hash(base2)) def testHashIgnoresAction(self): base1 = OIDBasedFilterDefinition(0, "include", "1.2.3.4.5") base2 = OIDBasedFilterDefinition(0, "exclude", "1.2.3.4.5") - self.assert_(base1.__hash__() == base2.__hash__()) + self.assertEqual(hash(base1), hash(base2)) class GenericTrapFilterDefinitionTest(TestCase): def testEQByOID(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "include", "1") - self.assert_(base1 == base2) + self.assertEqual(base1, base2) def testEQByOIDFails(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "include", "5") - self.assert_(base1 != base2) + self.assertNotEqual(base1, base2) def testEQByOIDIgnoresAction(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "exclude", "1") - self.assert_(base1 == base2) + self.assertEqual(base1, base2) def testEQByOIDFailsForDifferentClass(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = BaseFilterDefinition(0, "include") - self.assert_(base1 != base2) + self.assertNotEqual(base1, base2) def testHash(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "include", "1") - self.assertEquals(base1.__hash__(), base2.__hash__()) + self.assertEqual(hash(base1), hash(base2)) def testHashFails(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "include", "2") - self.assertNotEquals(base1.__hash__(), base2.__hash__()) + self.assertNotEqual(hash(base1), hash(base2)) def testHashIgnoresAction(self): base1 = GenericTrapFilterDefinition(0, "include", "1") base2 = GenericTrapFilterDefinition(0, "exclude", "1") - self.assert_(base1.__hash__() == base2.__hash__()) + self.assertEqual(hash(base1), hash(base2)) class FilterSpecificationTest(TestCase): @@ -105,99 +107,99 @@ def tearDown(t): def testValidateOIDForGlob(t): results = t.spec._validateOID("*") - t.assertEquals(results, None) + t.assertEqual(results, None) results = t.spec._validateOID("1.2.*") - t.assertEquals(results, None) + t.assertEqual(results, None) def testValidateOIDFailsForEmptyString(t): results = t.spec._validateOID("") - t.assertEquals(results, "Empty OID is invalid") + t.assertEqual(results, "Empty OID is invalid") def testValidateOIDFailsForSimpleNumber(t): results = t.spec._validateOID("123") - t.assertEquals(results, "At least one '.' required") + t.assertEqual(results, "At least one '.' required") def testValidateOIDFailsForInvalidChars(t): results = t.spec._validateOID("1.2.3-5.*") - t.assertEquals( + t.assertEqual( results, "Invalid character found; only digits, '.' and '*' allowed", ) def testValidateOIDFailsForDoubleDots(t): results = t.spec._validateOID("1.2..3") - t.assertEquals(results, "Consecutive '.'s not allowed") + t.assertEqual(results, "Consecutive '.'s not allowed") def testValidateOIDFailsForInvalidGlobbing(t): results = t.spec._validateOID("1.2.3.*.5.*") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("1.*.5") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("1.5*") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("*.") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("*.1") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("*.*") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("5*") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID("*5") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) results = t.spec._validateOID(".*") - t.assertEquals( + t.assertEqual( results, "When using '*', only a single '*' at the end of OID is allowed", ) def testParseFilterDefinitionForEmptyLine(t): results = t.spec._parseFilterDefinition("", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, "Incomplete filter definition") + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, "Incomplete filter definition") def testParseFilterDefinitionForIncompleteLine(t): results = t.spec._parseFilterDefinition("a b", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, "Incomplete filter definition") + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, "Incomplete filter definition") def testParseFilterDefinitionForInvalidAction(t): results = t.spec._parseFilterDefinition("invalid V1 ignored", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals( + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual( results, "Invalid action 'invalid'; the only valid actions are " "'include' or 'exclude'", @@ -205,8 +207,8 @@ def testParseFilterDefinitionForInvalidAction(t): def testParseFilterDefinitionForInvalidVersion(t): results = t.spec._parseFilterDefinition("include V4 ignored", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals( + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual( results, "Invalid SNMP version 'V4'; the only valid versions are " "'v1' or 'v2' or 'v3'", @@ -214,170 +216,170 @@ def testParseFilterDefinitionForInvalidVersion(t): def testParseFilterDefinitionForInvalidV1Definition(t): results = t.spec._parseFilterDefinition("include V1 .", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") def testParseFilterDefinitionForCaseInsensitiveDefinition(t): results = t.spec._parseFilterDefinition("InClude v1 3", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, None) + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, None) def testParseFilterDefinitionForValidV1Definition(t): results = t.spec._parseFilterDefinition("include V1 3", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, None) + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, None) def testParseFilterDefinitionForInvalidV2Definition(t): results = t.spec._parseFilterDefinition("include V2 .", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") def testParseFilterDefinitionForValidV2Definition(t): results = t.spec._parseFilterDefinition("include V2 .1.3.6.1.4.*", 99) - # t.assertEquals(t.spec._eventService.sendEvent.called, False) - t.assertEquals(results, None) + # t.assertEqual(t.spec._eventService.sendEvent.called, False) + t.assertEqual(results, None) def testParseFilterDefinitionForInvalidV3Definition(t): results = t.spec._parseFilterDefinition("include V3 .", 99) - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") def testParseFilterDefinitionForValidV3Definition(t): results = t.spec._parseFilterDefinition("include V3 .1.3.6.1.4.*", 99) - t.assertEquals(results, None) + t.assertEqual(results, None) def testParseV1FilterDefinitionForGenericTrap(t): results = t.spec._parseV1FilterDefinition(99, "include", ["0"], ".*") - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 1) - t.assertEquals(len(t.spec._v1Filters), 0) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 1) + t.assertEqual(len(t.spec._v1Filters), 0) + t.assertEqual(len(t.spec._v2Filters), 0) genericTrapDefinition = t.spec._v1Traps["0"] t.assertIsNotNone(genericTrapDefinition) - t.assertEquals(genericTrapDefinition.lineNumber, 99) - t.assertEquals(genericTrapDefinition.action, "include") - t.assertEquals(genericTrapDefinition.genericTrap, "0") + t.assertEqual(genericTrapDefinition.lineNumber, 99) + t.assertEqual(genericTrapDefinition.action, "include") + t.assertEqual(genericTrapDefinition.genericTrap, "0") # Now add another to make sure we can parse more than one results = t.spec._parseV1FilterDefinition(100, "exclude", ["5"], ".*") - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 2) - t.assertEquals(len(t.spec._v1Filters), 0) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 2) + t.assertEqual(len(t.spec._v1Filters), 0) + t.assertEqual(len(t.spec._v2Filters), 0) genericTrapDefinition = t.spec._v1Traps["5"] t.assertIsNotNone(genericTrapDefinition) - t.assertEquals(genericTrapDefinition.lineNumber, 100) - t.assertEquals(genericTrapDefinition.action, "exclude") - t.assertEquals(genericTrapDefinition.genericTrap, "5") + t.assertEqual(genericTrapDefinition.lineNumber, 100) + t.assertEqual(genericTrapDefinition.action, "exclude") + t.assertEqual(genericTrapDefinition.genericTrap, "5") def testParseV1FilterDefinitionEnterpriseSpecificTrap(t): results = t.spec._parseV1FilterDefinition( 99, "include", ["1.2.3.*"], ".*" ) - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 1) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 1) + t.assertEqual(len(t.spec._v2Filters), 0) oidLevels = 4 mapByLevel = t.spec._v1Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 1) + t.assertEqual(len(mapByLevel), 1) filterDef = mapByLevel["1.2.3.*"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 99) - t.assertEquals(filterDef.action, "include") - t.assertEquals(filterDef.oid, "1.2.3.*") - t.assertEquals(filterDef.specificTrap, None) + t.assertEqual(filterDef.lineNumber, 99) + t.assertEqual(filterDef.action, "include") + t.assertEqual(filterDef.oid, "1.2.3.*") + t.assertEqual(filterDef.specificTrap, None) # Add another 4-level OID results = t.spec._parseV1FilterDefinition( 100, "exclude", ["1.2.3.4", "25"], ".*" ) - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 1) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 1) + t.assertEqual(len(t.spec._v2Filters), 0) mapByLevel = t.spec._v1Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 2) + t.assertEqual(len(mapByLevel), 2) filterDef = mapByLevel["1.2.3.4-25"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 100) - t.assertEquals(filterDef.action, "exclude") - t.assertEquals(filterDef.oid, "1.2.3.4") - t.assertEquals(filterDef.specificTrap, "25") + t.assertEqual(filterDef.lineNumber, 100) + t.assertEqual(filterDef.action, "exclude") + t.assertEqual(filterDef.oid, "1.2.3.4") + t.assertEqual(filterDef.specificTrap, "25") # Add a different specific trap for the same OID results = t.spec._parseV1FilterDefinition( 101, "exclude", ["1.2.3.4", "99"], ".*" ) - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 1) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 1) + t.assertEqual(len(t.spec._v2Filters), 0) mapByLevel = t.spec._v1Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 3) + t.assertEqual(len(mapByLevel), 3) filterDef = mapByLevel["1.2.3.4-99"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 101) - t.assertEquals(filterDef.action, "exclude") - t.assertEquals(filterDef.oid, "1.2.3.4") - t.assertEquals(filterDef.specificTrap, "99") + t.assertEqual(filterDef.lineNumber, 101) + t.assertEqual(filterDef.action, "exclude") + t.assertEqual(filterDef.oid, "1.2.3.4") + t.assertEqual(filterDef.specificTrap, "99") # Add another single-level OID results = t.spec._parseV1FilterDefinition(101, "exclude", ["*"], ".*") - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 2) - t.assertEquals(len(t.spec._v2Filters), 0) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 2) + t.assertEqual(len(t.spec._v2Filters), 0) oidLevels = 1 mapByLevel = t.spec._v1Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 1) + t.assertEqual(len(mapByLevel), 1) filterDef = mapByLevel["*"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 101) - t.assertEquals(filterDef.action, "exclude") - t.assertEquals(filterDef.oid, "*") - t.assertEquals(filterDef.specificTrap, None) + t.assertEqual(filterDef.lineNumber, 101) + t.assertEqual(filterDef.action, "exclude") + t.assertEqual(filterDef.oid, "*") + t.assertEqual(filterDef.specificTrap, None) def testParseV1FilterDefinitionFailsForTooManyArgs(t): results = t.spec._parseV1FilterDefinition( 99, "include", ["0", "1", "2"], ".*" ) - t.assertEquals( + t.assertEqual( results, "Too many fields found; at most 4 fields allowed for V1 filters", ) def testParseV1FilterDefinitionFailsForEmptyOID(t): results = t.spec._parseV1FilterDefinition(99, "include", [], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV1FilterDefinition(99, "include", [""], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV1FilterDefinition(99, "include", ["."], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV1FilterDefinition(99, "include", ["..."], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") def testParseV1FilterDefinitionFailsForInvalidOID(t): results = t.spec._parseV1FilterDefinition( 99, "include", ["invalidOID"], ".*" ) - t.assertEquals( + t.assertEqual( results, "'invalidOID' is not a valid OID: Invalid character found; " "only digits, '.' and '*' allowed", @@ -385,24 +387,24 @@ def testParseV1FilterDefinitionFailsForInvalidOID(t): def testParseV1FilterDefinitionFailsForInvalidTrap(t): results = t.spec._parseV1FilterDefinition(99, "include", ["a"], ".*") - t.assertEquals(results, "Invalid generic trap 'a'; must be one of 0-5") + t.assertEqual(results, "Invalid generic trap 'a'; must be one of 0-5") results = t.spec._parseV1FilterDefinition(99, "include", ["6"], ".*") - t.assertEquals(results, "Invalid generic trap '6'; must be one of 0-5") + t.assertEqual(results, "Invalid generic trap '6'; must be one of 0-5") def testParseV1FilterDefinitionFailsForConflictingTrap(t): results = t.spec._parseV1FilterDefinition(99, "include", ["1"], ".*") - t.assertEquals(results, None) + t.assertEqual(results, None) results = t.spec._parseV1FilterDefinition(100, "include", ["1"], ".*") - t.assertEquals( + t.assertEqual( results, "Generic trap '1' conflicts with previous definition at line 99", ) # Verify we find a conflict for generic traps where the action differs results = t.spec._parseV1FilterDefinition(100, "exclude", ["1"], ".*") - t.assertEquals( + t.assertEqual( results, "Generic trap '1' conflicts with previous definition at line 99", ) @@ -411,12 +413,12 @@ def testParseV1FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV1FilterDefinition( 99, "include", [".1.3.6.1.4.5", "2"], ".*" ) - t.assertEquals(results, None) + t.assertEqual(results, None) results = t.spec._parseV1FilterDefinition( 100, "include", [".1.3.6.1.4.5", "2"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.5' conflicts with previous definition at line 99", ) @@ -425,7 +427,7 @@ def testParseV1FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV1FilterDefinition( 100, "exclude", [".1.3.6.1.4.5", "2"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.5' conflicts with previous definition at line 99", ) @@ -433,13 +435,13 @@ def testParseV1FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV1FilterDefinition( 101, "include", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals(results, None) + t.assertEqual(results, None) # Verify we find a conflict for glob-based OIDs results = t.spec._parseV1FilterDefinition( 102, "include", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.*' conflicts with previous definition at line 101", ) @@ -449,7 +451,7 @@ def testParseV1FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV1FilterDefinition( 102, "exclude", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.*' conflicts with previous definition at line 101", ) @@ -458,13 +460,13 @@ def testParseV1FilterDefinitionFailsForEnterpriseSpecificGlob(t): results = t.spec._parseV1FilterDefinition( 99, "include", [".1.3.6.1.4.5.*", "23"], ".*" ) - t.assertEquals(results, "Specific trap not allowed with globbed OID") + t.assertEqual(results, "Specific trap not allowed with globbed OID") def testParseV1FilterDefinitionFailsForInvalidEnterpriseSpecificTrap(t): results = t.spec._parseV1FilterDefinition( 99, "include", [".1.3.6.1.4.5", "abc"], ".*" ) - t.assertEquals( + t.assertEqual( results, "Specific trap 'abc' invalid; must be non-negative integer", ) @@ -472,7 +474,7 @@ def testParseV1FilterDefinitionFailsForInvalidEnterpriseSpecificTrap(t): results = t.spec._parseV1FilterDefinition( 99, "include", [".1.3.6.1.4.5", "-1"], ".*" ) - t.assertEquals( + t.assertEqual( results, "Specific trap '-1' invalid; must be non-negative integer" ) @@ -480,92 +482,92 @@ def testParseV1FilterDefinitionForSpecificOid(t): results = t.spec._parseV1FilterDefinition( 99, "include", [".1.3.6.1.4.5"], ".*" ) - t.assertEquals(results, None) + t.assertEqual(results, None) def testParseV2FilterDefinition(t): results = t.spec._parseV2FilterDefinition( 99, "include", ["1.2.3.*"], ".*" ) - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 0) - t.assertEquals(len(t.spec._v2Filters), 1) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 0) + t.assertEqual(len(t.spec._v2Filters), 1) oidLevels = 4 mapByLevel = t.spec._v2Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 1) + t.assertEqual(len(mapByLevel), 1) filterDef = mapByLevel["1.2.3.*"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 99) - t.assertEquals(filterDef.action, "include") - t.assertEquals(filterDef.oid, "1.2.3.*") + t.assertEqual(filterDef.lineNumber, 99) + t.assertEqual(filterDef.action, "include") + t.assertEqual(filterDef.oid, "1.2.3.*") # Add another 4-level OID results = t.spec._parseV2FilterDefinition( 100, "exclude", ["1.2.3.4"], ".*" ) - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 0) - t.assertEquals(len(t.spec._v2Filters), 1) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 0) + t.assertEqual(len(t.spec._v2Filters), 1) mapByLevel = t.spec._v2Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 2) + t.assertEqual(len(mapByLevel), 2) filterDef = mapByLevel["1.2.3.4"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 100) - t.assertEquals(filterDef.action, "exclude") - t.assertEquals(filterDef.oid, "1.2.3.4") + t.assertEqual(filterDef.lineNumber, 100) + t.assertEqual(filterDef.action, "exclude") + t.assertEqual(filterDef.oid, "1.2.3.4") # Add another single-level OID results = t.spec._parseV2FilterDefinition(101, "exclude", ["*"], ".*") - t.assertEquals(results, None) - t.assertEquals(len(t.spec._v1Traps), 0) - t.assertEquals(len(t.spec._v1Filters), 0) - t.assertEquals(len(t.spec._v2Filters), 2) + t.assertEqual(results, None) + t.assertEqual(len(t.spec._v1Traps), 0) + t.assertEqual(len(t.spec._v1Filters), 0) + t.assertEqual(len(t.spec._v2Filters), 2) oidLevels = 1 mapByLevel = t.spec._v2Filters[oidLevels] t.assertIsNotNone(mapByLevel) - t.assertEquals(len(mapByLevel), 1) + t.assertEqual(len(mapByLevel), 1) filterDef = mapByLevel["*"] t.assertIsNotNone(filterDef) - t.assertEquals(filterDef.lineNumber, 101) - t.assertEquals(filterDef.action, "exclude") - t.assertEquals(filterDef.oid, "*") + t.assertEqual(filterDef.lineNumber, 101) + t.assertEqual(filterDef.action, "exclude") + t.assertEqual(filterDef.oid, "*") def testParseV2FilterDefinitionFailsForTooManyArgs(t): results = t.spec._parseV2FilterDefinition( 99, "include", ["0", "1"], ".*" ) - t.assertEquals( + t.assertEqual( results, "Too many fields found; at most 3 fields allowed for V2 filters", ) def testParseV2FilterDefinitionFailsForEmptyOID(t): results = t.spec._parseV2FilterDefinition(99, "include", [], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV2FilterDefinition(99, "include", [""], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV2FilterDefinition(99, "include", ["."], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") results = t.spec._parseV2FilterDefinition(99, "include", ["..."], ".*") - t.assertEquals(results, "'' is not a valid OID: Empty OID is invalid") + t.assertEqual(results, "'' is not a valid OID: Empty OID is invalid") def testParseV2FilterDefinitionFailsForInvalidOID(t): results = t.spec._parseV2FilterDefinition( 99, "include", ["invalidOID"], ".*" ) - t.assertEquals( + t.assertEqual( results, "'invalidOID' is not a valid OID: Invalid character found; " "only digits, '.' and '*' allowed", @@ -575,12 +577,12 @@ def testParseV2FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV2FilterDefinition( 99, "include", [".1.3.6.1.4.5"], ".*" ) - t.assertEquals(results, None) + t.assertEqual(results, None) results = t.spec._parseV2FilterDefinition( 100, "include", [".1.3.6.1.4.5"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.5' conflicts with previous definition at line 99", ) @@ -589,7 +591,7 @@ def testParseV2FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV2FilterDefinition( 100, "exclude", [".1.3.6.1.4.5"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.5' conflicts with previous definition at line 99", ) @@ -597,13 +599,13 @@ def testParseV2FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV2FilterDefinition( 101, "include", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals(results, None) + t.assertEqual(results, None) # Verify we find a conflict for glob-based OIDs results = t.spec._parseV2FilterDefinition( 102, "include", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.*' conflicts with previous definition at line 101", ) @@ -613,17 +615,125 @@ def testParseV2FilterDefinitionFailsForConflictingOID(t): results = t.spec._parseV2FilterDefinition( 102, "exclude", [".1.3.6.1.4.*"], ".*" ) - t.assertEquals( + t.assertEqual( results, "OID '1.3.6.1.4.*' conflicts with previous definition at line 101", ) -def test_suite(): - from unittest import TestSuite, makeSuite +class TestPersistentFilterSpecProperties(TestCase): + """ + Test that the `v1traps`, `v1filters`, and `v2filters` properties are + updated after a call to `update_from_string` with a different config. + """ + + _rules_v1 = "\n".join( + ( + "include v1 0", + "include v1 1", + "include v1 2", + "include v1 3", + "include v1 4", + "include v1 5", + "include v1 *", + "include v2 *", + ) + ) + + _rules_v2 = "\n".join( + ( + "include v1 0", + "include v1 1", + "exclude v1 2", + "include v1 3", + "include v1 4", + "include v1 5", + "include v1 *", + "include v2 *", + "exclude v1 1.3.6.1.4.1.9.9.41.2.0.*", + "exclude v2 1.3.6.1.4.1.9.9.43.1.3.*", + ) + ) - suite = TestSuite() - suite.addTest(makeSuite(OIDBasedFilterDefinitionTest)) - suite.addTest(makeSuite(GenericTrapFilterDefinitionTest)) - suite.addTest(makeSuite(FilterSpecificationTest)) - return suite + def setUp(t): + t.spec = FilterSpecification("localhost") + t.spec.update_from_string(t._rules_v1) + + def test_v1trap_property(t): + reference = t.spec.v1traps + original = dict(reference) + t.spec.update_from_string(t._rules_v2) + + t.assertEqual(len(original), len(t.spec.v1traps)) + t.assertEqual(len(reference), len(t.spec.v1traps)) + t.assertFalse( + all( + (kr == kl) and _eq_v1generic(vr, vl) + for (kr, vr), (kl, vl) in zip( + sorted(original.items()), sorted(t.spec.v1traps.items()) + ) + ) + ) + t.assertTrue( + all( + (kr == kl) and _eq_v1generic(vr, vl) + for (kr, vr), (kl, vl) in zip( + sorted(reference.items()), sorted(t.spec.v1traps.items()) + ) + ) + ) + + def test_v1filters_property(t): + reference = t.spec.v1filters + original = dict(reference) + t.spec.update_from_string(t._rules_v2) + + t.assertNotEqual(len(original), len(t.spec.v1filters)) + t.assertEqual(len(reference), len(t.spec.v1filters)) + t.assertTrue( + all( + (kr == kl) and _eq_v1enterprise(vr, vl) + for (kr, vr), (kl, vl) in zip( + sorted(reference.items()), sorted(t.spec.v1filters.items()) + ) + ) + ) + + def test_v2filters_property(t): + reference = t.spec.v2filters + original = dict(reference) + t.spec.update_from_string(t._rules_v2) + + t.assertNotEqual(len(original), len(t.spec.v2filters)) + t.assertEqual(len(reference), len(t.spec.v2filters)) + t.assertTrue( + all( + (kr == kl) and _eq_v2(vr, vl) + for (kr, vr), (kl, vl) in zip( + sorted(reference.items()), sorted(t.spec.v2filters.items()) + ) + ) + ) + + +def _eq_v1generic(lv, rv): + return (lv.genericTrap == rv.genericTrap) and (lv.action == rv.action) + + +def _eq_v1enterprise(lv, rv): + return all( + (k1 == k2) + and (v1.action == v2.action) + and (v1.oid == v2.oid) + and (v1.specificTrap == v2.specificTrap) + for (k1, v1), (k2, v2) in zip(sorted(lv.items()), sorted(rv.items())) + ) + + +def _eq_v2(lv, rv): + return all( + (k1 == k2) + and (v1.action == v2.action) + and (v1.oid == v2.oid) + for (k1, v1), (k2, v2) in zip(sorted(lv.items()), sorted(rv.items())) + ) diff --git a/Products/ZenEvents/zentrap/trapfilter.py b/Products/ZenEvents/zentrap/trapfilter.py index e657b6f76a..28896590bb 100644 --- a/Products/ZenEvents/zentrap/trapfilter.py +++ b/Products/ZenEvents/zentrap/trapfilter.py @@ -119,6 +119,7 @@ def _dropEvent(self, event): (tf for tf in self._filters if tf.is_valid(event)), None ) if trapfilter: + log.debug("using trap filter %r", trapfilter) return trapfilter(event) log.error("dropping unknown trap event=%r", event) return True @@ -179,7 +180,7 @@ def __call__(self, event): ) return True - result = _check_definitions( + return _check_definitions( ( getter() for getter in ( @@ -190,9 +191,6 @@ def __call__(self, event): ) ) ) - if result: - log.debug("drop specific v1 trap trap=%s", event) - return result def _getSpecificTrapDefinition(self, event, enterpriseOID): specificTrap = event.get("snmpV1SpecificTrap", None) @@ -201,14 +199,14 @@ def _getSpecificTrapDefinition(self, event, enterpriseOID): key = "".join([enterpriseOID, "-", str(specificTrap)]) definition = _findFilterByLevel(key, self._definitions) if definition: - log.debug("matched definition %s", definition) + log.debug("matched [specific-trap] definition %s", definition) return definition def _getWildCardDefinition(self, enterpriseOID): key = "".join([enterpriseOID, "-", "*"]) definition = _findFilterByLevel(key, self._definitions) if definition: - log.debug("matched definition %s", definition) + log.debug("matched [wildcard] definition %s", definition) return definition def _getGlobMatchDefinition(self, enterpriseOID): @@ -216,7 +214,7 @@ def _getGlobMatchDefinition(self, enterpriseOID): enterpriseOID, self._definitions ) if definition: - log.debug("matched definition %s", definition) + log.debug("matched [glob] definition %s", definition) return definition @@ -226,7 +224,7 @@ def is_valid(self, event): def __call__(self, event): oid = event["oid"] - result = _check_definitions( + return _check_definitions( ( getter() for getter in ( @@ -236,21 +234,18 @@ def __call__(self, event): ) ) ) - if result: - log.debug("drop v2 trap trap=%s", event) - return result def _getExactMatchDefinition(self, oid): # First, try an exact match on the OID definition = _findFilterByLevel(oid, self._definitions) if definition: - log.debug("matched definition %s", definition) + log.debug("matched [exact] definition %s", definition) return definition def _getGlobMatchDefinition(self, oid): definition = _findClosestGlobbedFilter(oid, self._definitions) if definition: - log.debug("matched definition %s", definition) + log.debug("matched [glob] definition %s", definition) return definition diff --git a/Products/ZenHub/PBDaemon.py b/Products/ZenHub/PBDaemon.py index 0ebbb718f5..e3cc239d5a 100644 --- a/Products/ZenHub/PBDaemon.py +++ b/Products/ZenHub/PBDaemon.py @@ -130,7 +130,6 @@ def __init__( for evt in self.startEvent, self.stopEvent: evt.update(details) - self.__eventqueue = EventQueueManager(self.options, self.log) self._metrologyReporter = None self.__publisher = publisher @@ -138,12 +137,8 @@ def __init__( self.__metric_writer = None self.__derivative_tracker = None - self.__eventclient = EventClient( - self.options, - self.__eventqueue, - self.generateEvent, - lambda: self.getService("EventService"), - ) + self.__eventqueue = None + self.__eventclient = None self.__recordQueuedEventsCountLoop = task.LoopingCall( self.__record_queued_events_count ) @@ -186,7 +181,7 @@ def services(self): return self.__zhclient.services def __record_queued_events_count(self): - if self.rrdStats.name: + if self.rrdStats.name and self.__eventqueue is not None: self.rrdStats.gauge("eventQueueLength", len(self.__eventqueue)) def generateEvent(self, event, **kw): @@ -273,13 +268,19 @@ def eventService(self): return self.getServiceNow("EventService") def sendEvents(self, events): + if self.__eventclient is None: + return return self.__eventclient.sendEvents(events) def sendHeartbeat(self, event): + if self.__eventclient is None: + return self.__eventclient.sendHeartbeat(event) @defer.inlineCallbacks def sendEvent(self, event, **kw): + if self.__eventclient is None: + return yield self.__eventclient.sendEvent(event, **kw) def getServiceNow(self, svcName): @@ -427,16 +428,23 @@ def _started(self): @defer.inlineCallbacks def _stop(self): - if self.options.cycle: + if self.__eventclient is not None: self.__eventclient.sendEvent(self.stopEvent) yield self.__eventclient.stop() self.log.debug("stopped event client") yield self.__zhclient.stop() def _setup_event_client(self): + self.__eventqueue = EventQueueManager(self.options, self.log) + self.__eventclient = EventClient( + self.options, + self.__eventqueue, + self.generateEvent, + lambda: self.getService("EventService"), + ) self.__eventclient.start() - self.__recordQueuedEventsCountLoop.start(2.0, now=False) self.__eventclient.sendEvent(self.startEvent) + self.__recordQueuedEventsCountLoop.start(2.0, now=False) self.log.info("started event client") def _setup_stats_recording(self): diff --git a/Products/ZenHub/tests/test_PBDaemon.py b/Products/ZenHub/tests/test_PBDaemon.py index b52045751d..6ff97d0199 100644 --- a/Products/ZenHub/tests/test_PBDaemon.py +++ b/Products/ZenHub/tests/test_PBDaemon.py @@ -82,7 +82,7 @@ def test___init__( ls = _getLocalServer.return_value ls.add_resource.assert_called_once_with("zenhub", ANY) - EventQueueManager.assert_called_with(PBDaemon.options, PBDaemon.log) + EventQueueManager.assert_not_called() # Check lots of attributes, should verify that they are needed t.assertEqual(pbd._thresholds, Thresholds.return_value) @@ -415,6 +415,7 @@ def test_stop(t): def test_sendEvents(t): ec = t.EventClient.return_value + t.pbd._setup_event_client() events = [{"name": "evt_a"}, {"name": "evt_b"}] d = t.pbd.sendEvents(events) @@ -426,6 +427,7 @@ def test_sendEvent(t): ec = t.EventClient.return_value sendEvent = Mock(name="sendEvent") ec.sendEvent = sendEvent + t.pbd._setup_event_client() event = {"name": "event"} d = t.pbd.sendEvent(event, newkey="newkey") @@ -458,6 +460,7 @@ def test_postStatisticsImpl(t): def test_postStatistics(t): ec = t.EventClient.return_value ec.counters = collections.Counter() + t.pbd._setup_event_client() # sets rrdStats, then calls postStatisticsImpl t.pbd.rrdStats = Mock(name="rrdStats", spec_set=["counter"]) ctrs = {"c1": 3, "c2": 5}