diff --git a/cinder/tests/unit/volume/drivers/test_storpool.py b/cinder/tests/unit/volume/drivers/test_storpool.py index 02df8134d94..dbe90d6ab2d 100644 --- a/cinder/tests/unit/volume/drivers/test_storpool.py +++ b/cinder/tests/unit/volume/drivers/test_storpool.py @@ -140,6 +140,16 @@ class MockAPI(object): volumes[new_name]['name'] = new_name del volumes[name] + def volumeRevert(self, name, data): + if name not in volumes: + raise MockApiError('No such volume {name}'.format(name=name)) + + snapname = data['toSnapshot'] + if snapname not in snapshots: + raise MockApiError('No such snapshot {name}'.format(name=snapname)) + + volumes[name] = dict(snapshots[snapname]) + class MockAttachDB(object): def __init__(self, log): @@ -155,6 +165,10 @@ class MockAttachDB(object): return snapshotName(vtype, vid) +def MockVolumeRevertDesc(toSnapshot): + return {'toSnapshot': toSnapshot} + + def MockVolumeUpdateDesc(size): return {'size': size} @@ -170,6 +184,7 @@ def MockSPConfig(section = 's01'): fakeStorPool.spapi.ApiError = MockApiError fakeStorPool.spconfig.SPConfig = MockSPConfig fakeStorPool.spopenstack.AttachDB = MockAttachDB +fakeStorPool.sptypes.VolumeRevertDesc = MockVolumeRevertDesc fakeStorPool.sptypes.VolumeUpdateDesc = MockVolumeUpdateDesc @@ -524,3 +539,51 @@ class StorPoolTestCase(test.TestCase): self.driver.get_pool({ 'volume_type': volume_type })) + + def test_volume_revert(self): + vol_id = 'rev1' + vol_name = volumeName(vol_id) + snap_id = 'rev-s1' + snap_name = snapshotName('snap', snap_id) + + self.assertVolumeNames([]) + self.assertDictEqual({}, volumes) + self.assertDictEqual({}, snapshots) + + self.driver.create_volume({'id': vol_id, 'name': 'v1', 'size': 1, + 'volume_type': None}) + self.assertVolumeNames((vol_id,)) + self.assertDictEqual({}, snapshots) + + self.driver.create_snapshot({'id': snap_id, 'volume_id': vol_id}) + self.assertVolumeNames((vol_id,)) + self.assertListEqual([snap_name], sorted(snapshots.keys())) + self.assertDictEqual(volumes[vol_name], snapshots[snap_name]) + self.assertIsNot(volumes[vol_name], snapshots[snap_name]) + + self.driver.extend_volume({'id': vol_id}, 2) + self.assertVolumeNames((vol_id,)) + self.assertNotEqual(volumes[vol_name], snapshots[snap_name]) + + self.driver.revert_to_snapshot(None, {'id': vol_id}, {'id': snap_id}) + self.assertVolumeNames((vol_id,)) + self.assertDictEqual(volumes[vol_name], snapshots[snap_name]) + self.assertIsNot(volumes[vol_name], snapshots[snap_name]) + + self.driver.delete_snapshot({'id': snap_id}) + self.assertVolumeNames((vol_id,)) + self.assertDictEqual({}, snapshots) + + self.assertRaisesRegex(exception.VolumeBackendAPIException, + 'No such snapshot', + self.driver.revert_to_snapshot, None, + {'id': vol_id}, {'id': snap_id}) + + self.driver.delete_volume({'id': vol_id}) + self.assertDictEqual({}, volumes) + self.assertDictEqual({}, snapshots) + + self.assertRaisesRegex(exception.VolumeBackendAPIException, + 'No such volume', + self.driver.revert_to_snapshot, None, + {'id': vol_id}, {'id': snap_id}) diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py index 62e058e14c4..4e46ba05f26 100644 --- a/cinder/volume/drivers/storpool.py +++ b/cinder/volume/drivers/storpool.py @@ -90,9 +90,10 @@ class StorPoolDriver(driver.VolumeDriver): 1.2.2 - Reintroduce the driver into OpenStack Queens, add ignore_errors to the internal _detach_volume() method 1.2.3 - Advertise some more driver capabilities. + 2.0.0 - Implement revert_to_snapshot(). """ - VERSION = '1.2.3' + VERSION = '2.0.0' CI_WIKI_NAME = 'StorPool_distributed_storage_CI' def __init__(self, *args, **kwargs): @@ -423,3 +424,18 @@ class StorPoolDriver(driver.VolumeDriver): '%(err)s', {'tname': temp_name, 'oname': orig_name, 'err': e}) return {'_name_id': new_volume['_name_id'] or new_volume['id']} + + def revert_to_snapshot(self, context, volume, snapshot): + volname = self._attach.volumeName(volume['id']) + snapname = self._attach.snapshotName('snap', snapshot['id']) + try: + rev = sptypes.VolumeRevertDesc(toSnapshot=snapname) + self._attach.api().volumeRevert(volname, rev) + except spapi.ApiError as e: + LOG.error('StorPool revert_to_snapshot(): could not revert ' + 'the %(vol_id)s volume to the %(snap_id)s snapshot: ' + '%(err)s', + {'vol_id': volume['id'], + 'snap_id': snapshot['id'], + 'err': e}) + raise self._backendException(e) diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index c12552cba2e..76f6638dd6e 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -953,7 +953,7 @@ driver.rbd=complete driver.rbd_iscsi=complete driver.sandstone=complete driver.seagate=missing -driver.storpool=missing +driver.storpool=complete driver.synology=missing driver.toyou_netstor=complete driver.toyou_netstor_tyds=missing diff --git a/releasenotes/notes/storpool-revert-to-snapshot-a202358ee16ecb62.yaml b/releasenotes/notes/storpool-revert-to-snapshot-a202358ee16ecb62.yaml new file mode 100644 index 00000000000..ca80a87e8fe --- /dev/null +++ b/releasenotes/notes/storpool-revert-to-snapshot-a202358ee16ecb62.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + StorPool driver: implemented revert to snapshot, which happens + immediately i.e. without deleting and recreating the volume.