From f701d091bea170e595ed47469a60c4e148a2edcd Mon Sep 17 00:00:00 2001 From: Xiaoqin Li Date: Thu, 9 Nov 2017 02:13:57 -0800 Subject: [PATCH] Storwize: revert to snapshot support Adding support for reverting volume to a snapshot in Storwize/SVC driver. Change-Id: I0e83162641279ffe242d18dfd3062ce37b3a959b Implements: blueprint storwize-revert-snapshot --- .../volume/drivers/ibm/test_storwize_svc.py | 34 ++++++++++ .../ibm/storwize_svc/storwize_svc_common.py | 62 +++++++++++++++---- ...wize-revert-snapshot-681c76d68676558a.yaml | 3 + 3 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/storwize-revert-snapshot-681c76d68676558a.yaml diff --git a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py index e63c521390a..f60b9b4dbe1 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py @@ -6341,6 +6341,40 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): uid_of_new_volume = self._get_vdisk_uid(new_volume['name']) self.assertEqual(uid, uid_of_new_volume) + @mock.patch.object(storwize_svc_common.StorwizeSSH, + 'mkfcmap') + @mock.patch.object(storwize_svc_common.StorwizeHelpers, + '_prepare_fc_map') + @mock.patch.object(storwize_svc_common.StorwizeSSH, + 'startfcmap') + def test_revert_to_snapshot(self, startfcmap, prepare_fc_map, mkfcmap): + mkfcmap.side_effect = ['1'] + vol1 = self._generate_vol_info() + snap1 = self._generate_snap_info(vol1.id) + vol1.size = '11' + + self.assertRaises(exception.InvalidInput, + self.driver.revert_to_snapshot, self.ctxt, + vol1, snap1) + + vol2 = self._generate_vol_info() + snap2 = self._generate_snap_info(vol2.id) + + with mock.patch.object(storwize_svc_common.StorwizeSVCCommonDriver, + '_get_volume_replicated_type') as vol_rep_type: + vol_rep_type.side_effect = [True, False] + self.assertRaises(exception.InvalidInput, + self.driver.revert_to_snapshot, self.ctxt, + vol2, snap2) + self.driver.revert_to_snapshot(self.ctxt, vol2, snap2) + mkfcmap.assert_called_once_with( + snap2.name, vol2.name, True, + self.driver.configuration.storwize_svc_flashcopy_rate) + prepare_fc_map.assert_called_once_with( + '1', self.driver.configuration.storwize_svc_flashcopy_timeout, + True,) + startfcmap.assert_called_once_with('1', True) + class CLIResponseTestCase(test.TestCase): def test_empty(self): diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py index ab871904162..96f74fc3728 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py @@ -582,12 +582,18 @@ class StorwizeSSH(object): raise exception.VolumeBackendAPIException(data=msg) return fc_map_id - def prestartfcmap(self, fc_map_id): - ssh_cmd = ['svctask', 'prestartfcmap', fc_map_id] + def prestartfcmap(self, fc_map_id, restore=False): + ssh_cmd = ['svctask', 'prestartfcmap'] + if restore: + ssh_cmd.append('-restore') + ssh_cmd.append(fc_map_id) self.run_ssh_assert_no_output(ssh_cmd) - def startfcmap(self, fc_map_id): - ssh_cmd = ['svctask', 'startfcmap', fc_map_id] + def startfcmap(self, fc_map_id, restore=False): + ssh_cmd = ['svctask', 'startfcmap'] + if restore: + ssh_cmd.append('-restore') + ssh_cmd.append(fc_map_id) self.run_ssh_assert_no_output(ssh_cmd) def prestartfcconsistgrp(self, fc_consist_group): @@ -1461,8 +1467,8 @@ class StorwizeHelpers(object): copies['secondary'] = copy return copies - def _prepare_fc_map(self, fc_map_id, timeout): - self.ssh.prestartfcmap(fc_map_id) + def _prepare_fc_map(self, fc_map_id, timeout, restore): + self.ssh.prestartfcmap(fc_map_id, restore) mapping_ready = False max_retries = (timeout // self.WAIT_TIME) + 1 for try_number in range(1, max_retries): @@ -1474,7 +1480,7 @@ class StorwizeHelpers(object): mapping_ready = True break elif mapping_attrs['status'] == 'stopped': - self.ssh.prestartfcmap(fc_map_id) + self.ssh.prestartfcmap(fc_map_id, restore) elif mapping_attrs['status'] != 'preparing': msg = (_('Unexecpted mapping status %(status)s for mapping ' '%(id)s. Attributes: %(attr)s.') @@ -1650,15 +1656,15 @@ class StorwizeHelpers(object): return volume_model_updates def run_flashcopy(self, source, target, timeout, copy_rate, - full_copy=True): + full_copy=True, restore=False): """Create a FlashCopy mapping from the source to the target.""" LOG.debug('Enter: run_flashcopy: execute FlashCopy from source ' '%(source)s to target %(target)s.', {'source': source, 'target': target}) fc_map_id = self.ssh.mkfcmap(source, target, full_copy, copy_rate) - self._prepare_fc_map(fc_map_id, timeout) - self.ssh.startfcmap(fc_map_id) + self._prepare_fc_map(fc_map_id, timeout, restore) + self.ssh.startfcmap(fc_map_id, restore) LOG.debug('Leave: run_flashcopy: FlashCopy started from ' '%(source)s to %(target)s.', @@ -1704,10 +1710,14 @@ class StorwizeHelpers(object): return mapping_ids def _get_flashcopy_mapping_attributes(self, fc_map_id): - resp = self.ssh.lsfcmap(fc_map_id) - if not len(resp): + try: + resp = self.ssh.lsfcmap(fc_map_id) + return resp[0] if len(resp) else None + except exception.VolumeBackendAPIException as ex: + LOG.warning("Failed to get fcmap %(fcmap)s info. " + "Exception: %(ex)s.", {'fcmap': fc_map_id, + 'ex': ex}) return None - return resp[0] def _get_flashcopy_consistgrp_attr(self, fc_map_id): resp = self.ssh.lsfcconsistgrp(fc_map_id) @@ -1736,6 +1746,8 @@ class StorwizeHelpers(object): attrs = self._get_flashcopy_mapping_attributes(map_id) if attrs: status = attrs['status'] + else: + continue if copy_rate == '0': if source == name: @@ -4603,6 +4615,30 @@ class StorwizeSVCCommonDriver(san.SanDriver, return model_update, snapshots_model + @cinder_utils.trace + def revert_to_snapshot(self, context, volume, snapshot): + """Revert volume to snapshot.""" + if snapshot.volume_size != volume.size: + raise exception.InvalidInput( + reason=_('Reverting volume is not supported if the volume ' + 'size is not equal to the snapshot size.')) + + rep_type = self._get_volume_replicated_type(context, volume) + if rep_type: + raise exception.InvalidInput( + reason=_('Reverting replication volume is not supported.')) + try: + self._helpers.run_flashcopy( + snapshot.name, volume.name, + self.configuration.storwize_svc_flashcopy_timeout, + self.configuration.storwize_svc_flashcopy_rate, True, True) + except Exception as err: + msg = (_("Reverting volume %(vol)s to snapshot %(snap)s failed " + "due to: %(err)s.") + % {"vol": volume.name, "snap": snapshot.name, "err": err}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + def get_pool(self, volume): attr = self._helpers.get_vdisk_attributes(volume['name']) diff --git a/releasenotes/notes/storwize-revert-snapshot-681c76d68676558a.yaml b/releasenotes/notes/storwize-revert-snapshot-681c76d68676558a.yaml new file mode 100644 index 00000000000..d7e35938f66 --- /dev/null +++ b/releasenotes/notes/storwize-revert-snapshot-681c76d68676558a.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add reverting to snapshot support in Storwize Cinder driver.