|
Packit Service |
a04d08 |
# Copyright (C) 2015 Canonical Ltd.
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
events for reporting.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The events here are designed to be used with reporting.
|
|
Packit Service |
a04d08 |
They can be published to registered handlers with report_event.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
import base64
|
|
Packit Service |
a04d08 |
import os.path
|
|
Packit Service |
a04d08 |
import time
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
from . import instantiated_handler_registry, available_handlers
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
FINISH_EVENT_TYPE = 'finish'
|
|
Packit Service |
a04d08 |
START_EVENT_TYPE = 'start'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
DEFAULT_EVENT_ORIGIN = 'cloudinit'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class _nameset(set):
|
|
Packit Service |
a04d08 |
def __getattr__(self, name):
|
|
Packit Service |
a04d08 |
if name in self:
|
|
Packit Service |
a04d08 |
return name
|
|
Packit Service |
a04d08 |
raise AttributeError("%s not a valid value" % name)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
status = _nameset(("SUCCESS", "WARN", "FAIL"))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class ReportingEvent(object):
|
|
Packit Service |
a04d08 |
"""Encapsulation of event formatting."""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, event_type, name, description,
|
|
Packit Service |
a04d08 |
origin=DEFAULT_EVENT_ORIGIN, timestamp=None):
|
|
Packit Service |
a04d08 |
self.event_type = event_type
|
|
Packit Service |
a04d08 |
self.name = name
|
|
Packit Service |
a04d08 |
self.description = description
|
|
Packit Service |
a04d08 |
self.origin = origin
|
|
Packit Service |
a04d08 |
if timestamp is None:
|
|
Packit Service |
a04d08 |
timestamp = time.time()
|
|
Packit Service |
a04d08 |
self.timestamp = timestamp
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def as_string(self):
|
|
Packit Service |
a04d08 |
"""The event represented as a string."""
|
|
Packit Service |
a04d08 |
return '{0}: {1}: {2}'.format(
|
|
Packit Service |
a04d08 |
self.event_type, self.name, self.description)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def as_dict(self):
|
|
Packit Service |
a04d08 |
"""The event represented as a dictionary."""
|
|
Packit Service |
a04d08 |
return {'name': self.name, 'description': self.description,
|
|
Packit Service |
a04d08 |
'event_type': self.event_type, 'origin': self.origin,
|
|
Packit Service |
a04d08 |
'timestamp': self.timestamp}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class FinishReportingEvent(ReportingEvent):
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, name, description, result=status.SUCCESS,
|
|
Packit Service |
a04d08 |
post_files=None):
|
|
Packit Service |
a04d08 |
super(FinishReportingEvent, self).__init__(
|
|
Packit Service |
a04d08 |
FINISH_EVENT_TYPE, name, description)
|
|
Packit Service |
a04d08 |
self.result = result
|
|
Packit Service |
a04d08 |
if post_files is None:
|
|
Packit Service |
a04d08 |
post_files = []
|
|
Packit Service |
a04d08 |
self.post_files = post_files
|
|
Packit Service |
a04d08 |
if result not in status:
|
|
Packit Service |
a04d08 |
raise ValueError("Invalid result: %s" % result)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def as_string(self):
|
|
Packit Service |
a04d08 |
return '{0}: {1}: {2}: {3}'.format(
|
|
Packit Service |
a04d08 |
self.event_type, self.name, self.result, self.description)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def as_dict(self):
|
|
Packit Service |
a04d08 |
"""The event represented as json friendly."""
|
|
Packit Service |
a04d08 |
data = super(FinishReportingEvent, self).as_dict()
|
|
Packit Service |
a04d08 |
data['result'] = self.result
|
|
Packit Service |
a04d08 |
if self.post_files:
|
|
Packit Service |
a04d08 |
data['files'] = _collect_file_info(self.post_files)
|
|
Packit Service |
a04d08 |
return data
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
def report_event(event, excluded_handler_types=None):
|
|
Packit Service |
9bfd13 |
"""Report an event to all registered event handlers
|
|
Packit Service |
9bfd13 |
except those whose type is in excluded_handler_types.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This should generally be called via one of the other functions in
|
|
Packit Service |
a04d08 |
the reporting module.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
:param excluded_handler_types:
|
|
Packit Service |
9bfd13 |
List of handlers types to exclude from reporting the event to.
|
|
Packit Service |
a04d08 |
:param event_type:
|
|
Packit Service |
a04d08 |
The type of the event; this should be a constant from the
|
|
Packit Service |
a04d08 |
reporting module.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
9bfd13 |
|
|
Packit Service |
9bfd13 |
if not excluded_handler_types:
|
|
Packit Service |
9bfd13 |
excluded_handler_types = {}
|
|
Packit Service |
9bfd13 |
excluded_handler_classes = {
|
|
Packit Service |
9bfd13 |
hndl_cls
|
|
Packit Service |
9bfd13 |
for hndl_type, hndl_cls in available_handlers.registered_items.items()
|
|
Packit Service |
9bfd13 |
if hndl_type in excluded_handler_types
|
|
Packit Service |
9bfd13 |
}
|
|
Packit Service |
9bfd13 |
|
|
Packit Service |
9bfd13 |
handlers = instantiated_handler_registry.registered_items.items()
|
|
Packit Service |
9bfd13 |
for _, handler in handlers:
|
|
Packit Service |
9bfd13 |
if type(handler) in excluded_handler_classes:
|
|
Packit Service |
9bfd13 |
continue # skip this excluded handler
|
|
Packit Service |
a04d08 |
handler.publish_event(event)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def report_finish_event(event_name, event_description,
|
|
Packit Service |
a04d08 |
result=status.SUCCESS, post_files=None):
|
|
Packit Service |
a04d08 |
"""Report a "finish" event.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
See :py:func:`.report_event` for parameter details.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
event = FinishReportingEvent(event_name, event_description, result,
|
|
Packit Service |
a04d08 |
post_files=post_files)
|
|
Packit Service |
a04d08 |
return report_event(event)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def report_start_event(event_name, event_description):
|
|
Packit Service |
a04d08 |
"""Report a "start" event.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param event_name:
|
|
Packit Service |
a04d08 |
The name of the event; this should be a topic which events would
|
|
Packit Service |
a04d08 |
share (e.g. it will be the same for start and finish events).
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param event_description:
|
|
Packit Service |
a04d08 |
A human-readable description of the event that has occurred.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
event = ReportingEvent(START_EVENT_TYPE, event_name, event_description)
|
|
Packit Service |
a04d08 |
return report_event(event)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class ReportEventStack(object):
|
|
Packit Service |
a04d08 |
"""Context Manager for using :py:func:`report_event`
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This enables calling :py:func:`report_start_event` and
|
|
Packit Service |
a04d08 |
:py:func:`report_finish_event` through a context manager.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param name:
|
|
Packit Service |
a04d08 |
the name of the event
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param description:
|
|
Packit Service |
a04d08 |
the event's description, passed on to :py:func:`report_start_event`
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param message:
|
|
Packit Service |
a04d08 |
the description to use for the finish event. defaults to
|
|
Packit Service |
a04d08 |
:param:description.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param parent:
|
|
Packit Service |
a04d08 |
:type parent: :py:class:ReportEventStack or None
|
|
Packit Service |
a04d08 |
The parent of this event. The parent is populated with
|
|
Packit Service |
a04d08 |
results of all its children. The name used in reporting
|
|
Packit Service |
a04d08 |
is <parent.name>/<name>
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param reporting_enabled:
|
|
Packit Service |
a04d08 |
Indicates if reporting events should be generated.
|
|
Packit Service |
a04d08 |
If not provided, defaults to the parent's value, or True if no parent
|
|
Packit Service |
a04d08 |
is provided.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
:param result_on_exception:
|
|
Packit Service |
a04d08 |
The result value to set if an exception is caught. default
|
|
Packit Service |
a04d08 |
value is FAIL.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
def __init__(self, name, description, message=None, parent=None,
|
|
Packit Service |
a04d08 |
reporting_enabled=None, result_on_exception=status.FAIL,
|
|
Packit Service |
a04d08 |
post_files=None):
|
|
Packit Service |
a04d08 |
self.parent = parent
|
|
Packit Service |
a04d08 |
self.name = name
|
|
Packit Service |
a04d08 |
self.description = description
|
|
Packit Service |
a04d08 |
self.message = message
|
|
Packit Service |
a04d08 |
self.result_on_exception = result_on_exception
|
|
Packit Service |
a04d08 |
self.result = status.SUCCESS
|
|
Packit Service |
a04d08 |
if post_files is None:
|
|
Packit Service |
a04d08 |
post_files = []
|
|
Packit Service |
a04d08 |
self.post_files = post_files
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# use parents reporting value if not provided
|
|
Packit Service |
a04d08 |
if reporting_enabled is None:
|
|
Packit Service |
a04d08 |
if parent:
|
|
Packit Service |
a04d08 |
reporting_enabled = parent.reporting_enabled
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
reporting_enabled = True
|
|
Packit Service |
a04d08 |
self.reporting_enabled = reporting_enabled
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if parent:
|
|
Packit Service |
a04d08 |
self.fullname = '/'.join((parent.fullname, name,))
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
self.fullname = self.name
|
|
Packit Service |
a04d08 |
self.children = {}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __repr__(self):
|
|
Packit Service |
a04d08 |
return ("ReportEventStack(%s, %s, reporting_enabled=%s)" %
|
|
Packit Service |
a04d08 |
(self.name, self.description, self.reporting_enabled))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __enter__(self):
|
|
Packit Service |
a04d08 |
self.result = status.SUCCESS
|
|
Packit Service |
a04d08 |
if self.reporting_enabled:
|
|
Packit Service |
a04d08 |
report_start_event(self.fullname, self.description)
|
|
Packit Service |
a04d08 |
if self.parent:
|
|
Packit Service |
a04d08 |
self.parent.children[self.name] = (None, None)
|
|
Packit Service |
a04d08 |
return self
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _childrens_finish_info(self):
|
|
Packit Service |
a04d08 |
for cand_result in (status.FAIL, status.WARN):
|
|
Packit Service |
a04d08 |
for _name, (value, _msg) in self.children.items():
|
|
Packit Service |
a04d08 |
if value == cand_result:
|
|
Packit Service |
a04d08 |
return (value, self.message)
|
|
Packit Service |
a04d08 |
return (self.result, self.message)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def result(self):
|
|
Packit Service |
a04d08 |
return self._result
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@result.setter
|
|
Packit Service |
a04d08 |
def result(self, value):
|
|
Packit Service |
a04d08 |
if value not in status:
|
|
Packit Service |
a04d08 |
raise ValueError("'%s' not a valid result" % value)
|
|
Packit Service |
a04d08 |
self._result = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def message(self):
|
|
Packit Service |
a04d08 |
if self._message is not None:
|
|
Packit Service |
a04d08 |
return self._message
|
|
Packit Service |
a04d08 |
return self.description
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@message.setter
|
|
Packit Service |
a04d08 |
def message(self, value):
|
|
Packit Service |
a04d08 |
self._message = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _finish_info(self, exc):
|
|
Packit Service |
a04d08 |
# return tuple of description, and value
|
|
Packit Service |
a04d08 |
if exc:
|
|
Packit Service |
a04d08 |
return (self.result_on_exception, self.message)
|
|
Packit Service |
a04d08 |
return self._childrens_finish_info()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __exit__(self, exc_type, exc_value, traceback):
|
|
Packit Service |
a04d08 |
(result, msg) = self._finish_info(exc_value)
|
|
Packit Service |
a04d08 |
if self.parent:
|
|
Packit Service |
a04d08 |
self.parent.children[self.name] = (result, msg)
|
|
Packit Service |
a04d08 |
if self.reporting_enabled:
|
|
Packit Service |
a04d08 |
report_finish_event(self.fullname, msg, result,
|
|
Packit Service |
a04d08 |
post_files=self.post_files)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _collect_file_info(files):
|
|
Packit Service |
a04d08 |
if not files:
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
ret = []
|
|
Packit Service |
a04d08 |
for fname in files:
|
|
Packit Service |
a04d08 |
if not os.path.isfile(fname):
|
|
Packit Service |
a04d08 |
content = None
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
with open(fname, "rb") as fp:
|
|
Packit Service |
a04d08 |
content = base64.b64encode(fp.read()).decode()
|
|
Packit Service |
a04d08 |
ret.append({'path': fname, 'content': content,
|
|
Packit Service |
a04d08 |
'encoding': 'base64'})
|
|
Packit Service |
a04d08 |
return ret
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|