From 5d12a4e3175068a6b77606286a78bbe3edc561c2 Mon Sep 17 00:00:00 2001 From: Nicolas Simonds Date: Mon, 7 Mar 2016 14:46:32 -0800 Subject: [PATCH] libvirt: Allow use of live snapshots with RBD snapshot/clone The recently merged functionality for making use of RBD snapshot/clone when available is very valuable for the Ceph/RBD users out there. The new method also makes it possible to do live instance snapshots with Ceph/RBD. However, the current code explicitly forbids it. This patch allows the use of live instance snapshots when a RBD snapshot/clone is performed directly, and reverts back to cold instance snapshot when the old method is used. Co-Authored-By: Nicolas Simonds Change-Id: Ic3a3c73898aa868d6c510639ab12d2401dcb5001 Closes-Bug: #1539179 (cherry picked from commit 231832354932e26f0d76af1cf1711e701375672b) --- nova/tests/unit/virt/libvirt/test_driver.py | 59 +++++++++++++++++++++ nova/virt/libvirt/driver.py | 11 +++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index d8e3fcccb208..90e1d153a3e8 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -16932,6 +16932,62 @@ class LibvirtSnapshotTests(_BaseSnapshotTests): rbd.remove_snap.assert_called_with('c', 'd', ignore_errors=True, pool='b', force=True) + @mock.patch.object(imagebackend.Image, 'direct_snapshot') + @mock.patch.object(imagebackend.Image, 'resolve_driver_format') + @mock.patch.object(host.Host, 'has_min_version', return_value=True) + @mock.patch.object(host.Host, 'get_guest') + def test_raw_with_rbd_clone_is_live_snapshot(self, + mock_get_guest, + mock_version, + mock_resolve, + mock_snapshot): + self.flags(disable_libvirt_livesnapshot=False, group='workarounds') + self.flags(images_type='rbd', group='libvirt') + mock_guest = mock.Mock(spec=libvirt_guest.Guest) + mock_guest._domain = mock.Mock() + mock_get_guest.return_value = mock_guest + driver = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + recv_meta = self._create_image() + with mock.patch.object(driver, "suspend") as mock_suspend: + driver.snapshot(self.context, self.instance_ref, recv_meta['id'], + self.mock_update_task_state) + self.assertFalse(mock_suspend.called) + + @mock.patch.object(libvirt_driver.imagebackend.images, 'convert_image', + side_effect=_fake_convert_image) + @mock.patch.object(fake_libvirt_utils, 'find_disk') + @mock.patch.object(imagebackend.Image, 'resolve_driver_format') + @mock.patch.object(host.Host, 'has_min_version', return_value=True) + @mock.patch.object(host.Host, 'get_guest') + @mock.patch.object(rbd_utils, 'RBDDriver') + @mock.patch.object(rbd_utils, 'rbd') + def test_raw_with_rbd_clone_failure_does_cold_snapshot(self, + mock_rbd, + mock_driver, + mock_get_guest, + mock_version, + mock_resolve, + mock_find_disk, + mock_convert): + self.flags(disable_libvirt_livesnapshot=False, group='workarounds') + self.flags(images_type='rbd', group='libvirt') + rbd = mock_driver.return_value + rbd.parent_info = mock.Mock(side_effect=exception.ImageUnacceptable( + image_id='fake_id', reason='rbd testing')) + mock_find_disk.return_value = ('rbd://some/fake/rbd/image', 'raw') + mock_guest = mock.Mock(spec=libvirt_guest.Guest) + mock_guest.get_power_state.return_value = power_state.RUNNING + mock_guest._domain = mock.Mock() + mock_get_guest.return_value = mock_guest + driver = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + recv_meta = self._create_image() + + with mock.patch.object(fake_libvirt_utils, 'disk_type', new='rbd'): + with mock.patch.object(driver, "suspend") as mock_suspend: + driver.snapshot(self.context, self.instance_ref, + recv_meta['id'], self.mock_update_task_state) + self.assertTrue(mock_suspend.called) + class LXCSnapshotTests(LibvirtSnapshotTests): """Repeat all of the Libvirt snapshot tests, but with LXC enabled""" @@ -16939,6 +16995,9 @@ class LXCSnapshotTests(LibvirtSnapshotTests): super(LXCSnapshotTests, self).setUp() self.flags(virt_type='lxc', group='libvirt') + def test_raw_with_rbd_clone_failure_does_cold_snapshot(self): + self.skipTest("managedSave is not supported with LXC") + class LVMSnapshotTests(_BaseSnapshotTests): @mock.patch.object(fake_libvirt_utils, 'disk_type', new='lvm') diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index db028783979a..3a4a10796c37 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -1638,7 +1638,7 @@ class LibvirtDriver(driver.ComputeDriver): if (self._host.has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION, MIN_QEMU_LIVESNAPSHOT_VERSION, host.HV_DRIVER_QEMU) - and source_type not in ('lvm', 'rbd') + and source_type not in ('lvm') and not CONF.ephemeral_storage_encryption.enabled and not CONF.workarounds.disable_libvirt_livesnapshot): live_snapshot = True @@ -1703,6 +1703,15 @@ class LibvirtDriver(driver.ComputeDriver): update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD, expected_state=task_states.IMAGE_UPLOADING) + # TODO(nic): possibly abstract this out to the snapshot_backend + if source_type == 'rbd' and live_snapshot: + # Standard snapshot uses qemu-img convert from RBD which is + # not safe to run with live_snapshot. + live_snapshot = False + # Suspend the guest, so this is no longer a live snapshot + self._prepare_domain_for_snapshot(context, live_snapshot, + state, instance) + snapshot_directory = CONF.libvirt.snapshots_directory fileutils.ensure_tree(snapshot_directory) with utils.tempdir(dir=snapshot_directory) as tmpdir: