From 01971c9cb6cb555d0c440ffbb7332f18ed553930 Mon Sep 17 00:00:00 2001 From: Vipin Balachandran Date: Mon, 21 Aug 2017 18:04:57 -0700 Subject: [PATCH] VMware: Revert to snapshot Adding support for revert-to-snapshot for COW based snapshot in VMDK driver. Change-Id: I240d8d6f1745f9611855e6f6897697b5a0748ef9 --- .../volume/drivers/vmware/test_vmware_vmdk.py | 42 +++++++++++++++++++ .../drivers/vmware/test_vmware_volumeops.py | 27 ++++++++++++ cinder/volume/drivers/vmware/exceptions.py | 5 +++ cinder/volume/drivers/vmware/vmdk.py | 14 +++++++ cinder/volume/drivers/vmware/volumeops.py | 14 +++++++ ...k-revert-to-snapshot-ee3d638565649f44.yaml | 5 +++ 6 files changed, 107 insertions(+) create mode 100644 releasenotes/notes/vmware-vmdk-revert-to-snapshot-ee3d638565649f44.yaml diff --git a/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py b/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py index 5f1ffba583a..17bede03d51 100644 --- a/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py +++ b/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py @@ -3051,6 +3051,48 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): vops.move_backing_to_folder.assert_called_once_with(backing, new_folder) + @mock.patch.object(VMDK_DRIVER, 'volumeops') + def test_revert_to_snapshot_with_no_backing(self, vops): + vops.get_backing.return_value = None + + volume = self._create_volume_obj() + snapshot = fake_snapshot.fake_snapshot_obj(self._context, + volume=volume) + self._driver.revert_to_snapshot( + mock.sentinel.context, volume, snapshot) + + vops.get_backing.assert_called_once_with(volume.name) + vops.revert_to_snapshot.assert_not_called() + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + def test_revert_to_snapshot_template_format(self, vops): + volume = self._create_volume_obj() + loc = '/test-dc/foo' + snapshot = fake_snapshot.fake_snapshot_obj(self._context, + volume=volume, + provider_location=loc) + self.assertRaises(cinder_exceptions.InvalidSnapshot, + self._driver.revert_to_snapshot, + mock.sentinel.context, + volume, + snapshot) + vops.revert_to_snapshot.assert_not_called() + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + def test_revert_to_snapshot(self, vops): + backing = mock.sentinel.backing + vops.get_backing.return_value = backing + + volume = self._create_volume_obj() + snapshot = fake_snapshot.fake_snapshot_obj(self._context, + volume=volume) + self._driver.revert_to_snapshot( + mock.sentinel.context, volume, snapshot) + + vops.get_backing.assert_called_once_with(volume.name) + vops.revert_to_snapshot.assert_called_once_with(backing, + snapshot.name) + @ddt.ddt class ImageDiskTypeTest(test.TestCase): diff --git a/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py b/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py index ec2d0ad4a76..997e6a22e66 100644 --- a/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py +++ b/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py @@ -947,6 +947,33 @@ class VolumeOpsTestCase(test.TestCase): snapshot, removeChildren=False) self.session.wait_for_task.assert_called_once_with(task) + @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' + 'get_snapshot') + def test_revert_to_snapshot_with_missing_snapshot(self, get_snapshot): + get_snapshot.return_value = None + + backing = mock.sentinel.backing + self.assertRaises(vmdk_exceptions.SnapshotNotFoundException, + self.vops.revert_to_snapshot, backing, 'foo') + get_snapshot.assert_called_once_with(backing, 'foo') + + @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' + 'get_snapshot') + def test_revert_to_snapshot(self, get_snapshot): + snapshot = mock.sentinel.snapshot + get_snapshot.return_value = snapshot + + task = mock.sentinel.task + self.session.invoke_api.return_value = task + + backing = mock.sentinel.backing + self.vops.revert_to_snapshot(backing, 'foo') + + get_snapshot.assert_called_once_with(backing, 'foo') + self.session.invoke_api.assert_called_once_with( + self.session.vim, 'RevertToSnapshot_Task', snapshot) + self.session.wait_for_task.assert_called_once_with(task) + def test_get_folder(self): folder = mock.sentinel.folder backing = mock.sentinel.backing diff --git a/cinder/volume/drivers/vmware/exceptions.py b/cinder/volume/drivers/vmware/exceptions.py index 13b08a7e7f1..fedfadf69e2 100644 --- a/cinder/volume/drivers/vmware/exceptions.py +++ b/cinder/volume/drivers/vmware/exceptions.py @@ -60,3 +60,8 @@ class NoValidHostException(exceptions.VMwareDriverException): class TemplateNotFoundException(exceptions.VMwareDriverException): """Thrown when template cannot be found.""" msg_fmt = _("Template cannot be found at path: %(path)s.") + + +class SnapshotNotFoundException(exceptions.VMwareDriverException): + """Thrown when the backend snapshot cannot be found.""" + msg_fmt = _("Snapshot: %(name)s not found.") diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index 0841d9cacd6..8cd6e71ea21 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -2142,3 +2142,17 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): dc = self.volumeops.get_dc(backing) new_folder = self._get_volume_group_folder(dc, new_project) self.volumeops.move_backing_to_folder(backing, new_folder) + + def revert_to_snapshot(self, context, volume, snapshot): + inv_path = snapshot.provider_location + is_template = inv_path is not None + if is_template: + LOG.error("Revert to template based snapshot is not supported.") + raise exception.InvalidSnapshot("Cannot revert to template " + "based snapshot") + + backing = self.volumeops.get_backing(volume.name) + if not backing: + LOG.debug("Backing does not exist for volume.", resource=volume) + else: + self.volumeops.revert_to_snapshot(backing, snapshot.name) diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index e5789707059..d35a20e168e 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -1048,6 +1048,20 @@ class VMwareVolumeOps(object): LOG.info("Successfully deleted snapshot: %(name)s of backing: " "%(backing)s.", {'backing': backing, 'name': name}) + def revert_to_snapshot(self, backing, name): + LOG.debug("Revert to snapshot: %(name)s of backing: %(backing)s.", + {'name': name, 'backing': backing}) + + snapshot = self.get_snapshot(backing, name) + if not snapshot: + raise vmdk_exceptions.SnapshotNotFoundException( + name=name) + + task = self._session.invoke_api(self._session.vim, + 'RevertToSnapshot_Task', + snapshot) + self._session.wait_for_task(task) + def _get_folder(self, backing): """Get parent folder of the backing. diff --git a/releasenotes/notes/vmware-vmdk-revert-to-snapshot-ee3d638565649f44.yaml b/releasenotes/notes/vmware-vmdk-revert-to-snapshot-ee3d638565649f44.yaml new file mode 100644 index 00000000000..acac105f008 --- /dev/null +++ b/releasenotes/notes/vmware-vmdk-revert-to-snapshot-ee3d638565649f44.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for revert-to-snapshot in the VMware VMDK driver. +