diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 88b66147640..d7c47cee870 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -3034,6 +3034,70 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) + def test_revert_to_snapshot(self): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + volume = {'name': self.VOLUME_NAME, + 'id': self.VOLUME_ID_SNAP, + 'display_name': 'Foo Volume', + 'size': 2, + 'host': self.FAKE_CINDER_HOST, + 'volume_type': None, + 'volume_type_id': None} + + mock_client = self.setup_driver() + mock_client.isOnlinePhysicalCopy.return_value = False + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.revert_to_snapshot(self.ctxt, volume, self.snapshot) + + expected = [ + mock.call.isOnlinePhysicalCopy('osv-dh-F5VGRTseuujPjbeRBVg'), + mock.call.promoteVirtualCopy('oss-L4I73ONuTci9Fd4ceij-MQ', + optional={}) + ] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + + @mock.patch.object(volume_types, 'get_volume_type') + def test_revert_to_snapshot_replicated_volume(self, _mock_volume_types): + + _mock_volume_types.return_value = { + 'name': 'replicated', + 'extra_specs': { + 'replication_enabled': ' True', + 'volume_type': self.volume_type_replicated}} + + mock_client = self.setup_driver() + mock_client.isOnlinePhysicalCopy.return_value = True + mock_client.getStorageSystemInfo.return_value = mock.ANY + + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.revert_to_snapshot( + self.ctxt, + self.volume_replicated, + self.snapshot) + expected = [ + mock.call.stopRemoteCopy('rcg-0DM4qZEVSKON-DXN-N'), + mock.call.isOnlinePhysicalCopy('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.promoteVirtualCopy( + 'oss-L4I73ONuTci9Fd4ceij-MQ', + optional={'online': True, 'allowRemoteCopyParent': True}), + mock.call.startRemoteCopy('rcg-0DM4qZEVSKON-DXN-N') + ] + mock_client.assert_has_calls( + self.get_id_login + + self.standard_logout + + self.standard_login + + expected + + self.standard_logout) + def test_delete_snapshot(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 989c8b0ab2b..fef38d88ec8 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -264,11 +264,12 @@ class HPE3PARCommon(object): 3.0.37 - Fixed image cache enabled capability. bug #1686985 3.0.38 - Fixed delete operation of replicated volume which is part of QOS. bug #1717875 + 3.0.39 - Add support for revert to snapshot. """ - VERSION = "3.0.38" + VERSION = "3.0.39" stats = {} @@ -3068,6 +3069,49 @@ class HPE3PARCommon(object): msg = _("Volume has a temporary snapshot.") raise exception.VolumeIsBusy(message=msg) + def revert_to_snapshot(self, volume, snapshot): + """Revert volume to snapshot. + + :param volume: A dictionary describing the volume to revert + :param snapshot: A dictionary describing the latest snapshot + """ + volume_name = self._get_3par_vol_name(volume['id']) + snapshot_name = self._get_3par_snap_name(snapshot['id']) + rcg_name = self._get_3par_rcg_name(volume['id']) + + optional = {} + replication_flag = self._volume_of_replicated_type(volume) + if replication_flag: + LOG.debug("Found replicated volume: %(volume)s.", + {'volume': volume_name}) + optional['allowRemoteCopyParent'] = True + try: + self.client.stopRemoteCopy(rcg_name) + except Exception as ex: + msg = (_("There was an error stoping remote copy: %s.") % + six.text_type(ex)) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + if self.client.isOnlinePhysicalCopy(volume_name): + LOG.debug("Found an online copy for %(volume)s.", + {'volume': volume_name}) + optional['online'] = True + + self.client.promoteVirtualCopy(snapshot_name, optional=optional) + + if replication_flag: + try: + self.client.startRemoteCopy(rcg_name) + except Exception as ex: + msg = (_("There was an error starting remote copy: %s.") % + six.text_type(ex)) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + LOG.info("Volume %(volume)s succesfully reverted to %(snap)s.", + {'volume': volume_name, 'snap': snapshot_name}) + def find_existing_vlun(self, volume, host): """Finds an existing VLUN for a volume on a host. diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index f9fdbde658b..34f7d960600 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -675,6 +675,15 @@ class HPE3PARFCDriver(driver.ManageableVD, finally: self._logout(common) + @utils.trace + def revert_to_snapshot(self, context, volume, snapshot): + """Revert volume to snapshot.""" + common = self._login() + try: + common.revert_to_snapshot(volume, snapshot) + finally: + self._logout(common) + @utils.trace def migrate_volume(self, context, volume, host): if volume['status'] == 'in-use': diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index 6912597273b..6ee609c4640 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -946,6 +946,15 @@ class HPE3PARISCSIDriver(driver.ManageableVD, finally: self._logout(common) + @utils.trace + def revert_to_snapshot(self, context, volume, snapshot): + """Revert volume to snapshot.""" + common = self._login() + try: + common.revert_to_snapshot(volume, snapshot) + finally: + self._logout(common) + @utils.trace def migrate_volume(self, context, volume, host): if volume['status'] == 'in-use': diff --git a/releasenotes/notes/revert-volume-to-snapshot-6aa0dffb010265e5.yaml b/releasenotes/notes/revert-volume-to-snapshot-6aa0dffb010265e5.yaml new file mode 100644 index 00000000000..3eb4c6bfac4 --- /dev/null +++ b/releasenotes/notes/revert-volume-to-snapshot-6aa0dffb010265e5.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added revert volume to snapshot in 3par driver.