From d63f7c1b47509f31c1e5aa8d27392d7f83e6dad2 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Tue, 12 Feb 2013 13:32:06 +0000 Subject: Add support for lifecycle events in the libvirt driver This wires up the libvirt compute driver to process async events from libvirtd and emit lifecycle events. This makes use of the native libvirt event loop to handle I/O processing which requires a native thread. The native thread uses a queue + self-pipe to forward events onto a green thread. The green thread reads events off the queue and uses the emit_event method to dispatch them to the compute manager. Blueprint: compute-driver-events Change-Id: Icd2cb7081adde10420ae55beebe60350afe21379 Signed-off-by: Daniel P. Berrange --- nova/tests/fakelibvirt.py | 38 +++++++++++++++++++++ nova/tests/test_libvirt.py | 84 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) (limited to 'nova/tests') diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py index 6abe7771c..69a4e677e 100644 --- a/nova/tests/fakelibvirt.py +++ b/nova/tests/fakelibvirt.py @@ -70,6 +70,17 @@ VIR_DOMAIN_CRASHED = 6 VIR_DOMAIN_XML_SECURE = 1 +VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0 + +VIR_DOMAIN_EVENT_DEFINED = 0 +VIR_DOMAIN_EVENT_UNDEFINED = 1 +VIR_DOMAIN_EVENT_STARTED = 2 +VIR_DOMAIN_EVENT_SUSPENDED = 3 +VIR_DOMAIN_EVENT_RESUMED = 4 +VIR_DOMAIN_EVENT_STOPPED = 5 +VIR_DOMAIN_EVENT_SHUTDOWN = 6 +VIR_DOMAIN_EVENT_PMSUSPENDED = 7 + VIR_DOMAIN_UNDEFINE_MANAGED_SAVE = 1 VIR_DOMAIN_AFFECT_CURRENT = 0 @@ -506,6 +517,7 @@ class Connection(object): self._running_vms = {} self._id_counter = 1 # libvirt reserves 0 for the hypervisor. self._nwfilters = {} + self._event_callbacks = {} self.fakeLibVersion = version self.fakeVersion = version @@ -517,6 +529,7 @@ class Connection(object): def _mark_running(self, dom): self._running_vms[self._id_counter] = dom + self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STARTED, 0) self._id_counter += 1 def _mark_not_running(self, dom): @@ -528,10 +541,13 @@ class Connection(object): for (k, v) in self._running_vms.iteritems(): if v == dom: del self._running_vms[k] + self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STOPPED, 0) return def _undefine(self, dom): del self._vms[dom.name()] + if not dom._transient: + self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_UNDEFINED, 0) def getInfo(self): return [node_arch, @@ -563,14 +579,25 @@ class Connection(object): 'name "%s"' % name, VIR_ERR_NO_DOMAIN, VIR_FROM_QEMU) + def _emit_lifecycle(self, dom, event, detail): + if VIR_DOMAIN_EVENT_ID_LIFECYCLE not in self._event_callbacks: + return + + cbinfo = self._event_callbacks[VIR_DOMAIN_EVENT_ID_LIFECYCLE] + callback = cbinfo[0] + opaque = cbinfo[1] + callback(self, dom, event, detail, opaque) + def defineXML(self, xml): dom = Domain(connection=self, running=False, transient=False, xml=xml) self._vms[dom.name()] = dom + self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_DEFINED, 0) return dom def createXML(self, xml, flags): dom = Domain(connection=self, running=True, transient=True, xml=xml) self._vms[dom.name()] = dom + self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_STARTED, 0) return dom def getType(self): @@ -586,6 +613,9 @@ class Connection(object): def getHostname(self): return 'compute1' + def domainEventRegisterAny(self, dom, eventid, callback, opaque): + self._event_callbacks[eventid] = [callback, opaque] + def getCapabilities(self): return ''' @@ -875,6 +905,14 @@ def openAuth(uri, auth, flags): return Connection(uri, readonly=False) +def virEventRunDefaultImpl(): + time.sleep(1) + + +def virEventRegisterDefaultImpl(): + pass + + virDomain = Domain diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 8a2130c80..039189a9e 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -52,6 +52,7 @@ from nova import utils from nova import version from nova.virt.disk import api as disk from nova.virt import driver +from nova.virt import event as virtevent from nova.virt import fake from nova.virt import firewall as base_firewall from nova.virt import images @@ -99,7 +100,8 @@ class FakeVirDomainSnapshot(object): class FakeVirtDomain(object): - def __init__(self, fake_xml=None): + def __init__(self, fake_xml=None, uuidstr=None): + self.uuidstr = uuidstr if fake_xml: self._fake_dom_xml = fake_xml else: @@ -131,6 +133,9 @@ class FakeVirtDomain(object): def XMLDesc(self, *args): return self._fake_dom_xml + def UUIDString(self): + return self.uuidstr + class CacheConcurrencyTestCase(test.TestCase): def setUp(self): @@ -3340,6 +3345,83 @@ class LibvirtConnTestCase(test.TestCase): got = conn.get_instance_capabilities() self.assertEqual(want, got) + def test_event_dispatch(self): + # Validate that the libvirt self-pipe for forwarding + # events between threads is working sanely + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + got_events = [] + + def handler(event): + got_events.append(event) + + conn.register_event_listener(handler) + + conn._init_events_pipe() + + event1 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_STARTED) + event2 = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_PAUSED) + conn._queue_event(event1) + conn._queue_event(event2) + conn._dispatch_events() + + 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) + + conn._queue_event(event3) + conn._queue_event(event4) + conn._dispatch_events() + + want_events = [event1, event2, event3, event4] + self.assertEqual(want_events, got_events) + + def test_event_lifecycle(self): + # Validate that libvirt events are correctly translated + # to Nova events + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + got_events = [] + + def handler(event): + got_events.append(event) + + conn.register_event_listener(handler) + conn._init_events_pipe() + fake_dom_xml = """ + + cef19ce0-0ca2-11df-855d-b19fbce37686 + + + + + + + """ + dom = FakeVirtDomain(fake_dom_xml, + "cef19ce0-0ca2-11df-855d-b19fbce37686") + + conn._event_lifecycle_callback(conn._conn, + dom, + libvirt.VIR_DOMAIN_EVENT_STOPPED, + 0, + conn) + conn._dispatch_events() + self.assertEqual(len(got_events), 1) + self.assertEqual(type(got_events[0]), virtevent.LifecycleEvent) + self.assertEqual(got_events[0].uuid, + "cef19ce0-0ca2-11df-855d-b19fbce37686") + self.assertEqual(got_events[0].transition, + virtevent.EVENT_LIFECYCLE_STOPPED) + class HostStateTestCase(test.TestCase): -- cgit