diff --git a/cinder/tests/unit/test_emc_vnx.py b/cinder/tests/unit/test_emc_vnx.py index 61644d3409d..38046cb8922 100644 --- a/cinder/tests/unit/test_emc_vnx.py +++ b/cinder/tests/unit/test_emc_vnx.py @@ -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), diff --git a/cinder/volume/drivers/emc/emc_cli_fc.py b/cinder/volume/drivers/emc/emc_cli_fc.py index 2d32e9dcd56..ee0e2c9a766 100644 --- a/cinder/volume/drivers/emc/emc_cli_fc.py +++ b/cinder/volume/drivers/emc/emc_cli_fc.py @@ -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): diff --git a/cinder/volume/drivers/emc/emc_cli_iscsi.py b/cinder/volume/drivers/emc/emc_cli_iscsi.py index c495cf7bc6a..0e1a062cf0b 100644 --- a/cinder/volume/drivers/emc/emc_cli_iscsi.py +++ b/cinder/volume/drivers/emc/emc_cli_iscsi.py @@ -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): diff --git a/cinder/volume/drivers/emc/emc_vnx_cli.py b/cinder/volume/drivers/emc/emc_vnx_cli.py index 346e9cca848..ff08f9d2ade 100644 --- a/cinder/volume/drivers/emc/emc_vnx_cli.py +++ b/cinder/volume/drivers/emc/emc_vnx_cli.py @@ -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 index 00000000000..8c1d3b35d37 --- /dev/null +++ b/releasenotes/notes/vnx-configurable-migration-rate-5e0a2235777c314f.yaml @@ -0,0 +1,3 @@ +--- +features: + - Configrable migration rate in VNX driver via metadata