]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Implement the update_migrated_volume for the drivers
authorVincent Hou <sbhou@cn.ibm.com>
Thu, 7 May 2015 05:53:25 +0000 (13:53 +0800)
committerVincent Hou <sbhou@cn.ibm.com>
Thu, 2 Jul 2015 14:14:55 +0000 (22:14 +0800)
This patch implements update_migrated_volume for LVM, Storwize
and updates Dell, StorPool and Infortrend drivers accordingly. It
makes sure that after a successful volume migration, the cinder
volume name(id) is the same as the backend volume name(id). Other
back-end drivers can take this patch as a reference to implement
update_migrated_volume.

PS: Not applicable to multi-attached volumes, since we need to wait
until the multi-attach lands in Nova.

This patch also adds a unit test for the StorPool driver's
update_migrated_volume() implementation.

Co-Authored-By: Peter Penchev <openstack-dev@storpool.com>
Change-Id: I69707340ddf2b55286ff0d84319529b2f502cefa
Partial-Bug: #1450649

18 files changed:
cinder/db/sqlalchemy/api.py
cinder/tests/unit/db/test_finish_migration.py
cinder/tests/unit/test_dellsc.py
cinder/tests/unit/test_infortrend_cli.py
cinder/tests/unit/test_infortrend_common.py
cinder/tests/unit/test_storpool.py
cinder/tests/unit/test_storwize_svc.py
cinder/tests/unit/test_volume.py
cinder/volume/driver.py
cinder/volume/drivers/dell/dell_storagecenter_common.py
cinder/volume/drivers/ibm/storwize_svc/__init__.py
cinder/volume/drivers/infortrend/eonstor_ds_cli/common_cli.py
cinder/volume/drivers/infortrend/infortrend_fc_cli.py
cinder/volume/drivers/infortrend/infortrend_iscsi_cli.py
cinder/volume/drivers/lvm.py
cinder/volume/drivers/storpool.py
cinder/volume/manager.py
cinder/volume/rpcapi.py

index 7dc908013b286d9724a354474c2bcc914f059493..6fb4ea4163ca1d7e4e0fa8d1174cc849c59ca22b 100644 (file)
@@ -1151,12 +1151,13 @@ def finish_volume_migration(context, src_vol_id, dest_vol_id):
             return attr in inst.__class__.__table__.columns
 
         for key, value in dest_volume_ref.iteritems():
-            if key == 'id' or not is_column(dest_volume_ref, key):
+            # The implementation of update_migrated_volume will decide the
+            # values for _name_id and provider_location.
+            if (key in ('id', '_name_id', 'provider_location')
+                    or not is_column(dest_volume_ref, key)):
                 continue
             elif key == 'migration_status':
                 value = None
-            elif key == '_name_id':
-                value = dest_volume_ref['_name_id'] or dest_volume_ref['id']
 
             setattr(src_volume_ref, key, value)
 
index 38b6e9b427dbac83c55c94d3d038ca363b3ec826..3993c226ae7dfadc2c462cb2b09bbeb7cf6ded89 100644 (file)
@@ -38,9 +38,6 @@ class FinishVolumeMigrationTestCase(test.TestCase):
                                    dest_volume['id'])
 
         src_volume = db.volume_get(ctxt, src_volume['id'])
-        expected_name = 'volume-%s' % dest_volume['id']
-        self.assertEqual(src_volume['_name_id'], dest_volume['id'])
-        self.assertEqual(src_volume['name'], expected_name)
-        self.assertEqual(src_volume['host'], 'dest')
-        self.assertEqual(src_volume['status'], 'available')
+        self.assertEqual('dest', src_volume['host'])
+        self.assertEqual('available', src_volume['status'])
         self.assertIsNone(src_volume['migration_status'])
index e8bb59a06d89727c89123e0f5588fcf25bd068b6..95bc8c674a398640f5292928c3229ccec4f0c064 100644 (file)
@@ -1058,7 +1058,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
         volume = {'id': 111}
         backend_volume = {'id': 112}
         model_update = {'_name_id': None}
-        rt = self.driver.update_migrated_volume(None, volume, backend_volume)
+        rt = self.driver.update_migrated_volume(None, volume, backend_volume,
+                                                'available')
         mock_rename_volume.assert_called_once_with(self.VOLUME,
                                                    volume['id'])
         self.assertEqual(model_update, rt)
@@ -1080,20 +1081,22 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
                                                 mock_open_connection,
                                                 mock_init):
         volume = {'id': 111}
-        backend_volume = {'id': 112}
-        rt = self.driver.update_migrated_volume(None, volume, backend_volume)
+        backend_volume = {'id': 112, '_name_id': 113}
+        rt = self.driver.update_migrated_volume(None, volume, backend_volume,
+                                                'available')
         mock_rename_volume.assert_called_once_with(self.VOLUME,
                                                    volume['id'])
-        self.assertEqual(None, rt)
+        self.assertEqual({'_name_id': 113}, 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)
+        backend_volume = {'id': 112, '_name_id': 113}
+        rt = self.driver.update_migrated_volume(None, volume, backend_volume,
+                                                'available')
+        self.assertEqual({'_name_id': 113}, rt)
 
     @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
                        'find_sc',
@@ -1108,8 +1111,9 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
                                                   mock_open_connection,
                                                   mock_init):
         volume = {'id': 111}
-        backend_volume = {'id': None}
-        rt = self.driver.update_migrated_volume(None, volume, backend_volume)
+        backend_volume = {'id': None, '_name_id': None}
+        rt = self.driver.update_migrated_volume(None, volume, backend_volume,
+                                                'available')
         mock_find_sc.assert_called_once_with()
         mock_find_volume.assert_called_once_with(None)
-        self.assertEqual(None, rt)
+        self.assertEqual({'_name_id': None}, rt)
index 9573c6385b20c86d41be6c2a527c2abe14c00c48..9bac359855a991d61a017441d457f308a541099c 100644 (file)
@@ -90,6 +90,7 @@ class InfortrendCLITestData(object):
         'provider_auth': None,
         'project_id': 'project',
         'display_name': None,
+        '_name_id': '6bb119a8-d25b-45a7-8d1b-88e127885666',
         'display_description': 'Part-1-Copy',
         'volume_type_id': None,
         'provider_location': '',
index 65600cb236f8e36d6bf3f87769de8c9b52991d9b..aefc139e9ee3766c528d1e0381409245b15b0fa5 100644 (file)
@@ -1983,6 +1983,7 @@ class InfortrendiSCSICommonTestCase(InfortrendTestCass):
         dst_volume['provider_location'] = 'system_id^%s@partition_id^%s' % (
             int(self.cli_data.fake_system_id[0], 16), test_dst_part_id)
         test_model_update = {
+            '_name_id': None,
             'provider_location': dst_volume['provider_location'],
         }
 
@@ -1992,19 +1993,20 @@ class InfortrendiSCSICommonTestCase(InfortrendTestCass):
         self._driver_setup(mock_commands)
 
         model_update = self.driver.update_migrated_volume(
-            None, src_volume, dst_volume)
+            None, src_volume, dst_volume, 'available')
 
         expect_cli_cmd = [
             mock.call('SetPartition', test_dst_part_id,
                       'name=%s' % src_volume['id'].replace('-', '')),
         ]
         self._assert_cli_has_calls(expect_cli_cmd)
-        self.assertDictMatch(model_update, test_model_update)
+        self.assertDictMatch(test_model_update, model_update)
 
     @mock.patch.object(common_cli.LOG, 'debug', mock.Mock())
     def test_update_migrated_volume_rename_fail(self):
         src_volume = self.cli_data.test_volume
         dst_volume = self.cli_data.test_dst_volume
+        dst_volume['_name_id'] = 'fake_name_id'
         test_dst_part_id = self.cli_data.fake_partition_id[1]
         dst_volume['provider_location'] = 'system_id^%s@partition_id^%s' % (
             int(self.cli_data.fake_system_id[0], 16), test_dst_part_id)
@@ -2013,10 +2015,6 @@ class InfortrendiSCSICommonTestCase(InfortrendTestCass):
             'SetPartition': FAKE_ERROR_RETURN
         }
         self._driver_setup(mock_commands)
-
-        self.assertRaises(
-            exception.InfortrendCliException,
-            self.driver.update_migrated_volume,
-            None,
-            src_volume,
-            dst_volume)
+        model_update = self.driver.update_migrated_volume(
+            None, src_volume, dst_volume, 'available')
+        self.assertEqual({'_name_id': 'fake_name_id'}, model_update)
index 816b7c255aea1d00460debfbdb461effe77a4636..780e062714c143c899e7d494b4f65d88655c85b5 100644 (file)
@@ -77,6 +77,11 @@ class MockDisk(object):
         self.agAllocated = 1
 
 
+class MockVolume(object):
+    def __init__(self, v):
+        self.name = v['name']
+
+
 class MockTemplate(object):
     def __init__(self, name):
         self.name = name
@@ -114,14 +119,22 @@ class MockAPI(object):
     def volumeDelete(self, name):
         del volumes[name]
 
+    def volumesList(self):
+        return [MockVolume(v[1]) for v in volumes.items()]
+
     def volumeTemplatesList(self):
         return self._templates
 
     def volumesReassign(self, json):
         pass
 
-    def volumeUpdate(self, name, size):
-        volumes[name]['size'] = size['size']
+    def volumeUpdate(self, name, data):
+        if 'size' in data:
+            volumes[name]['size'] = data['size']
+
+        if 'rename' in data and data['rename'] != name:
+            volumes[data['rename']] = volumes[name]
+            del volumes[name]
 
 
 class MockAttachDB(object):
@@ -303,6 +316,48 @@ class StorPoolTestCase(test.TestCase):
         self.assertDictEqual({}, volumes)
         self.assertDictEqual({}, snapshots)
 
+    @mock_volume_types
+    def test_update_migrated_volume(self):
+        self.assertVolumeNames([])
+        self.assertDictEqual({}, volumes)
+        self.assertDictEqual({}, snapshots)
+
+        # Create two volumes
+        self.driver.create_volume({'id': '1', 'name': 'v1', 'size': 1,
+                                   'volume_type': None})
+        self.driver.create_volume({'id': '2', 'name': 'v2', 'size': 1,
+                                   'volume_type': None})
+        self.assertListEqual([volumeName('1'), volumeName('2')],
+                             volumes.keys())
+        self.assertVolumeNames(('1', '2',))
+
+        # Failure: the "migrated" volume does not even exist
+        res = self.driver.update_migrated_volume(None, {'id': '1'},
+                                                 {'id': '3', '_name_id': '1'},
+                                                 'available')
+        self.assertDictEqual({'_name_id': '1'}, res)
+
+        # Failure: a volume with the original volume's name already exists
+        res = self.driver.update_migrated_volume(None, {'id': '1'},
+                                                 {'id': '2', '_name_id': '1'},
+                                                 'available')
+        self.assertDictEqual({'_name_id': '1'}, res)
+
+        # Success: rename the migrated volume to match the original
+        res = self.driver.update_migrated_volume(None, {'id': '3'},
+                                                 {'id': '2', '_name_id': '3'},
+                                                 'available')
+        self.assertDictEqual({'_name_id': None}, res)
+        self.assertListEqual([volumeName('1'), volumeName('3')],
+                             volumes.keys())
+        self.assertVolumeNames(('1', '3',))
+
+        for vid in ('1', '3'):
+            self.driver.delete_volume({'id': vid})
+        self.assertVolumeNames([])
+        self.assertDictEqual({}, volumes)
+        self.assertDictEqual({}, snapshots)
+
     def test_clone_extend_volume(self):
         self.assertVolumeNames([])
         self.assertDictEqual({}, volumes)
index 7653dc54907140917f7bb600fe7cf8bdbb08bbfe..5ccceab979b21a58a9f84000c3c0f7ea652d0593 100644 (file)
@@ -2998,6 +2998,35 @@ class StorwizeSVCDriverTestCase(test.TestCase):
             self.assertEqual((7, 2, 0, 0), res['code_level'],
                              'Get code level error')
 
+    @mock.patch.object(helpers.StorwizeHelpers, 'rename_vdisk')
+    def test_storwize_update_migrated_volume(self, rename_vdisk):
+        ctxt = testutils.get_test_admin_context()
+        current_volume_id = 'fake_volume_id'
+        original_volume_id = 'fake_original_volume_id'
+        current_name = 'volume-' + current_volume_id
+        original_name = 'volume-' + original_volume_id
+        backend_volume = self._create_volume(id=current_volume_id)
+        volume = self._create_volume(id=original_volume_id)
+        model_update = self.driver.update_migrated_volume(ctxt, volume,
+                                                          backend_volume,
+                                                          'available')
+        rename_vdisk.assert_called_once_with(current_name, original_name)
+        self.assertEqual({'_name_id': None}, model_update)
+
+        rename_vdisk.reset_mock()
+        rename_vdisk.side_effect = exception.VolumeBackendAPIException
+        model_update = self.driver.update_migrated_volume(ctxt, volume,
+                                                          backend_volume,
+                                                          'available')
+        self.assertEqual({'_name_id': current_volume_id}, model_update)
+
+        rename_vdisk.reset_mock()
+        rename_vdisk.side_effect = exception.VolumeBackendAPIException
+        model_update = self.driver.update_migrated_volume(ctxt, volume,
+                                                          backend_volume,
+                                                          'attached')
+        self.assertEqual({'_name_id': current_volume_id}, model_update)
+
     def test_storwize_vdisk_copy_ops(self):
         ctxt = testutils.get_test_admin_context()
         volume = self._create_volume()
index e61331fcad49b32d67222fe3c7a9b6797eab6443..26bd8fe4a35a2b1056a14ae1aa4eadf1f5715f17 100644 (file)
@@ -3917,6 +3917,46 @@ class VolumeTestCase(BaseVolumeTestCase):
         self.volume.delete_volume(self.context, volume_dst['id'])
         self.volume.delete_volume(self.context, volume_src['id'])
 
+    @mock.patch('cinder.db.volume_update')
+    def test_update_migrated_volume(self, volume_update):
+        fake_host = 'fake_host'
+        fake_new_host = 'fake_new_host'
+        fake_update = {'_name_id': None, 'provider_location': None}
+        fake_elevated = 'fake_elevated'
+        volume = tests_utils.create_volume(self.context, size=1,
+                                           status='available',
+                                           host=fake_host)
+        new_volume = tests_utils.create_volume(self.context, size=1,
+                                               status='available',
+                                               host=fake_new_host)
+        new_volume['_name_id'] = 'fake_name_id'
+        new_volume['provider_location'] = 'fake_provider_location'
+        fake_update_error = {'_name_id': new_volume['_name_id'],
+                             'provider_location':
+                             new_volume['provider_location']}
+        with mock.patch.object(self.volume.driver,
+                               'update_migrated_volume') as \
+                migrate_update,\
+                mock.patch.object(self.context, 'elevated') as elevated:
+            migrate_update.return_value = fake_update
+            elevated.return_value = fake_elevated
+            self.volume.update_migrated_volume(self.context, volume,
+                                               new_volume, 'available')
+            volume_update.assert_called_once_with(fake_elevated,
+                                                  volume['id'],
+                                                  fake_update)
+
+            # Test the case for update_migrated_volume not implemented
+            # for the driver.
+            migrate_update.reset_mock()
+            volume_update.reset_mock()
+            migrate_update.side_effect = NotImplementedError
+            self.volume.update_migrated_volume(self.context, volume,
+                                               new_volume, 'available')
+            volume_update.assert_called_once_with(fake_elevated,
+                                                  volume['id'],
+                                                  fake_update_error)
+
     def test_list_availability_zones_enabled_service(self):
         services = [
             {'availability_zone': 'ping', 'disabled': 0},
@@ -5868,6 +5908,43 @@ class LVMVolumeDriverTestCase(DriverTestCase):
 
         mock_volume_get.assert_called_with(self.context, vol['id'])
 
+    def test_update_migrated_volume(self):
+        fake_volume_id = 'vol1'
+        fake_new_volume_id = 'vol2'
+        fake_provider = 'fake_provider'
+        original_volume_name = CONF.volume_name_template % fake_volume_id
+        current_name = CONF.volume_name_template % fake_new_volume_id
+        fake_volume = tests_utils.create_volume(self.context)
+        fake_volume['id'] = fake_volume_id
+        fake_new_volume = tests_utils.create_volume(self.context)
+        fake_new_volume['id'] = fake_new_volume_id
+        fake_new_volume['provider_location'] = fake_provider
+        fake_vg = fake_lvm.FakeBrickLVM('cinder-volumes', False,
+                                        None, 'default')
+        with mock.patch.object(self.volume.driver, 'vg') as vg:
+            vg.return_value = fake_vg
+            vg.rename_volume.return_value = None
+            update = self.volume.driver.update_migrated_volume(self.context,
+                                                               fake_volume,
+                                                               fake_new_volume,
+                                                               'available')
+            vg.rename_volume.assert_called_once_with(current_name,
+                                                     original_volume_name)
+            self.assertEqual({'_name_id': None,
+                              'provider_location': None}, update)
+
+            vg.rename_volume.reset_mock()
+            vg.rename_volume.side_effect = processutils.ProcessExecutionError
+            update = self.volume.driver.update_migrated_volume(self.context,
+                                                               fake_volume,
+                                                               fake_new_volume,
+                                                               'available')
+            vg.rename_volume.assert_called_once_with(current_name,
+                                                     original_volume_name)
+            self.assertEqual({'_name_id': fake_new_volume_id,
+                              'provider_location': fake_provider},
+                             update)
+
 
 class ISCSITestCase(DriverTestCase):
     """Test Case for ISCSIDriver"""
index 61142ffa033b86b1ea06675dae33c330ca99af93..ba5c52a2b5ad61ce65606e436c4c8b48e94084cf 100644 (file)
@@ -1358,15 +1358,24 @@ class VolumeDriver(ConsistencyGroupVD, TransferVD, ManageableVD, ExtendVD,
         """
         return None
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         """Return model update for migrated volume.
 
+        Each driver implementing this method needs to be responsible for the
+        values of _name_id and provider_location. If None is returned or either
+        key is not set, it means the volume table does not need to change the
+        value(s) for the key(s).
+        The return format is {"_name_id": value, "provider_location": value}.
+
         :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
+        :param original_volume_status: The status of the original volume
         :return model_update to update DB with any needed changes
         """
-        return None
+        msg = _("The method update_migrated_volume not implemented.")
+        raise NotImplementedError(msg)
 
     def migrate_volume(self, context, volume, host):
         return (False, None)
index b259adba8e566d8680c22fe652bf9b60de9934eb..adec896c5614ef3bc9b97a99add46cb7f2eb8492 100644 (file)
@@ -343,12 +343,14 @@ class DellCommonDriver(san.SanDriver):
                       {'total': data['total_capacity_gb'],
                        'free': data['free_capacity_gb']})
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         '''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
+        :param original_volume_status: The status of the original volume
         :return model_update to update DB with any needed changes
         '''
         # We use id as our volume name so we need to rename the backend
@@ -369,4 +371,4 @@ class DellCommonDriver(san.SanDriver):
         # The world was horrible to us so we should error and leave.
         LOG.error(_LE('Unable to rename the logical volume for volume: %s'),
                   original_volume_name)
-        return None
+        return {'_name_id': new_volume['_name_id'] or new_volume['id']}
index 741fe598a4067b5f39d63e7493e7cfde3980d809..39165bbe56a0fc00e8841422bde650d59dee53ee 100644 (file)
@@ -945,6 +945,37 @@ class StorwizeSVCDriver(san.SanDriver):
                                                    'host': host['host']})
         return True, model_update
 
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
+        """Return model update from Storwize for migrated volume.
+
+        This method should rename the back-end volume name(id) on the
+        destination host back to its original name(id) on the source host.
+
+        :param ctxt: The context used to run the method update_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
+        :param original_volume_status: The status of the original volume
+        :return model_update to update DB with any needed changes
+        """
+        current_name = CONF.volume_name_template % new_volume['id']
+        original_volume_name = CONF.volume_name_template % volume['id']
+        try:
+            self._helpers.rename_vdisk(current_name, original_volume_name)
+        except exception.VolumeBackendAPIException:
+            LOG.error(_LE('Unable to rename the logical volume '
+                          'for volume: %s'), volume['id'])
+            return {'_name_id': new_volume['_name_id'] or new_volume['id']}
+        # If the back-end name(id) for the volume has been renamed,
+        # it is OK for the volume to keep the original name(id) and there is
+        # no need to use the column "_name_id" to establish the mapping
+        # relationship between the volume id and the back-end volume
+        # name(id).
+        # Set the key "_name_id" to None for a successful rename.
+        model_update = {'_name_id': None}
+        return model_update
+
     def manage_existing(self, volume, ref):
         """Manages an existing vdisk.
 
index 59502826a8bc50547b34c645539522abc2d5f3fd..1bc27ee02051cf40cf8395fee8a4b2b9fd9d0fc1 100644 (file)
@@ -1904,7 +1904,8 @@ class InfortrendCommon(object):
                 'volume_id': volume['id']})
             return True
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         """Return model update for migrated volume."""
 
         src_volume_id = volume['id'].replace('-', '')
@@ -1919,13 +1920,19 @@ class InfortrendCommon(object):
             'Rename partition %(part_id)s '
             'into new volume %(new_volume)s.', {
                 'part_id': part_id, 'new_volume': dst_volume_id})
-
-        self._execute('SetPartition', part_id, 'name=%s' % src_volume_id)
+        try:
+            self._execute('SetPartition', part_id, 'name=%s' % src_volume_id)
+        except exception.InfortrendCliException:
+            LOG.exception(_LE('Failed to rename %(new_volume)s into '
+                              '%(volume)s.'), {'new_volume': new_volume['id'],
+                                               'volume': volume['id']})
+            return {'_name_id': new_volume['_name_id'] or new_volume['id']}
 
         LOG.info(_LI('Update migrated volume %(new_volume)s completed.'), {
             'new_volume': new_volume['id']})
 
         model_update = {
+            '_name_id': None,
             'provider_location': new_volume['provider_location'],
         }
         return model_update
index 42514a6db41080bc63b7d1ff0ecf39219e03c252..33f4c691d0b84396629e530a4b52508508fcc5b5 100644 (file)
@@ -262,16 +262,19 @@ class InfortrendCLIFCDriver(driver.FibreChannelDriver):
                 'volume_id': volume['id'], 'type_id': new_type['id']})
         return self.common.retype(ctxt, volume, new_type, diff, host)
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         """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
+        :param original_volume_status: The status of the original volume
         :return model_update to update DB with any needed changes
         """
         LOG.debug(
             'update migrated volume original volume id= %(volume_id)s '
             'new volume id=%(new_volume_id)s', {
                 'volume_id': volume['id'], 'new_volume_id': new_volume['id']})
-        return self.common.update_migrated_volume(ctxt, volume, new_volume)
+        return self.common.update_migrated_volume(ctxt, volume, new_volume,
+                                                  original_volume_status)
index 6c86228712b3ff2a85d78a2821728bd4ce906417..293c191782c80a2472d5fb30011e8238ae66f53d 100644 (file)
@@ -234,16 +234,19 @@ class InfortrendCLIISCSIDriver(driver.ISCSIDriver):
                 'volume_id': volume['id'], 'type_id': new_type['id']})
         return self.common.retype(ctxt, volume, new_type, diff, host)
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         """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
+        :param original_volume_status: The status of the original volume
         :return model_update to update DB with any needed changes
         """
         LOG.debug(
             'update migrated volume original volume id= %(volume_id)s '
             'new volume id=%(new_volume_id)s', {
                 'volume_id': volume['id'], 'new_volume_id': new_volume['id']})
-        return self.common.update_migrated_volume(ctxt, volume, new_volume)
+        return self.common.update_migrated_volume(ctxt, volume, new_volume,
+                                                  original_volume_status)
index b01c9bbb6fb1dbf0409b4eb9aaf8af6bcbb4f8e3..3c57b632bd203b0de079ba84cf07c7fe67fe4f9f 100644 (file)
@@ -306,6 +306,41 @@ class LVMVolumeDriver(driver.VolumeDriver):
                             self.configuration.lvm_type,
                             mirror_count)
 
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
+        """Return model update from LVM for migrated volume.
+
+        This method should rename the back-end volume name(id) on the
+        destination host back to its original name(id) on the source host.
+
+        :param ctxt: The context used to run the method update_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
+        :param original_volume_status: The status of the original volume
+        :return model_update to update DB with any needed changes
+        """
+        name_id = None
+        provider_location = None
+        if original_volume_status == 'available':
+            current_name = CONF.volume_name_template % new_volume['id']
+            original_volume_name = CONF.volume_name_template % volume['id']
+            try:
+                self.vg.rename_volume(current_name, original_volume_name)
+            except processutils.ProcessExecutionError:
+                LOG.error(_LE('Unable to rename the logical volume '
+                              'for volume: %s'), volume['name'])
+                # If the rename fails, _name_id should be set to the new
+                # volume id and provider_location should be set to the
+                # one from the new volume as well.
+                name_id = new_volume['_name_id'] or new_volume['id']
+                provider_location = new_volume['provider_location']
+        else:
+            # The back-end will not be renamed.
+            name_id = new_volume['_name_id'] or new_volume['id']
+            provider_location = new_volume['provider_location']
+        return {'_name_id': name_id, 'provider_location': provider_location}
+
     def create_volume_from_snapshot(self, volume, snapshot):
         """Creates a volume from a snapshot."""
         self._create_volume(volume['name'],
index 78a4e351cbb0223ffece1f55280e45fc091665bc..d69feaf88fbff363c78f3c3bd5f6423f2625848c 100644 (file)
@@ -434,7 +434,8 @@ class StorPoolDriver(driver.TransferVD, driver.ExtendVD, driver.CloneableVD,
 
         return True
 
-    def update_migrated_volume(self, context, volume, new_volume):
+    def update_migrated_volume(self, context, volume, new_volume,
+                               original_volume_status):
         orig_id = volume['id']
         orig_name = self._attach.volumeName(orig_id)
         temp_id = new_volume['id']
@@ -442,14 +443,25 @@ class StorPoolDriver(driver.TransferVD, driver.ExtendVD, driver.CloneableVD,
         vols = {v.name: True for v in self._attach.api().volumesList()}
         if temp_name not in vols:
             LOG.error(_LE('StorPool update_migrated_volume(): it seems '
-                          'that the StorPool volume "%(tid)" was not '
+                          'that the StorPool volume "%(tid)s" was not '
                           'created as part of the migration from '
-                          '"%(oid)"'), {'tid': temp_id, 'oid': orig_id})
+                          '"%(oid)s"'), {'tid': temp_id, 'oid': orig_id})
+            return {'_name_id': new_volume['_name_id'] or new_volume['id']}
         elif orig_name in vols:
             LOG.error(_LE('StorPool update_migrated_volume(): both '
-                          'the original volume "%(oid)" and the migrated '
-                          'StorPool volume "%(tid)" seem to exist on '
+                          'the original volume "%(oid)s" and the migrated '
+                          'StorPool volume "%(tid)s" seem to exist on '
                           'the StorPool cluster'),
                       {'oid': orig_id, 'tid': temp_id})
+            return {'_name_id': new_volume['_name_id'] or new_volume['id']}
         else:
-            self._attach.api().volumeUpdate(temp_name, {'rename': orig_name})
+            try:
+                self._attach.api().volumeUpdate(temp_name,
+                                                {'rename': orig_name})
+                return {'_name_id': None}
+            except spapi.ApiError as e:
+                LOG.error(_LE('StorPool update_migrated_volume(): '
+                              'could not rename %(tname)s to %(oname)s: '
+                              '%(err)s'),
+                          {'tname': temp_name, 'oname': orig_name, 'err': e})
+                return {'_name_id': new_volume['_name_id'] or new_volume['id']}
index e77ecca0e314d065865a48a7a9d82ad544e0a99b..d3b34533420d00597aafaed44b63b8162ee67bc0 100644 (file)
@@ -1374,11 +1374,11 @@ class VolumeManager(manager.SchedulerDependentManager):
                       {'err': ex}, resource=volume)
 
         # Give driver (new_volume) a chance to update things as needed
+        # after a successful migration.
         # Note this needs to go through rpc to the host of the new volume
-        # the current host and driver object is for the "existing" volume
-        rpcapi.update_migrated_volume(ctxt,
-                                      volume,
-                                      new_volume)
+        # the current host and driver object is for the "existing" volume.
+        rpcapi.update_migrated_volume(ctxt, volume, new_volume,
+                                      orig_volume_status)
         self.db.finish_volume_migration(ctxt, volume_id, new_volume_id)
         self.db.volume_destroy(ctxt, new_volume_id)
         if orig_volume_status == 'in-use':
@@ -2580,14 +2580,23 @@ class VolumeManager(manager.SchedulerDependentManager):
 
         return True
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               volume_status):
         """Finalize migration process on backend device."""
-
         model_update = None
-        model_update = self.driver.update_migrated_volume(ctxt,
-                                                          volume,
-                                                          new_volume)
+        try:
+            model_update = self.driver.update_migrated_volume(ctxt,
+                                                              volume,
+                                                              new_volume,
+                                                              volume_status)
+        except NotImplementedError:
+            # If update_migrated_volume is not implemented for the driver,
+            # _name_id and provider_location will be set with the values
+            # from new_volume.
+            model_update = {'_name_id': new_volume['_name_id'] or
+                            new_volume['id'],
+                            'provider_location':
+                            new_volume['provider_location']}
         if model_update:
-            self.db.volume_update(ctxt.elevated(),
-                                  volume['id'],
+            self.db.volume_update(ctxt.elevated(), volume['id'],
                                   model_update)
index 68944ca26fb7d93c0804581c2a4e66a7298bfc10..a8212ecead71fd538cac1f04d024e6562b610288 100644 (file)
@@ -255,10 +255,12 @@ class VolumeAPI(object):
         cctxt = self.client.prepare(server=new_host, version='1.17')
         cctxt.cast(ctxt, 'reenable_replication', volume_id=volume['id'])
 
-    def update_migrated_volume(self, ctxt, volume, new_volume):
+    def update_migrated_volume(self, ctxt, volume, new_volume,
+                               original_volume_status):
         host = utils.extract_host(new_volume['host'])
         cctxt = self.client.prepare(server=host, version='1.19')
         cctxt.call(ctxt,
                    'update_migrated_volume',
                    volume=volume,
-                   new_volume=new_volume)
+                   new_volume=new_volume,
+                   volume_status=original_volume_status)