diff --git a/Products/ZenCollector/configcache/tasks/deviceconfig.py b/Products/ZenCollector/configcache/tasks/deviceconfig.py index f8f8812420..5a7e8d6883 100644 --- a/Products/ZenCollector/configcache/tasks/deviceconfig.py +++ b/Products/ZenCollector/configcache/tasks/deviceconfig.py @@ -84,7 +84,10 @@ def buildDeviceConfig( key.service, submitted, ) - store.clear_status(key) + # Speculatively delete the config because this device may have been + # re-identified under a new ID so the config keyed by the old ID + # should be removed. + _delete_config(key, store, log) return if _job_is_old(status, submitted, started, device, log): @@ -135,6 +138,12 @@ def buildDeviceConfig( # get a new store; the prior store's connection may have gone stale. store = _getStore() if config is None: + log.info( + "no configuration built device=%s collector=%s service=%s", + key.device, + key.monitor, + key.service, + ) _delete_config(key, store, log) else: uid = device.getPrimaryId() @@ -237,12 +246,6 @@ def _should_update_status( def _delete_config(key, store, log): - log.info( - "no configuration built device=%s collector=%s service=%s", - key.device, - key.monitor, - key.service, - ) if key in store: store.remove(key) log.info( diff --git a/Products/ZenCollector/configcache/tests/test_build_device_config.py b/Products/ZenCollector/configcache/tests/test_build_device_config.py index baa6ebfdc1..6c757d3752 100644 --- a/Products/ZenCollector/configcache/tests/test_build_device_config.py +++ b/Products/ZenCollector/configcache/tests/test_build_device_config.py @@ -14,8 +14,9 @@ from unittest import TestCase from Products.Jobber.tests.utils import RedisLayer +from Products.ZenCollector.services.config import DeviceProxy -from ..cache import DeviceKey +from ..cache import DeviceKey, DeviceRecord from ..cache.storage import DeviceConfigStore from ..tasks.deviceconfig import buildDeviceConfig @@ -26,7 +27,6 @@ class TestBuildDeviceConfig(TestCase): - layer = RedisLayer def setUp(t): @@ -92,3 +92,41 @@ def test_device_not_found(t, _resolve, _createObject): status = t.store.get_status(key) t.assertIsNone(status) + + @mock.patch("{task}.createObject".format(**PATH), autospec=True) + @mock.patch("{task}.resolve".format(**PATH), autospec=True) + def test_device_reidentified(t, _resolve, _createObject): + # A 're-identified' device will no longer be found in ZODB under its + # old ID, but a config keyed for the old ID will still exist. + monitor = "localhost" + clsname = "Products.ZenHub.services.PingService.PingService" + svcname = clsname.rsplit(".", 1)[0] + proxy = DeviceProxy() + submitted = 123456.34 + record = DeviceRecord.make( + svcname, + monitor, + t.device_name, + t.device_uid, + submitted - 300, + proxy, + ) + key = record.key + t.store.add(record) + + svcclass = mock.Mock() + svc = mock.MagicMock() + dmd = mock.Mock() + log = mock.Mock() + + _createObject.return_value = t.store + _resolve.return_value = svcclass + svcclass.return_value = svc + dmd.Devices.findDeviceByIdExact.return_value = None + + t.store.set_pending((key, submitted)) + + buildDeviceConfig(dmd, log, monitor, t.device_name, clsname, submitted) + + status = t.store.get_status(key) + t.assertIsNone(status)