VNX: add option vnx_async_migrate
Before this fix, customers can disable async migration only by passing `--metadata async_migrate=False` in volumes metadata. This fix adds an option named `vnx_async_migrate` which could help set the default value of async migration for whole backend. If customers don't pass in the `async_migrate` in metadata, the value of this option will be used. Change-Id: I8e59039da78ecc9fb5415a10ff2a7c26ee689171 Closes-bug: #1796825
This commit is contained in:
parent
698b522a57
commit
3ecdc2bfc8
cinder
tests/unit/volume/drivers/dell_emc/vnx
volume/drivers/dell_emc/vnx
doc/source/configuration/block-storage/drivers
releasenotes/notes
@ -573,6 +573,9 @@ test_calc_migrate_and_provision_image_cache:
|
|||||||
test_calc_migrate_and_provision:
|
test_calc_migrate_and_provision:
|
||||||
volume: *volume_base
|
volume: *volume_base
|
||||||
|
|
||||||
|
test_calc_migrate_and_provision_default:
|
||||||
|
volume: *volume_base
|
||||||
|
|
||||||
test_get_backend_qos_specs:
|
test_get_backend_qos_specs:
|
||||||
volume:
|
volume:
|
||||||
_type: 'volume'
|
_type: 'volume'
|
||||||
|
@ -205,6 +205,18 @@ class TestUtils(test_base.TestCase):
|
|||||||
async_migrate)
|
async_migrate)
|
||||||
self.assertEqual(provision.name, 'THICK')
|
self.assertEqual(provision.name, 'THICK')
|
||||||
|
|
||||||
|
@res_mock.mock_driver_input
|
||||||
|
def test_calc_migrate_and_provision_default(self, mocked):
|
||||||
|
volume = mocked['volume']
|
||||||
|
volume.display_name = 'volume-ca86b9a0-d0d5-4267-8cd5-c62274056cc0'
|
||||||
|
async_migrate, provision = vnx_utils.calc_migrate_and_provision(
|
||||||
|
volume, default_async_migrate=False)
|
||||||
|
self.assertFalse(async_migrate)
|
||||||
|
self.assertEqual(provision.name, 'THICK')
|
||||||
|
async_migrate, provision = vnx_utils.calc_migrate_and_provision(
|
||||||
|
volume, default_async_migrate=True)
|
||||||
|
self.assertTrue(async_migrate)
|
||||||
|
|
||||||
@ut_utils.patch_extra_specs({})
|
@ut_utils.patch_extra_specs({})
|
||||||
@res_mock.mock_driver_input
|
@res_mock.mock_driver_input
|
||||||
def test_get_backend_qos_specs(self, cinder_input):
|
def test_get_backend_qos_specs(self, cinder_input):
|
||||||
|
@ -67,6 +67,7 @@ class CommonAdapter(replication.ReplicationAdapter):
|
|||||||
self.destroy_empty_sg = None
|
self.destroy_empty_sg = None
|
||||||
self.itor_auto_dereg = None
|
self.itor_auto_dereg = None
|
||||||
self.queue_path = None
|
self.queue_path = None
|
||||||
|
self.async_migrate = None
|
||||||
|
|
||||||
def _build_client_from_config(self, config, queue_path=None):
|
def _build_client_from_config(self, config, queue_path=None):
|
||||||
return client.Client(
|
return client.Client(
|
||||||
@ -104,6 +105,7 @@ class CommonAdapter(replication.ReplicationAdapter):
|
|||||||
self.protocol = self.config.storage_protocol
|
self.protocol = self.config.storage_protocol
|
||||||
self.destroy_empty_sg = self.config.destroy_empty_storage_group
|
self.destroy_empty_sg = self.config.destroy_empty_storage_group
|
||||||
self.itor_auto_dereg = self.config.initiator_auto_deregistration
|
self.itor_auto_dereg = self.config.initiator_auto_deregistration
|
||||||
|
self.async_migrate = self.config.vnx_async_migrate
|
||||||
self.set_extra_spec_defaults()
|
self.set_extra_spec_defaults()
|
||||||
|
|
||||||
def _normalize_config(self):
|
def _normalize_config(self):
|
||||||
@ -349,7 +351,8 @@ class CommonAdapter(replication.ReplicationAdapter):
|
|||||||
volume_metadata['snapcopy'] = 'True'
|
volume_metadata['snapcopy'] = 'True'
|
||||||
volume_metadata['async_migrate'] = 'False'
|
volume_metadata['async_migrate'] = 'False'
|
||||||
else:
|
else:
|
||||||
async_migrate, provision = utils.calc_migrate_and_provision(volume)
|
async_migrate, provision = utils.calc_migrate_and_provision(
|
||||||
|
volume, default_async_migrate=self.async_migrate)
|
||||||
new_snap_name = (
|
new_snap_name = (
|
||||||
utils.construct_snap_name(volume) if async_migrate else None)
|
utils.construct_snap_name(volume) if async_migrate else None)
|
||||||
new_lun_id = emc_taskflow.create_volume_from_snapshot(
|
new_lun_id = emc_taskflow.create_volume_from_snapshot(
|
||||||
@ -404,7 +407,8 @@ class CommonAdapter(replication.ReplicationAdapter):
|
|||||||
volume_metadata['snapcopy'] = 'True'
|
volume_metadata['snapcopy'] = 'True'
|
||||||
volume_metadata['async_migrate'] = 'False'
|
volume_metadata['async_migrate'] = 'False'
|
||||||
else:
|
else:
|
||||||
async_migrate, provision = utils.calc_migrate_and_provision(volume)
|
async_migrate, provision = utils.calc_migrate_and_provision(
|
||||||
|
volume, default_async_migrate=self.async_migrate)
|
||||||
new_lun_id = emc_taskflow.create_cloned_volume(
|
new_lun_id = emc_taskflow.create_cloned_volume(
|
||||||
client=self.client,
|
client=self.client,
|
||||||
snap_name=snap_name,
|
snap_name=snap_name,
|
||||||
@ -779,7 +783,8 @@ class CommonAdapter(replication.ReplicationAdapter):
|
|||||||
|
|
||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
"""Deletes an EMC volume."""
|
"""Deletes an EMC volume."""
|
||||||
async_migrate = utils.is_async_migrate_enabled(volume)
|
async_migrate = utils.is_async_migrate_enabled(
|
||||||
|
volume, default=self.async_migrate)
|
||||||
snap_copy = (utils.construct_snap_name(volume) if
|
snap_copy = (utils.construct_snap_name(volume) if
|
||||||
utils.is_snapcopy_enabled(volume) else None)
|
utils.is_snapcopy_enabled(volume) else None)
|
||||||
self.cleanup_lun_replication(volume)
|
self.cleanup_lun_replication(volume)
|
||||||
|
@ -109,7 +109,17 @@ VNX_OPTS = [
|
|||||||
default=False,
|
default=False,
|
||||||
help='Force LUN creation even if '
|
help='Force LUN creation even if '
|
||||||
'the full threshold of pool is reached. '
|
'the full threshold of pool is reached. '
|
||||||
'By default, the value is False.')
|
'By default, the value is False.'),
|
||||||
|
cfg.BoolOpt('vnx_async_migrate',
|
||||||
|
default=True,
|
||||||
|
help='Always use asynchronous migration during volume cloning '
|
||||||
|
'and creating from snapshot. As described in '
|
||||||
|
'configuration doc, async migration has some '
|
||||||
|
'constraints. Besides using metadata, customers could '
|
||||||
|
'use this option to disable async migration. Be aware '
|
||||||
|
'that `async_migrate` in metadata overrides this '
|
||||||
|
'option when both are set. By default, the value is True.'
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF.register_opts(VNX_OPTS, group=configuration.SHARED_CONF_GROUP)
|
CONF.register_opts(VNX_OPTS, group=configuration.SHARED_CONF_GROUP)
|
||||||
|
@ -86,9 +86,11 @@ class VNXDriver(driver.ManageableVD,
|
|||||||
under `destroy_empty_stroage_group` setting to `True`
|
under `destroy_empty_stroage_group` setting to `True`
|
||||||
14.0.0 - Fix bug 1794646: failed to delete LUNs from backend due to
|
14.0.0 - Fix bug 1794646: failed to delete LUNs from backend due to
|
||||||
the temporary snapshots on them wasn't deleted.
|
the temporary snapshots on them wasn't deleted.
|
||||||
|
14.0.1 - Fix bug 1796825, add an option to set default value for
|
||||||
|
`async_migrate`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = '14.00.00'
|
VERSION = '14.00.01'
|
||||||
VENDOR = 'Dell EMC'
|
VENDOR = 'Dell EMC'
|
||||||
# ThirdPartySystems wiki page
|
# ThirdPartySystems wiki page
|
||||||
CI_WIKI_NAME = "EMC_VNX_CI"
|
CI_WIKI_NAME = "EMC_VNX_CI"
|
||||||
|
@ -257,7 +257,7 @@ def is_snapcopy_enabled(volume):
|
|||||||
return 'snapcopy' in meta and meta['snapcopy'].lower() == 'true'
|
return 'snapcopy' in meta and meta['snapcopy'].lower() == 'true'
|
||||||
|
|
||||||
|
|
||||||
def is_async_migrate_enabled(volume):
|
def is_async_migrate_enabled(volume, default=True):
|
||||||
extra_specs = common.ExtraSpecs.from_volume(volume)
|
extra_specs = common.ExtraSpecs.from_volume(volume)
|
||||||
if extra_specs.is_replication_enabled:
|
if extra_specs.is_replication_enabled:
|
||||||
# For replication-enabled volume, we should not use the async-cloned
|
# For replication-enabled volume, we should not use the async-cloned
|
||||||
@ -266,8 +266,7 @@ def is_async_migrate_enabled(volume):
|
|||||||
return False
|
return False
|
||||||
meta = get_metadata(volume)
|
meta = get_metadata(volume)
|
||||||
if 'async_migrate' not in meta:
|
if 'async_migrate' not in meta:
|
||||||
# Asynchronous migration is the default behavior now
|
return default
|
||||||
return True
|
|
||||||
return 'async_migrate' in meta and meta['async_migrate'].lower() == 'true'
|
return 'async_migrate' in meta and meta['async_migrate'].lower() == 'true'
|
||||||
|
|
||||||
|
|
||||||
@ -441,7 +440,7 @@ def is_image_cache_volume(volume):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def calc_migrate_and_provision(volume):
|
def calc_migrate_and_provision(volume, default_async_migrate=True):
|
||||||
"""Returns a tuple of async migrate and provision type.
|
"""Returns a tuple of async migrate and provision type.
|
||||||
|
|
||||||
The first element is the flag whether to enable async migrate,
|
The first element is the flag whether to enable async migrate,
|
||||||
@ -451,7 +450,8 @@ def calc_migrate_and_provision(volume):
|
|||||||
return False, storops.VNXProvisionEnum.THIN
|
return False, storops.VNXProvisionEnum.THIN
|
||||||
else:
|
else:
|
||||||
specs = common.ExtraSpecs.from_volume(volume)
|
specs = common.ExtraSpecs.from_volume(volume)
|
||||||
return is_async_migrate_enabled(volume), specs.provision
|
return (is_async_migrate_enabled(volume, default_async_migrate),
|
||||||
|
specs.provision)
|
||||||
|
|
||||||
|
|
||||||
def get_backend_qos_specs(volume):
|
def get_backend_qos_specs(volume):
|
||||||
|
@ -433,6 +433,16 @@ Ignore pool full threshold
|
|||||||
If ``ignore_pool_full_threshold`` is set to ``True``, driver will force LUN
|
If ``ignore_pool_full_threshold`` is set to ``True``, driver will force LUN
|
||||||
creation even if the full threshold of pool is reached. Default to ``False``.
|
creation even if the full threshold of pool is reached. Default to ``False``.
|
||||||
|
|
||||||
|
Default value for async migration
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Option ``vnx_async_migrate`` is used to set the default value of async
|
||||||
|
migration for the backend. The default value of this option is `True` if it
|
||||||
|
isn't set in ``cinder.conf`` to preserve compatibility. If ``async_migrate`` is
|
||||||
|
not set in metadata of volume, the value of this option will be used.
|
||||||
|
Otherwise, ``async_migrate`` value in metadata will override the value of this
|
||||||
|
option. For more detail, refer to `asynchronous migration support`_.
|
||||||
|
|
||||||
Extra spec options
|
Extra spec options
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -791,8 +801,14 @@ as the default cloning method. The driver will return immediately after the
|
|||||||
migration session starts on the VNX, which dramatically reduces the time before
|
migration session starts on the VNX, which dramatically reduces the time before
|
||||||
a volume is available for use.
|
a volume is available for use.
|
||||||
|
|
||||||
To disable this feature, user can add ``--metadata async_migrate=False`` when
|
To disable this feature, user needs to do any one of below actions:
|
||||||
creating new volume from source.
|
|
||||||
|
- Configure ``vnx_async_migrate = False`` for the backend in ``cinder.conf``,
|
||||||
|
then restart Cinder services.
|
||||||
|
- Add ``--metadata async_migrate=False`` when creating new volume from source.
|
||||||
|
|
||||||
|
Be aware, ``async_migrate`` in metadata overrides the option
|
||||||
|
``vnx_async_migrate`` when both are set.
|
||||||
|
|
||||||
**Constraints**
|
**Constraints**
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Dell EMC VNX Driver: Fix `bug 1796825
|
||||||
|
<https://bugs.launchpad.net/cinder/+bug/1796825>`__, adding an option named
|
||||||
|
`vnx_async_migrate` to accept the default setting for async migration.
|
Loading…
x
Reference in New Issue
Block a user