]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
VNX: Allow set migrate rate when migrating volumes
authorpeter_wang <wangxu198709@gmail.com>
Mon, 28 Dec 2015 05:56:18 +0000 (13:56 +0800)
committerpeter_wang <peter.wang13@emc.com>
Mon, 14 Mar 2016 02:11:01 +0000 (22:11 -0400)
VNX driver is leveraging LUN migration functionality when
clone/migrate/retype volume. The migration rate is set to 'high'
by hard code.

This patch will allow admin to control the migration rate by
adding volume metadata before triggering any operations involving
LUN migration.

The required metadata key is 'migrate_rate', available metadata
values are 'high', 'asap', 'low' and 'medium'.

DocImpact
Change-Id: Ie9a875dd63dd60351e46a66c99977d9b6fd23244
Closes-Bug: 1529553

cinder/tests/unit/test_emc_vnx.py
cinder/volume/drivers/emc/emc_cli_fc.py
cinder/volume/drivers/emc/emc_cli_iscsi.py
cinder/volume/drivers/emc/emc_vnx_cli.py
releasenotes/notes/vnx-configurable-migration-rate-5e0a2235777c314f.yaml [new file with mode: 0644]

index 61644d3409de7c78979566ae85d629546e3ececa..38046cb89220f7899b11003f4cc59c361a580980 100644 (file)
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+import ddt
 import json
 import os
 import re
@@ -707,9 +708,9 @@ class EMCVNXCLIDriverTestData(object):
                 '-initialTier', tier,
                 '-tieringPolicy', policy)
 
-    def MIGRATION_CMD(self, src_id=1, dest_id=1):
+    def MIGRATION_CMD(self, src_id=1, dest_id=1, rate='high'):
         cmd = ("migrate", "-start", "-source", src_id, "-dest", dest_id,
-               "-rate", "high", "-o")
+               "-rate", rate, "-o")
         return cmd
 
     def MIGRATION_VERIFY_CMD(self, src_id):
@@ -1621,6 +1622,7 @@ class DriverTestCaseBase(test.TestCase):
         return _safe_get
 
 
+@ddt.ddt
 class EMCVNXCLIDriverISCSITestCase(DriverTestCaseBase):
     def generate_driver(self, conf):
         return emc_cli_iscsi.EMCCLIISCSIDriver(configuration=conf)
@@ -2018,7 +2020,7 @@ Time Remaining:  0 second(s)
                     23)]]
         fake_cli = self.driverSetup(commands, results)
         fakehost = {'capabilities': {'location_info':
-                                     "unit_test_pool2|fake_serial",
+                                     'unit_test_pool2|fake_serial',
                                      'storage_protocol': 'iSCSI'}}
         ret = self.driver.migrate_volume(None, self.testData.test_volume,
                                          fakehost)[0]
@@ -2063,7 +2065,7 @@ Time Remaining:  0 second(s)
                      'currently migrating', 23)]]
         fake_cli = self.driverSetup(commands, results)
         fake_host = {'capabilities': {'location_info':
-                                      "unit_test_pool2|fake_serial",
+                                      'unit_test_pool2|fake_serial',
                                       'storage_protocol': 'iSCSI'}}
         ret = self.driver.migrate_volume(None, self.testData.test_volume,
                                          fake_host)[0]
@@ -2078,6 +2080,51 @@ Time Remaining:  0 second(s)
                                 poll=False)]
         fake_cli.assert_has_calls(expect_cmd)
 
+    @mock.patch("cinder.volume.drivers.emc.emc_vnx_cli."
+                "CommandLineHelper.create_lun_by_cmd",
+                mock.Mock(
+                    return_value={'lun_id': 1}))
+    @mock.patch(
+        "cinder.volume.drivers.emc.emc_vnx_cli.EMCVnxCliBase.get_lun_id",
+        mock.Mock(
+            side_effect=[1, 1]))
+    def test_volume_migration_with_rate(self):
+
+        test_volume_asap = self.testData.test_volume.copy()
+        test_volume_asap.update({'metadata': {'migrate_rate': 'asap'}})
+        commands = [self.testData.MIGRATION_CMD(rate="asap"),
+                    self.testData.MIGRATION_VERIFY_CMD(1)]
+        FAKE_MIGRATE_PROPERTY = """\
+Source LU Name:  volume-f6247ae1-8e1c-4927-aa7e-7f8e272e5c3d
+Source LU ID:  63950
+Dest LU Name:  volume-f6247ae1-8e1c-4927-aa7e-7f8e272e5c3d_dest
+Dest LU ID:  136
+Migration Rate:  ASAP
+Current State:  MIGRATED
+Percent Complete:  100
+Time Remaining:  0 second(s)
+"""
+        results = [SUCCEED,
+                   [(FAKE_MIGRATE_PROPERTY, 0),
+                    ('The specified source LUN is not '
+                     'currently migrating', 23)]]
+        fake_cli = self.driverSetup(commands, results)
+        fake_host = {'capabilities': {'location_info':
+                                      'unit_test_pool2|fake_serial',
+                                      'storage_protocol': 'iSCSI'}}
+        ret = self.driver.migrate_volume(None, test_volume_asap,
+                                         fake_host)[0]
+        self.assertTrue(ret)
+        # verification
+        expect_cmd = [mock.call(*self.testData.MIGRATION_CMD(rate='asap'),
+                                retry_disable=True,
+                                poll=True),
+                      mock.call(*self.testData.MIGRATION_VERIFY_CMD(1),
+                                poll=True),
+                      mock.call(*self.testData.MIGRATION_VERIFY_CMD(1),
+                                poll=False)]
+        fake_cli.assert_has_calls(expect_cmd)
+
     @mock.patch("cinder.volume.drivers.emc.emc_vnx_cli."
                 "CommandLineHelper.create_lun_by_cmd",
                 mock.Mock(
@@ -2106,7 +2153,7 @@ Time Remaining:  0 second(s)
                      23)]]
         fake_cli = self.driverSetup(commands, results)
         fakehost = {'capabilities': {'location_info':
-                                     "unit_test_pool2|fake_serial",
+                                     'unit_test_pool2|fake_serial',
                                      'storage_protocol': 'iSCSI'}}
         ret = self.driver.migrate_volume(None, self.testData.test_volume5,
                                          fakehost)[0]
@@ -2134,7 +2181,7 @@ Time Remaining:  0 second(s)
         results = [FAKE_ERROR_RETURN]
         fake_cli = self.driverSetup(commands, results)
         fakehost = {'capabilities': {'location_info':
-                                     "unit_test_pool2|fake_serial",
+                                     'unit_test_pool2|fake_serial',
                                      'storage_protocol': 'iSCSI'}}
         ret = self.driver.migrate_volume(None, self.testData.test_volume,
                                          fakehost)[0]
@@ -2166,7 +2213,7 @@ Time Remaining:  0 second(s)
                    SUCCEED]
         fake_cli = self.driverSetup(commands, results)
         fake_host = {'capabilities': {'location_info':
-                                      "unit_test_pool2|fake_serial",
+                                      'unit_test_pool2|fake_serial',
                                       'storage_protocol': 'iSCSI'}}
 
         self.assertRaisesRegex(exception.VolumeBackendAPIException,
@@ -2219,7 +2266,7 @@ Time Remaining:  0 second(s)
                      'currently migrating', 23)]]
         fake_cli = self.driverSetup(commands, results)
         fake_host = {'capabilities': {'location_info':
-                                      "unit_test_pool2|fake_serial",
+                                      'unit_test_pool2|fake_serial',
                                       'storage_protocol': 'iSCSI'}}
 
         vol = EMCVNXCLIDriverTestData.convert_volume(
@@ -2744,18 +2791,20 @@ Time Remaining:  0 second(s)
 
         fake_cli.assert_has_calls(expect_cmd)
 
-    def test_create_volume_from_snapshot(self):
+    @ddt.data('high', 'asap', 'low', 'medium')
+    def test_create_volume_from_snapshot(self, migrate_rate):
         test_snapshot = EMCVNXCLIDriverTestData.convert_snapshot(
             self.testData.test_snapshot)
         test_volume = EMCVNXCLIDriverTestData.convert_volume(
             self.testData.test_volume2)
+        test_volume.metadata = {'migrate_rate': migrate_rate}
         cmd_dest = self.testData.LUN_PROPERTY_ALL_CMD(
             build_migration_dest_name(test_volume.name))
         cmd_dest_np = self.testData.LUN_PROPERTY_ALL_CMD(
             build_migration_dest_name(test_volume.name))
         output_dest = self.testData.LUN_PROPERTY(
             build_migration_dest_name(test_volume.name))
-        cmd_migrate = self.testData.MIGRATION_CMD(1, 1)
+        cmd_migrate = self.testData.MIGRATION_CMD(1, 1, rate=migrate_rate)
         output_migrate = ("", 0)
         cmd_migrate_verify = self.testData.MIGRATION_VERIFY_CMD(1)
         output_migrate_verify = (r'The specified source LUN '
@@ -2785,7 +2834,7 @@ Time Remaining:  0 second(s)
             mock.call(*self.testData.LUN_PROPERTY_ALL_CMD(
                       build_migration_dest_name(test_volume.name)),
                       poll=False),
-            mock.call(*self.testData.MIGRATION_CMD(1, 1),
+            mock.call(*self.testData.MIGRATION_CMD(1, 1, rate=migrate_rate),
                       retry_disable=True,
                       poll=True),
             mock.call(*self.testData.MIGRATION_VERIFY_CMD(1),
@@ -2945,7 +2994,8 @@ Time Remaining:  0 second(s)
             mock.call(*self.testData.LUN_DELETE_CMD('volume-2'))]
         fake_cli.assert_has_calls(expect_cmd)
 
-    def test_create_cloned_volume(self):
+    @ddt.data('high', 'asap', 'low', 'medium')
+    def test_create_cloned_volume(self, migrate_rate):
         cmd_dest = self.testData.LUN_PROPERTY_ALL_CMD(
             build_migration_dest_name('volume-2'))
         cmd_dest_p = self.testData.LUN_PROPERTY_ALL_CMD(
@@ -2954,7 +3004,7 @@ Time Remaining:  0 second(s)
             build_migration_dest_name('volume-2'))
         cmd_clone = self.testData.LUN_PROPERTY_ALL_CMD("volume-2")
         output_clone = self.testData.LUN_PROPERTY("volume-2")
-        cmd_migrate = self.testData.MIGRATION_CMD(1, 1)
+        cmd_migrate = self.testData.MIGRATION_CMD(1, 1, rate=migrate_rate)
         output_migrate = ("", 0)
         cmd_migrate_verify = self.testData.MIGRATION_VERIFY_CMD(1)
         output_migrate_verify = (r'The specified source LUN '
@@ -2970,6 +3020,7 @@ Time Remaining:  0 second(s)
         volume = EMCVNXCLIDriverTestData.convert_volume(volume)
         # Make sure this size is used
         volume.size = 10
+        volume.metadata = {'migrate_rate': migrate_rate}
         self.driver.create_cloned_volume(volume, self.testData.test_volume)
         tmp_snap = 'tmp-snap-' + volume.id
         expect_cmd = [
@@ -2990,7 +3041,7 @@ Time Remaining:  0 second(s)
                 build_migration_dest_name('volume-2')), poll=False),
             mock.call(*self.testData.LUN_PROPERTY_ALL_CMD(
                 build_migration_dest_name('volume-2')), poll=False),
-            mock.call(*self.testData.MIGRATION_CMD(1, 1),
+            mock.call(*self.testData.MIGRATION_CMD(1, 1, rate=migrate_rate),
                       poll=True,
                       retry_disable=True),
             mock.call(*self.testData.MIGRATION_VERIFY_CMD(1),
index 2d32e9dcd56d5c10c9ffda0cd0d509d71e045812..ee0e2c9a766c04fa35039734d867dadb762096c1 100644 (file)
@@ -60,6 +60,7 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
                 Support efficient non-disruptive backup
         7.0.0 - Clone consistency group support
                 Replication v2 support(managed)
+                Configurable migration rate support
     """
 
     def __init__(self, *args, **kwargs):
index c495cf7bc6a0e08ddda953ac68c8dfc12c5a3b62..0e1a062cf0b85c5e347f90a05ec900e5cdce9636 100644 (file)
@@ -58,6 +58,7 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
                 Support efficient non-disruptive backup
         7.0.0 - Clone consistency group support
                 Replication v2 support(managed)
+                Configurable migration rate support
     """
 
     def __init__(self, *args, **kwargs):
index 346e9cca8488be609f52a0edcbbda49ab2631218..ff08f9d2adeb9ab42e9afa74ab698d92302f89d6 100644 (file)
@@ -272,6 +272,13 @@ class VNXError(_Enum):
                     for error_code in error_codes])
 
 
+class VNXMigrationRate(_Enum):
+    LOW = 'low'
+    MEDIUM = 'medium'
+    HIGH = 'high'
+    ASAP = 'asap'
+
+
 class VNXProvisionEnum(_Enum):
     THIN = 'thin'
     THICK = 'thick'
@@ -1269,11 +1276,11 @@ class CommandLineHelper(object):
 
         return rc
 
-    def migrate_lun(self, src_id, dst_id):
+    def migrate_lun(self, src_id, dst_id, rate=VNXMigrationRate.HIGH):
         command_migrate_lun = ('migrate', '-start',
                                '-source', src_id,
                                '-dest', dst_id,
-                               '-rate', 'high',
+                               '-rate', rate,
                                '-o')
         # SP HA is not supported by LUN migration
         out, rc = self.command_execute(*command_migrate_lun,
@@ -1286,9 +1293,10 @@ class CommandLineHelper(object):
         return rc
 
     def migrate_lun_without_verification(self, src_id, dst_id,
-                                         dst_name=None):
+                                         dst_name=None,
+                                         rate=VNXMigrationRate.HIGH):
         try:
-            self.migrate_lun(src_id, dst_id)
+            self.migrate_lun(src_id, dst_id, rate)
             return True
         except exception.EMCVnxCLICmdError as ex:
             migration_succeed = False
@@ -1398,9 +1406,10 @@ class CommandLineHelper(object):
 
     def migrate_lun_with_verification(self, src_id,
                                       dst_id,
-                                      dst_name=None):
+                                      dst_name=None,
+                                      rate=VNXMigrationRate.HIGH):
         migration_started = self.migrate_lun_without_verification(
-            src_id, dst_id, dst_name)
+            src_id, dst_id, dst_name, rate)
         if not migration_started:
             return False
 
@@ -2368,6 +2377,18 @@ class EMCVnxCliBase(object):
         specs = self.get_volumetype_extraspecs(volume)
         self._get_and_validate_extra_specs(specs)
 
+    def _get_migration_rate(self, volume):
+        metadata = self._get_volume_metadata(volume)
+        rate = metadata.get('migrate_rate', VNXMigrationRate.HIGH)
+        if rate:
+            if rate.lower() in VNXMigrationRate.get_all():
+                return rate.lower()
+            else:
+                LOG.warning(_LW('Unknown migration rate specified, '
+                                'using [high] as migration rate.'))
+
+        return VNXMigrationRate.HIGH
+
     def _get_and_validate_extra_specs(self, specs):
         """Checks on extra specs combinations."""
         if "storagetype:pool" in specs:
@@ -2614,7 +2635,8 @@ class EMCVnxCliBase(object):
 
         dst_id = data['lun_id']
         moved = self._client.migrate_lun_with_verification(
-            src_id, dst_id, new_volume_name)
+            src_id, dst_id, new_volume_name,
+            rate=self._get_migration_rate(volume))
 
         lun_type = self._extract_provider_location(
             volume['provider_location'], 'type')
@@ -2929,6 +2951,7 @@ class EMCVnxCliBase(object):
                 new_lun_id, 'smp', base_lun_name)
             volume_metadata['snapcopy'] = 'True'
         else:
+            store_spec.update({'rate': self._get_migration_rate(volume)})
             work_flow.add(CreateSMPTask(),
                           AttachSnapTask(),
                           CreateDestLunTask(),
@@ -3005,6 +3028,7 @@ class EMCVnxCliBase(object):
                 new_lun_id, 'smp', base_lun_name)
         else:
             # snapcopy feature disabled, need to migrate
+            store_spec.update({'rate': self._get_migration_rate(volume)})
             work_flow.add(CreateSnapshotTask(),
                           CreateSMPTask(),
                           AttachSnapTask(),
@@ -4480,15 +4504,16 @@ class MigrateLunTask(task.Task):
                                              rebind=rebind)
         self.wait_for_completion = wait_for_completion
 
-    def execute(self, client, new_smp_id, lun_data, *args, **kwargs):
+    def execute(self, client, new_smp_id, lun_data, rate=VNXMigrationRate.HIGH,
+                *args, **kwargs):
         LOG.debug('MigrateLunTask.execute')
         dest_vol_lun_id = lun_data['lun_id']
-
         LOG.debug('Migrating Mount Point Volume ID: %s', new_smp_id)
         if self.wait_for_completion:
             migrated = client.migrate_lun_with_verification(new_smp_id,
                                                             dest_vol_lun_id,
-                                                            None)
+                                                            None,
+                                                            rate)
         else:
             migrated = client.migrate_lun_without_verification(
                 new_smp_id, dest_vol_lun_id, None)
diff --git a/releasenotes/notes/vnx-configurable-migration-rate-5e0a2235777c314f.yaml b/releasenotes/notes/vnx-configurable-migration-rate-5e0a2235777c314f.yaml
new file mode 100644 (file)
index 0000000..8c1d3b3
--- /dev/null
@@ -0,0 +1,3 @@
+---
+features:
+  - Configrable migration rate in VNX driver via metadata