Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Cloud:OpenStack:Icehouse
openstack-nova-doc
0001-Delay-STOPPED-lifecycle-event-for-Xen-doma...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-Delay-STOPPED-lifecycle-event-for-Xen-domains.patch of Package openstack-nova-doc
From e0b02e357f49761a3033f82e3a472d787ae5e9c1 Mon Sep 17 00:00:00 2001 From: Thomas Bechtold <tbechtold@suse.com> Date: Tue, 19 Aug 2014 17:41:57 +0200 Subject: [PATCH] Delay STOPPED lifecycle event for Xen domains When using libvirt, a reboot from inside of a kvm VM doesn't trigger any libvirt lifecycle event. That's fine. But rebooting a Xen VM leads to the events VIR_DOMAIN_EVENT_STOPPED and VIR_DOMAIN_EVENT_STARTED. Nova compute manager catches these events and tries to sync the power state of the VM with the power state in the database. In the case the VM state is ACTIVE but the power state is something that doesn't fit, the stop API call is executed to trigger all stop hooks. This leads to the problem that a reboot of a Xen VM without using the API isn't possible. To fix it, delay the emission of the STOPPED lifecycle event a couple of seconds. If a VIR_DOMAIN_EVENT_STARTED event is received while the STOPPED event is pending, cancel the pending STOPPED lifecycle event so the VM can continue to run. Conflicts: nova/virt/libvirt/driver.py Closes-Bug: #1293480 Change-Id: I690d3d700ab4d057554350da143ff77d78b509c6 (cherry picked from commit bd8329b34098436d18441a8129f3f20af53c2b91) --- nova/tests/virt/libvirt/test_libvirt.py | 39 +++++++++++++++++++++++++++++ nova/virt/libvirt/driver.py | 44 ++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) Index: nova-2014.1.5.dev30/nova/tests/virt/libvirt/test_libvirt.py =================================================================== --- nova-2014.1.5.dev30.orig/nova/tests/virt/libvirt/test_libvirt.py +++ nova-2014.1.5.dev30/nova/tests/virt/libvirt/test_libvirt.py @@ -6592,6 +6592,45 @@ class LibvirtConnTestCase(test.TestCase) self.assertEqual(got_events[0].transition, virtevent.EVENT_LIFECYCLE_STOPPED) + @mock.patch.object(libvirt_driver.LibvirtDriver, 'emit_event') + def test_event_emit_delayed_call_now(self, emit_event_mock): + self.flags(virt_type="kvm", group="libvirt") + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + conn._event_emit_delayed(None) + emit_event_mock.assert_called_once_with(None) + + @mock.patch.object(greenthread, 'spawn_after') + def test_event_emit_delayed_call_delayed(self, spawn_after_mock): + CONF.set_override("virt_type", "xen", group="libvirt") + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + event = virtevent.LifecycleEvent( + "cef19ce0-0ca2-11df-855d-b19fbce37686", + virtevent.EVENT_LIFECYCLE_STOPPED) + conn._event_emit_delayed(event) + spawn_after_mock.assert_called_once_with(15, conn.emit_event, event) + + @mock.patch.object(greenthread, 'spawn_after') + def test_event_emit_delayed_call_delayed_pending(self, spawn_after_mock): + self.flags(virt_type="xen", group="libvirt") + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + uuid = "cef19ce0-0ca2-11df-855d-b19fbce37686" + conn._events_delayed[uuid] = None + event = virtevent.LifecycleEvent( + uuid, virtevent.EVENT_LIFECYCLE_STOPPED) + conn._event_emit_delayed(event) + self.assertFalse(spawn_after_mock.called) + + def test_event_delayed_cleanup(self): + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + uuid = "cef19ce0-0ca2-11df-855d-b19fbce37686" + event = virtevent.LifecycleEvent( + uuid, virtevent.EVENT_LIFECYCLE_STARTED) + gt_mock = mock.Mock() + conn._events_delayed[uuid] = gt_mock + conn._event_delayed_cleanup(event) + gt_mock.cancel.assert_called_once_with() + self.assertNotIn(uuid, conn._events_delayed.keys()) + def test_set_cache_mode(self): self.flags(disk_cachemodes=['file=directsync'], group='libvirt') conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) Index: nova-2014.1.5.dev30/nova/virt/libvirt/driver.py =================================================================== --- nova-2014.1.5.dev30.orig/nova/virt/libvirt/driver.py +++ nova-2014.1.5.dev30/nova/virt/libvirt/driver.py @@ -412,6 +412,15 @@ class LibvirtDriver(driver.ComputeDriver self.disk_cachemodes[disk_type] = cache_mode self._volume_api = volume.API() + self._events_delayed = {} + # Note(toabctl): During a reboot of a Xen domain, STOPPED and + # STARTED events are sent. To prevent shutting + # down the domain during a reboot, delay the + # STOPPED lifecycle event some seconds. + if CONF.libvirt.virt_type == "xen": + self._lifecycle_delay = 15 + else: + self._lifecycle_delay = 0 @property def disk_cachemode(self): @@ -565,7 +574,9 @@ class LibvirtDriver(driver.ComputeDriver try: event = self._event_queue.get(block=False) if isinstance(event, virtevent.LifecycleEvent): - self.emit_event(event) + # call possibly with delay + self._event_delayed_cleanup(event) + self._event_emit_delayed(event) elif 'conn' in event and 'reason' in event: last_close_event = event except native_Queue.Empty: @@ -585,6 +596,37 @@ class LibvirtDriver(driver.ComputeDriver # new instances of being scheduled on this host. self._set_host_enabled(False, disable_reason=_error) + def _event_delayed_cleanup(self, event): + """Cleanup possible delayed stop events.""" + if (event.transition == virtevent.EVENT_LIFECYCLE_STARTED or + event.transition == virtevent.EVENT_LIFECYCLE_RESUMED): + if event.uuid in self._events_delayed.keys(): + self._events_delayed[event.uuid].cancel() + self._events_delayed.pop(event.uuid, None) + LOG.debug("Removed pending event for %s due to " + "lifecycle event", event.uuid) + + def _event_emit_delayed(self, event): + """Emit events - possibly delayed.""" + def event_cleanup(gt, *args, **kwargs): + """Callback function for greenthread. Called + to cleanup the _events_delayed dictionary when a event + was called. + """ + event = args[0] + self._events_delayed.pop(event.uuid, None) + + if self._lifecycle_delay > 0: + if event.uuid not in self._events_delayed.keys(): + id_ = greenthread.spawn_after(self._lifecycle_delay, + self.emit_event, event) + self._events_delayed[event.uuid] = id_ + # add callback to cleanup self._events_delayed dict after + # event was called + id_.link(event_cleanup, event) + else: + self.emit_event(event) + def _init_events_pipe(self): """Create a self-pipe for the native thread to synchronize on.
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor