summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-02-20 08:41:47 +0000
committerGerrit Code Review <review@openstack.org>2013-02-20 08:41:47 +0000
commit22a1cb330c63b82bf9bd4a1bb9649ed0143de961 (patch)
tree56d868de95187875184b6d6c1e70dfe3f1010608
parent785513debe30b0046b0efa0405653c26012d0c2a (diff)
parentd8c3a6d2655a4ccc64ebf46a856319e2221a9072 (diff)
downloadnova-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.py67
-rwxr-xr-xnova/virt/driver.py34
-rw-r--r--nova/virt/event.py85
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