diff options
| author | Jenkins <jenkins@review.openstack.org> | 2013-02-20 08:41:47 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2013-02-20 08:41:47 +0000 |
| commit | 22a1cb330c63b82bf9bd4a1bb9649ed0143de961 (patch) | |
| tree | 56d868de95187875184b6d6c1e70dfe3f1010608 | |
| parent | 785513debe30b0046b0efa0405653c26012d0c2a (diff) | |
| parent | d8c3a6d2655a4ccc64ebf46a856319e2221a9072 (diff) | |
| download | nova-22a1cb330c63b82bf9bd4a1bb9649ed0143de961.tar.gz nova-22a1cb330c63b82bf9bd4a1bb9649ed0143de961.tar.xz nova-22a1cb330c63b82bf9bd4a1bb9649ed0143de961.zip | |
Merge "Add basic infrastructure for compute driver async events"
| -rw-r--r-- | nova/tests/test_virt_drivers.py | 67 | ||||
| -rwxr-xr-x | nova/virt/driver.py | 34 | ||||
| -rw-r--r-- | nova/virt/event.py | 85 |
3 files changed, 186 insertions, 0 deletions
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index 5a46beffb..f6e0bca38 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -28,6 +28,7 @@ from nova import test from nova.tests import fake_libvirt_utils from nova.tests.image import fake as fake_image from nova.tests import utils as test_utils +from nova.virt import event as virtevent from nova.virt import fake LOG = logging.getLogger(__name__) @@ -549,6 +550,72 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): def test_remove_from_aggregate(self): self.connection.remove_from_aggregate(self.ctxt, 'aggregate', 'host') + def test_events(self): + got_events = [] + + def handler(event): + got_events.append(event) + + self.connection.register_event_listener(handler) + + event1 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_STARTED) + event2 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_PAUSED) + + self.connection.emit_event(event1) + self.connection.emit_event(event2) + want_events = [event1, event2] + self.assertEqual(want_events, got_events) + + event3 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_RESUMED) + event4 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_STOPPED) + + self.connection.emit_event(event3) + self.connection.emit_event(event4) + + want_events = [event1, event2, event3, event4] + self.assertEqual(want_events, got_events) + + def test_event_bad_object(self): + # Passing in something which does not inherit + # from virtevent.Event + + def handler(event): + pass + + self.connection.register_event_listener(handler) + + badevent = { + "foo": "bar" + } + + self.assertRaises(ValueError, + self.connection.emit_event, + badevent) + + def test_event_bad_callback(self): + # Check that if a callback raises an exception, + # it does not propagate back out of the + # 'emit_event' call + + def handler(event): + raise Exception("Hit Me!") + + self.connection.register_event_listener(handler) + + event1 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_STARTED) + + self.connection.emit_event(event1) + class AbstractDriverTestCase(_VirtDriverTestCase, test.TestCase): def setUp(self): diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 13b8bead8..c0979c9d1 100755 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -28,6 +28,7 @@ from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import log as logging from nova import utils +from nova.virt import event as virtevent driver_opts = [ cfg.StrOpt('compute_driver', @@ -130,6 +131,7 @@ class ComputeDriver(object): def __init__(self, virtapi): self.virtapi = virtapi + self._compute_event_callback = None def init_host(self, host): """Initialize anything that is necessary for the driver to function, @@ -863,6 +865,38 @@ class ComputeDriver(object): """ return False + def register_event_listener(self, callback): + """Register a callback to receive events. + + Register a callback to receive asynchronous event + notifications from hypervisors. The callback will + be invoked with a single parameter, which will be + an instance of the nova.virt.event.Event class.""" + + self._compute_event_callback = callback + + def emit_event(self, event): + """Dispatches an event to the compute manager. + + Invokes the event callback registered by the + compute manager to dispatch the event. This + must only be invoked from a green thread.""" + + if not self._compute_event_callback: + LOG.debug("Discarding event %s" % str(event)) + return + + if not isinstance(event, virtevent.Event): + raise ValueError( + _("Event must be an instance of nova.virt.event.Event")) + + try: + LOG.debug("Emitting event %s" % str(event)) + self._compute_event_callback(event) + except Exception, ex: + LOG.error(_("Exception dispatching event %(event)s: %(ex)s") + % locals()) + def load_compute_driver(virtapi, compute_driver=None): """Load a compute driver module. diff --git a/nova/virt/event.py b/nova/virt/event.py new file mode 100644 index 000000000..684986f8a --- /dev/null +++ b/nova/virt/event.py @@ -0,0 +1,85 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Asynchronous event notifications from virtualization drivers. + +This module defines a set of classes representing data for +various asynchronous events that can occurr in a virtualization +driver. +""" + +import time + +EVENT_LIFECYCLE_STARTED = 0 +EVENT_LIFECYCLE_STOPPED = 1 +EVENT_LIFECYCLE_PAUSED = 2 +EVENT_LIFECYCLE_RESUMED = 3 + + +class Event(object): + """Base class for all events emitted by a hypervisor. + + All events emitted by a virtualization driver are + subclasses of this base object. The only generic + information recorded in the base class is a timestamp + indicating when the event first occurred. The timestamp + is recorded as fractional seconds since the UNIX epoch. + """ + + def __init__(self, timestamp=None): + if timestamp is None: + self.timestamp = time.time() + else: + self.timestamp = timestamp + + def get_timestamp(self): + return self.timestamp + + +class InstanceEvent(Event): + """Base class for all instance events. + + All events emitted by a virtualization driver which + are associated with a virtual domain instance are + subclasses of this base object. This object records + the UUID associated with the instance.""" + + def __init__(self, uuid, timestamp=None): + super(InstanceEvent, self).__init__(timestamp) + + self.uuid = uuid + + def get_instance_uuid(self): + return self.uuid + + +class LifecycleEvent(InstanceEvent): + """Class for instance lifecycle state change events. + + When a virtual domain instance lifecycle state changes, + events of this class are emitted. The EVENT_LIFECYCLE_XX + constants defined why lifecycle change occurred. This + event allows detection of an instance starting/stopping + without need for polling""" + + def __init__(self, uuid, transition, timestamp=None): + super(LifecycleEvent, self).__init__(uuid, timestamp) + + self.transition = transition + + def get_transition(self): + return self.transition |
