VNX: Allow set migrate rate when migrating volumes
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
This commit is contained in:
parent
d428d9bc84
commit
719bedd625
@ -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),
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Configrable migration rate in VNX driver via metadata
|
Loading…
Reference in New Issue
Block a user