← Back to team overview

cloud-init-dev team mailing list archive

Re: [Merge] lp:~smoser/cloud-init/trunk.reporting into lp:cloud-init

 

thanks. addressed and pushing now.


Diff comments:

> 
> === added file 'cloudinit/reporting.py'
> --- cloudinit/reporting.py	1970-01-01 00:00:00 +0000
> +++ cloudinit/reporting.py	2015-07-31 14:49:49 +0000
> @@ -0,0 +1,201 @@
> +# Copyright 2015 Canonical Ltd.
> +# This file is part of cloud-init.  See LICENCE file for license information.
> +#
> +# vi: ts=4 expandtab
> +"""
> +cloud-init reporting framework
> +
> +The reporting framework is intended to allow all parts of cloud-init to
> +report events in a structured manner.
> +"""
> +
> +import abc
> +import logging
> +
> +from cloudinit.registry import DictRegistry
> +
> +
> +FINISH_EVENT_TYPE = 'finish'
> +START_EVENT_TYPE = 'start'
> +
> +DEFAULT_CONFIG = {
> +    'logging': {'type': 'log'},
> +    'print': {'type': 'print'},
> +}
> +
> +
> +class _nameset(set):
> +    def __getattr__(self, name):
> +        if name in self:
> +            return name
> +        raise AttributeError
> +
> +status = _nameset(("SUCCESS", "WARN", "FAIL"))
> +
> +instantiated_handler_registry = DictRegistry()
> +available_handlers = DictRegistry()
> +
> +
> +class ReportingEvent(object):
> +    """Encapsulation of event formatting."""
> +
> +    def __init__(self, event_type, name, description):
> +        self.event_type = event_type
> +        self.name = name
> +        self.description = description
> +
> +    def as_string(self):
> +        """The event represented as a string."""
> +        return '{0}: {1}: {2}'.format(
> +            self.event_type, self.name, self.description)
> +
> +
> +class FinishReportingEvent(ReportingEvent):
> +
> +    def __init__(self, name, description, result=None):
> +        super(FinishReportingEvent, self).__init__(
> +            FINISH_EVENT_TYPE, name, description)
> +        if result is None:
> +            result = status.SUCCESS
> +        self.result = result
> +        if result not in status:
> +            raise ValueError("Invalid result: %s" % result)
> +
> +    def as_string(self):
> +        return '{0}: {1}: {2}: {3}'.format(
> +            self.event_type, self.name, self.result, self.description)
> +
> +
> +class ReportingHandler(object):
> +
> +    @abc.abstractmethod
> +    def publish_event(self, event):
> +        raise NotImplementedError
> +
> +
> +class LogHandler(ReportingHandler):
> +    """Publishes events to the cloud-init log at the ``INFO`` log level."""
> +
> +    def publish_event(self, event):
> +        """Publish an event to the ``INFO`` log level."""
> +        logger = logging.getLogger(
> +            '.'.join([__name__, event.event_type, event.name]))
> +        logger.info(event.as_string())
> +
> +
> +class PrintHandler(ReportingHandler):
> +    def publish_event(self, event):
> +        print(event.as_string())

yeah, stderr or dropping the PrintHandler is fine. it was for debugging.
obviously should not be in default.

> +
> +
> +def add_configuration(config):
> +    for handler_name, handler_config in config.items():
> +        handler_config = handler_config.copy()
> +        cls = available_handlers.registered_items[handler_config.pop('type')]
> +        instance = cls(**handler_config)
> +        instantiated_handler_registry.register_item(handler_name, instance)
> +
> +
> +def report_event(event):
> +    """Report an event to all registered event handlers.
> +
> +    This should generally be called via one of the other functions in
> +    the reporting module.
> +
> +    :param event_type:
> +        The type of the event; this should be a constant from the
> +        reporting module.
> +    """
> +    for _, handler in instantiated_handler_registry.registered_items.items():
> +        handler.publish_event(event)
> +
> +
> +def report_finish_event(event_name, event_description, result):
> +    """Report a "finish" event.
> +
> +    See :py:func:`.report_event` for parameter details.
> +    """
> +    event = FinishReportingEvent(event_name, event_description, result)
> +    return report_event(event)
> +
> +
> +def report_start_event(event_name, event_description):
> +    """Report a "start" event.
> +
> +    :param event_name:
> +        The name of the event; this should be a topic which events would
> +        share (e.g. it will be the same for start and finish events).
> +
> +    :param event_description:
> +        A human-readable description of the event that has occurred.
> +    """
> +    event = ReportingEvent(START_EVENT_TYPE, event_name, event_description)
> +    return report_event(event)
> +
> +
> +class ReportStack(object):
> +    def __init__(self, name, description, parent=None, reporting=None,

reporting_enabled is fine.
i'm fine with changes there. will apply.

> +                 exc_result=None):
> +        self.parent = parent
> +        self.name = name
> +        self.description = description
> +
> +        if exc_result is None:
> +            exc_result = status.FAIL
> +        self.exc_result = exc_result
> +
> +        # use parents reporting value if not provided
> +        if reporting is None:
> +            if parent:
> +                reporting = parent.reporting
> +            else:
> +                reporting = True
> +        self.reporting = reporting
> +
> +        if parent:
> +            self.fullname = '/'.join((parent.fullname, name,))
> +        else:
> +            self.fullname = self.name
> +        self.children = {}
> +
> +    def __repr__(self):
> +        return ("%s reporting=%s" % (self.fullname, self.reporting))
> +
> +    def __enter__(self):
> +        self.exception = None

somewhat vestigial. will drop.

> +        if self.reporting:
> +            report_start_event(self.fullname, self.description)
> +        if self.parent:
> +            self.parent.children[self.name] = (None, None)
> +        return self
> +
> +    def childrens_finish_info(self, result=None, description=None):
> +        for cand_result in (status.FAIL, status.WARN):
> +            for name, (value, msg) in self.children.items():
> +                if value == cand_result:
> +                    return (value, "[" + name + "]" + msg)
> +        if result is None:

see the just-added SearchReportStack for use of it.

> +            result = status.SUCCESS
> +        if description is None:
> +            description = self.description
> +        return (result, description)
> +
> +    def finish_info(self, exc):
> +        # return tuple of description, and value
> +        if exc:
> +            # by default, exceptions are fatal
> +            return (self.exc_result, self.description)
> +        return self.childrens_finish_info()
> +
> +    def __exit__(self, exc_type, exc_value, traceback):
> +        self.exception = exc_value
> +        (result, msg) = self.finish_info(exc_value)
> +        if self.parent:
> +            self.parent.children[self.name] = (result, msg)
> +        if self.reporting:
> +            report_finish_event(self.fullname, msg, result)
> +
> +        
> +available_handlers.register_item('log', LogHandler)
> +available_handlers.register_item('print', PrintHandler)
> +add_configuration(DEFAULT_CONFIG)


-- 
https://code.launchpad.net/~smoser/cloud-init/trunk.reporting/+merge/266578
Your team cloud init development team is requested to review the proposed merge of lp:~smoser/cloud-init/trunk.reporting into lp:cloud-init.


References