diff options
| -rw-r--r-- | cloudinit/registry.py | 14 | ||||
| -rw-r--r-- | cloudinit/reporting/__init__.py | 29 | ||||
| -rw-r--r-- | cloudinit/reporting/handlers.py | 15 | ||||
| -rw-r--r-- | tests/unittests/test_reporting.py | 51 | 
4 files changed, 95 insertions, 14 deletions
| diff --git a/cloudinit/registry.py b/cloudinit/registry.py index 46cf0585..04368ddf 100644 --- a/cloudinit/registry.py +++ b/cloudinit/registry.py @@ -1,3 +1,7 @@ +# Copyright 2015 Canonical Ltd. +# This file is part of cloud-init.  See LICENCE file for license information. +# +# vi: ts=4 expandtab  import copy @@ -5,6 +9,9 @@ class DictRegistry(object):      """A simple registry for a mapping of objects."""      def __init__(self): +        self.reset() + +    def reset(self):          self._items = {}      def register_item(self, key, item): @@ -14,6 +21,13 @@ class DictRegistry(object):                  'Item already registered with key {0}'.format(key))          self._items[key] = item +    def unregister_item(self, key, force=True): +        """Remove item from the registry.""" +        if key in self._items: +            del self._items[key] +        elif not force: +            raise KeyError("%s: key not present to unregister" % key) +      @property      def registered_items(self):          """All the items that have been registered. diff --git a/cloudinit/reporting/__init__.py b/cloudinit/reporting/__init__.py index 2b92ab58..d0bc14e3 100644 --- a/cloudinit/reporting/__init__.py +++ b/cloudinit/reporting/__init__.py @@ -20,8 +20,6 @@ DEFAULT_CONFIG = {      'logging': {'type': 'log'},  } -instantiated_handler_registry = DictRegistry() -  class _nameset(set):      def __getattr__(self, name): @@ -46,6 +44,11 @@ class ReportingEvent(object):          return '{0}: {1}: {2}'.format(              self.event_type, self.name, self.description) +    def as_dict(self): +        """The event represented as a dictionary.""" +        return {'name': self.name, 'description': self.description, +                'event_type': self.event_type} +  class FinishReportingEvent(ReportingEvent): @@ -60,9 +63,26 @@ class FinishReportingEvent(ReportingEvent):          return '{0}: {1}: {2}: {3}'.format(              self.event_type, self.name, self.result, self.description) +    def as_dict(self): +        """The event represented as json friendly.""" +        data = super(FinishReportingEvent, self).as_dict() +        data['result'] = self.result +        return data + -def add_configuration(config): +def update_configuration(config): +    """Update the instanciated_handler_registry. + +    :param config: +        The dictionary containing changes to apply.  If a key is given +        with a False-ish value, the registered handler matching that name +        will be unregistered. +    """      for handler_name, handler_config in config.items(): +        if not handler_config: +            instantiated_handler_registry.unregister_item( +                handler_name, force=True) +            continue          handler_config = handler_config.copy()          cls = available_handlers.registered_items[handler_config.pop('type')]          instance = cls(**handler_config) @@ -214,4 +234,5 @@ class ReportEventStack(object):              report_finish_event(self.fullname, msg, result) -add_configuration(DEFAULT_CONFIG) +instantiated_handler_registry = DictRegistry() +update_configuration(DEFAULT_CONFIG) diff --git a/cloudinit/reporting/handlers.py b/cloudinit/reporting/handlers.py index be323f53..86cbe3c3 100644 --- a/cloudinit/reporting/handlers.py +++ b/cloudinit/reporting/handlers.py @@ -1,14 +1,27 @@ +# vi: ts=4 expandtab +  import abc  import logging +import oauthlib.oauth1 as oauth1 + +import six  from cloudinit.registry import DictRegistry +from cloudinit import url_helper +from cloudinit import util +@six.add_metaclass(abc.ABCMeta)  class ReportingHandler(object): +    """Base class for report handlers. + +    Implement :meth:`~publish_event` for controlling what +    the handler does with an event. +    """      @abc.abstractmethod      def publish_event(self, event): -        raise NotImplementedError +        """Publish an event to the ``INFO`` log level."""  class LogHandler(ReportingHandler): diff --git a/tests/unittests/test_reporting.py b/tests/unittests/test_reporting.py index ffeb55d2..1a4ee8c4 100644 --- a/tests/unittests/test_reporting.py +++ b/tests/unittests/test_reporting.py @@ -4,6 +4,7 @@  # vi: ts=4 expandtab  from cloudinit import reporting +from cloudinit.reporting import handlers  from .helpers import (mock, TestCase) @@ -95,13 +96,29 @@ class TestReportingEvent(TestCase):              [event_type, name, description])          self.assertEqual(expected_string_representation, event.as_string()) +    def test_as_dict(self): +        event_type, name, desc = 'test_type', 'test_name', 'test_desc' +        event = reporting.ReportingEvent(event_type, name, desc) +        self.assertEqual( +            {'event_type': event_type, 'name': name, 'description': desc}, +            event.as_dict()) + + +class TestFinishReportingEvent(TestCase): +    def test_as_has_result(self): +        result = reporting.status.SUCCESS +        name, desc = 'test_name', 'test_desc' +        event = reporting.FinishReportingEvent(name, desc, result) +        ret = event.as_dict() +        self.assertTrue('result' in ret) +        self.assertEqual(ret['result'], result) + -class TestReportingHandler(TestCase): +class TestBaseReportingHandler(TestCase): -    def test_no_default_publish_event_implementation(self): -        self.assertRaises(NotImplementedError, -                          reporting.handlers.ReportingHandler().publish_event, -                          None) +    def test_base_reporting_handler_is_abstract(self): +        regexp = r".*abstract.*publish_event.*" +        self.assertRaisesRegexp(TypeError, regexp, handlers.ReportingHandler)  class TestLogHandler(TestCase): @@ -147,7 +164,7 @@ class TestReportingConfiguration(TestCase):      @mock.patch.object(reporting, 'instantiated_handler_registry')      def test_empty_configuration_doesnt_add_handlers(              self, instantiated_handler_registry): -        reporting.add_configuration({}) +        reporting.update_configuration({})          self.assertEqual(              0, instantiated_handler_registry.register_item.call_count) @@ -159,7 +176,7 @@ class TestReportingConfiguration(TestCase):          handler_cls = mock.Mock()          available_handlers.registered_items = {handler_type_name: handler_cls}          handler_name = 'my_test_handler' -        reporting.add_configuration( +        reporting.update_configuration(              {handler_name: {'type': handler_type_name}})          self.assertEqual(              {handler_name: handler_cls.return_value}, @@ -177,7 +194,7 @@ class TestReportingConfiguration(TestCase):          handler_config = extra_kwargs.copy()          handler_config.update({'type': handler_type_name})          handler_name = 'my_test_handler' -        reporting.add_configuration({handler_name: handler_config}) +        reporting.update_configuration({handler_name: handler_config})          self.assertEqual(              handler_cls.return_value,              reporting.instantiated_handler_registry.registered_items[ @@ -194,9 +211,25 @@ class TestReportingConfiguration(TestCase):          available_handlers.registered_items = {handler_type_name: handler_cls}          handler_config = {'type': handler_type_name, 'foo': 'bar'}          expected_handler_config = handler_config.copy() -        reporting.add_configuration({'my_test_handler': handler_config}) +        reporting.update_configuration({'my_test_handler': handler_config})          self.assertEqual(expected_handler_config, handler_config) +    @mock.patch.object( +        reporting, 'instantiated_handler_registry', reporting.DictRegistry()) +    @mock.patch.object(reporting, 'available_handlers') +    def test_handlers_removed_if_falseish_specified(self, available_handlers): +        handler_type_name = 'test_handler' +        handler_cls = mock.Mock() +        available_handlers.registered_items = {handler_type_name: handler_cls} +        handler_name = 'my_test_handler' +        reporting.update_configuration( +            {handler_name: {'type': handler_type_name}}) +        self.assertEqual( +            1, len(reporting.instantiated_handler_registry.registered_items)) +        reporting.update_configuration({handler_name: None}) +        self.assertEqual( +            0, len(reporting.instantiated_handler_registry.registered_items)) +  class TestReportingEventStack(TestCase):      @mock.patch('cloudinit.reporting.report_finish_event') | 
