diff options
author | Lianhao Lu <lianhao.lu@intel.com> | 2015-09-01 16:33:22 +0800 |
---|---|---|
committer | Lianhao Lu <lianhao.lu@intel.com> | 2015-09-02 09:22:04 +0800 |
commit | f9981f3e3e6870c1ce03da92f5b81218e93efb4c (patch) | |
tree | 42e6adb1b578f8ea14b686807857c817ac6511f1 | |
parent | 1520f65f8b1ff480efc52521faaf891b629e68e1 (diff) | |
download | ceilometer-f9981f3e3e6870c1ce03da92f5b81218e93efb4c.tar.gz |
Support to load pollsters extensions at runtime
Make AgentManager to load pollster extensions at runtime. So that a
pollster builder may build multiple pollster extensions to be used by
the AgentManager.
In this way, we don't need to change the existing pollsters interface in
order to support declarative pollster, which means a single pollster
class might gives more than one meters.
All pollsters which implements classmethod interface build_pollsters()
should be registered in the namesapce of ceilometer.builder.poll.xxx, so
that AgentManger can correctly find it and use to dynamically generate
multiple pollsters through the 'builder' logic.
Change-Id: If7f8f80301474e2684ea0f1bbdc69cc202b9ef7e
Implements: blueprint declarative-snmp-metrics
-rw-r--r-- | ceilometer/agent/base.py | 40 | ||||
-rw-r--r-- | ceilometer/tests/unit/agent/test_manager.py | 36 |
2 files changed, 67 insertions, 9 deletions
diff --git a/ceilometer/agent/base.py b/ceilometer/agent/base.py index d3e70205..de3511c5 100644 --- a/ceilometer/agent/base.py +++ b/ceilometer/agent/base.py @@ -227,11 +227,17 @@ class AgentManager(service_base.BaseService): # be passed extensions = (self._extensions('poll', namespace).extensions for namespace in namespaces) + # get the extensions from pollster builder + extensions_fb = (self._extensions_from_builder('poll', namespace) + for namespace in namespaces) if pollster_list: extensions = (moves.filter(_match, exts) for exts in extensions) + extensions_fb = (moves.filter(_match, exts) + for exts in extensions_fb) - self.extensions = list(itertools.chain(*list(extensions))) + self.extensions = list(itertools.chain(*list(extensions))) + list( + itertools.chain(*list(extensions_fb))) self.discovery_manager = self._extensions('discover') self.context = context.RequestContext('admin', 'admin', is_admin=True) @@ -249,10 +255,7 @@ class AgentManager(service_base.BaseService): publisher_id="ceilometer.api") @staticmethod - def _extensions(category, agent_ns=None): - namespace = ('ceilometer.%s.%s' % (category, agent_ns) if agent_ns - else 'ceilometer.%s' % category) - + def _get_ext_mgr(namespace): def _catch_extension_load_error(mgr, ep, exc): # Extension raising ExtensionLoadError can be ignored, # and ignore anything we can't import as a safety measure. @@ -260,10 +263,9 @@ class AgentManager(service_base.BaseService): LOG.error(_("Skip loading extension for %s") % ep.name) return if isinstance(exc, ImportError): - LOG.error( - _("Failed to import extension for %(name)s: %(error)s"), - {'name': ep.name, 'error': exc}, - ) + LOG.error(_("Failed to import extension for %(name)s: " + "%(error)s"), + {'name': ep.name, 'error': exc}) return raise exc @@ -273,6 +275,26 @@ class AgentManager(service_base.BaseService): on_load_failure_callback=_catch_extension_load_error, ) + def _extensions(self, category, agent_ns=None): + namespace = ('ceilometer.%s.%s' % (category, agent_ns) if agent_ns + else 'ceilometer.%s' % category) + return self._get_ext_mgr(namespace) + + def _extensions_from_builder(self, category, agent_ns=None): + ns = ('ceilometer.builder.%s.%s' % (category, agent_ns) if agent_ns + else 'ceilometer.builder.%s' % category) + mgr = self._get_ext_mgr(ns) + + def _build(ext): + return ext.plugin.get_pollsters_extensions() + + # NOTE: this seems a stevedore bug. if no extensions are found, + # map will raise runtimeError which is not documented. + if mgr.names(): + return list(itertools.chain(*mgr.map(_build))) + else: + return [] + def join_partitioning_groups(self): self.groups = set([self.construct_group_id(d.obj.group_id) for d in self.discovery_manager]) diff --git a/ceilometer/tests/unit/agent/test_manager.py b/ceilometer/tests/unit/agent/test_manager.py index 3f61f13d..3ccd191f 100644 --- a/ceilometer/tests/unit/agent/test_manager.py +++ b/ceilometer/tests/unit/agent/test_manager.py @@ -39,6 +39,12 @@ class PollingException(Exception): pass +class TestPollsterBuilder(agentbase.TestPollster): + @classmethod + def build_pollsters(cls): + return [('builder1', cls()), ('builder2', cls())] + + @mock.patch('ceilometer.compute.pollsters.' 'BaseComputePollster.setup_environment', mock.Mock(return_value=None)) @@ -126,6 +132,36 @@ class TestManager(base.BaseTestCase): pollster_list=['disk.*']) manager.cfg.CONF.reset() + def test_builder(self): + @staticmethod + def fake_get_ext_mgr(namespace): + if 'builder' in namespace: + return extension.ExtensionManager.make_test_instance( + [ + extension.Extension('builder', + None, + TestPollsterBuilder, + None), + ] + ) + else: + return extension.ExtensionManager.make_test_instance( + [ + extension.Extension('test', + None, + None, + agentbase.TestPollster()), + ] + ) + + with mock.patch.object(manager.AgentManager, '_get_ext_mgr', + new=fake_get_ext_mgr): + mgr = manager.AgentManager(namespaces=['central']) + self.assertEqual(3, len(mgr.extensions)) + for ext in mgr.extensions: + self.assertIn(ext.name, ['builder1', 'builder2', 'test']) + self.assertIsInstance(ext.obj, agentbase.TestPollster) + class TestPollsterKeystone(agentbase.TestPollster): @plugin_base.check_keystone |