From e316d6f11a071dc75d1a543efe7521ef0ee98791 Mon Sep 17 00:00:00 2001 From: Tom Swanson Date: Thu, 7 May 2015 16:39:26 -0500 Subject: [PATCH] Dell: Added support for update_migrated_volume Added support for updated_migrated_volume to common driver. Added rename_volume to dell_storagecenter_api to support it. Added tests to test_dellsc.py and test_dellapi.py. The only thing we do in this rename the newly migrated volume destination volume on the Dell Storage center to be the same as the original volume ID. Since we identify the volumes by volume ID this prevents them from being lost. Change-Id: I531e24853cf5db8b7c34d91460d3abe786a652de Closes-bug: 1452919 (cherry picked from commit fc09713f2c8102d4b04c8df57fc401a34525d79e) --- cinder/tests/test_dellsc.py | 75 +++++++++++++++++++ cinder/tests/test_dellscapi.py | 24 ++++++ .../drivers/dell/dell_storagecenter_api.py | 15 ++++ .../drivers/dell/dell_storagecenter_common.py | 30 ++++++++ 4 files changed, 144 insertions(+) diff --git a/cinder/tests/test_dellsc.py b/cinder/tests/test_dellsc.py index 19c60bb43..3a9b4f2ab 100644 --- a/cinder/tests/test_dellsc.py +++ b/cinder/tests/test_dellsc.py @@ -1025,3 +1025,78 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): stats = self.driver.get_volume_stats(False) self.assertEqual(stats['storage_protocol'], 'iSCSI') assert mock_get_storage_usage.called is False + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_sc', + return_value=12345) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_volume', + return_value=VOLUME) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'rename_volume', + return_value=True) + def test_update_migrated_volume(self, + mock_rename_volume, + mock_find_volume, + mock_find_sc, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': 111} + backend_volume = {'id': 112} + model_update = {'_name_id': None} + rt = self.driver.update_migrated_volume(None, volume, backend_volume) + mock_rename_volume.assert_called_once_with(self.VOLUME, + volume['id']) + self.assertEqual(model_update, rt) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_sc', + return_value=12345) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_volume', + return_value=VOLUME) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'rename_volume', + return_value=False) + def test_update_migrated_volume_rename_fail(self, + mock_rename_volume, + mock_find_volume, + mock_find_sc, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': 111} + backend_volume = {'id': 112} + rt = self.driver.update_migrated_volume(None, volume, backend_volume) + mock_rename_volume.assert_called_once_with(self.VOLUME, + volume['id']) + self.assertEqual(None, rt) + + def test_update_migrated_volume_no_volume_id(self, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': None} + backend_volume = {'id': 112} + rt = self.driver.update_migrated_volume(None, volume, backend_volume) + self.assertEqual(None, rt) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_sc', + return_value=12345) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_volume', + return_value=None) + def test_update_migrated_volume_no_backend_id(self, + mock_find_volume, + mock_find_sc, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': 111} + backend_volume = {'id': None} + rt = self.driver.update_migrated_volume(None, volume, backend_volume) + mock_find_sc.assert_called_once_with(12345) + mock_find_volume.assert_called_once_with(12345, None) + self.assertEqual(None, rt) diff --git a/cinder/tests/test_dellscapi.py b/cinder/tests/test_dellscapi.py index 85f38ebeb..934f4fd6b 100644 --- a/cinder/tests/test_dellscapi.py +++ b/cinder/tests/test_dellscapi.py @@ -3587,6 +3587,30 @@ class DellSCSanAPITestCase(test.TestCase): self.assertTrue(mock_post.called) self.assertIsNone(res, 'Expected None') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post', + return_value=RESPONSE_200) + def test_rename_volume(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + res = self.scapi.rename_volume(self.VOLUME, 'newname') + self.assertTrue(mock_post.called) + self.assertTrue(res) + + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post', + return_value=RESPONSE_204) + def test_rename_volume_failure(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + res = self.scapi.rename_volume(self.VOLUME, 'newname') + self.assertTrue(mock_post.called) + self.assertFalse(res) + @mock.patch.object(dell_storagecenter_api.HttpClient, 'delete', return_value=RESPONSE_200) diff --git a/cinder/volume/drivers/dell/dell_storagecenter_api.py b/cinder/volume/drivers/dell/dell_storagecenter_api.py index 3f1d39625..e09c7a387 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_api.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_api.py @@ -1130,6 +1130,21 @@ class StorageCenterApi(object): 's': vol['configuredSize']}) return vol + def rename_volume(self, scvolume, name): + payload = {} + payload['Name'] = name + r = self.client.post('StorageCenter/ScVolume/%s/Modify' + % self._get_id(scvolume), + payload) + if r.status_code != 200: + LOG.error(_LE('Error renaming volume %(o)s to %(n)s: %(c)d %(r)s'), + {'o': scvolume['name'], + 'n': name, + 'c': r.status_code, + 'r': r.reason}) + return False + return True + def _delete_server(self, scserver): '''_delete_server diff --git a/cinder/volume/drivers/dell/dell_storagecenter_common.py b/cinder/volume/drivers/dell/dell_storagecenter_common.py index 47511ab7b..207f1840d 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_common.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_common.py @@ -348,3 +348,33 @@ class DellCommonDriver(san.SanDriver): LOG.debug('Total cap %(t)s Free cap %(f)s', {'t': totalcapacitygb, 'f': freespacegb}) + + def update_migrated_volume(self, ctxt, volume, new_volume): + """Return model update for migrated volume. + + :param volume: The original volume that was migrated to this backend + :param new_volume: The migration volume object that was created on + this backend as part of the migration process + :return model_update to update DB with any needed changes + """ + # We use id as our volume name so we need to rename the backend + # volume to the original volume name. + original_volume_name = volume.get('id') + current_name = new_volume.get('id') + LOG.debug('update_migrated_volume: %(c)s to %(o)s', + {'c': current_name, + 'o': original_volume_name}) + if original_volume_name: + with self._client.open_connection() as api: + ssn = api.find_sc(self.configuration.dell_sc_ssn) + if ssn is not None: + scvolume = api.find_volume(ssn, + current_name) + if scvolume: + if api.rename_volume(scvolume, original_volume_name): + model_update = {'_name_id': None} + return model_update + # The world was horrible to us so we should error and leave. + LOG.error(_LE('Unabled to rename the logical volume for volume: %s'), + original_volume_name) + return None -- 2.45.2