VNX: Update replication for v2.1

Supporting replication v2.1 specification as well
as failback capability.

Change-Id: Idb2fae7f5caa240f6c347e60953d5dd1cdf3f8c8
Closes-Bug: #1547543
This commit is contained in:
peter_wang 2016-02-24 10:57:28 +08:00
parent 1a8361ed32
commit ab2a05aab3
6 changed files with 211 additions and 228 deletions

View File

@ -62,7 +62,7 @@ class EMCVNXCLIDriverTestData(object):
base_lun_name = 'volume-1'
replication_metadata = {'host': 'host@backendsec#unit_test_pool',
'system': 'FNM11111'}
'system': 'fake_serial'}
test_volume = {
'status': 'creating',
'name': 'volume-1',
@ -1924,6 +1924,7 @@ class EMCVNXCLIDriverISCSITestCase(DriverTestCaseBase):
'max_over_subscription_ratio': 20.0,
'consistencygroup_support': 'True',
'replication_enabled': False,
'replication_targets': [],
'pool_name': 'unit_test_pool',
'fast_cache_enabled': True,
'fast_support': 'True'}
@ -4992,6 +4993,7 @@ class EMCVNXCLIDArrayBasedDriverTestCase(DriverTestCaseBase):
'thick_provisioning_support': True,
'consistencygroup_support': 'True',
'replication_enabled': False,
'replication_targets': [],
'pool_name': 'unit_test_pool',
'max_over_subscription_ratio': 20.0,
'fast_cache_enabled': True,
@ -5011,6 +5013,7 @@ class EMCVNXCLIDArrayBasedDriverTestCase(DriverTestCaseBase):
'thick_provisioning_support': True,
'consistencygroup_support': 'True',
'replication_enabled': False,
'replication_targets': [],
'pool_name': 'unit_test_pool2',
'max_over_subscription_ratio': 20.0,
'fast_cache_enabled': False,
@ -5041,6 +5044,7 @@ class EMCVNXCLIDArrayBasedDriverTestCase(DriverTestCaseBase):
'consistencygroup_support': 'False',
'pool_name': 'unit_test_pool',
'replication_enabled': False,
'replication_targets': [],
'max_over_subscription_ratio': 20.0,
'fast_cache_enabled': 'False',
'fast_support': 'False'}
@ -5059,6 +5063,7 @@ class EMCVNXCLIDArrayBasedDriverTestCase(DriverTestCaseBase):
'thick_provisioning_support': True,
'consistencygroup_support': 'False',
'replication_enabled': False,
'replication_targets': [],
'pool_name': 'unit_test_pool2',
'max_over_subscription_ratio': 20.0,
'fast_cache_enabled': 'False',
@ -5589,6 +5594,7 @@ class EMCVNXCLIDriverFCTestCase(DriverTestCaseBase):
'max_over_subscription_ratio': 20.0,
'consistencygroup_support': 'True',
'replication_enabled': False,
'replication_targets': [],
'pool_name': 'unit_test_pool',
'fast_cache_enabled': True,
'fast_support': 'True'}
@ -5939,17 +5945,18 @@ class EMCVNXCLIMultiPoolsTestCase(DriverTestCaseBase):
class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
def setUp(self):
super(EMCVNXCLIDriverReplicationV2TestCase, self).setUp()
self.target_device_id = 'fake_serial'
self.backend_id = 'fake_serial'
self.configuration.replication_device = [{
'target_device_id': self.target_device_id,
'managed_backend_name': 'host@backend#unit_test_pool',
'backend_id': self.backend_id,
'san_ip': '192.168.1.2', 'san_login': 'admin',
'san_password': 'admin', 'san_secondary_ip': '192.168.2.2',
'storage_vnx_authentication_type': 'global',
'storage_vnx_security_file_dir': None}]
def generate_driver(self, conf):
return emc_cli_iscsi.EMCCLIISCSIDriver(configuration=conf)
def generate_driver(self, conf, active_backend_id=None):
return emc_cli_iscsi.EMCCLIISCSIDriver(
configuration=conf,
active_backend_id=active_backend_id)
def _build_mirror_name(self, volume_id):
return 'mirror_' + volume_id
@ -5976,8 +5983,7 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
self.assertTrue(model_update['replication_status'] == 'enabled')
self.assertTrue(model_update['replication_driver_data'] ==
build_replication_data(self.configuration))
self.assertDictMatch({'system': self.target_device_id,
'host': rep_volume.host,
self.assertDictMatch({'system': self.backend_id,
'snapcopy': 'False'},
model_update['metadata'])
fake_cli.assert_has_calls(
@ -6051,72 +6057,14 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
mock.call(*self.testData.MIRROR_DESTROY_CMD(mirror_name),
poll=True)])
def test_enable_replication(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
mirror_name = self._build_mirror_name(rep_volume.id)
image_uid = '50:06:01:60:88:60:05:FE'
commands = [self.testData.MIRROR_LIST_CMD(mirror_name),
self.testData.MIRROR_SYNC_IMAGE_CMD(
mirror_name, image_uid)]
results = [[self.testData.MIRROR_LIST_RESULT(
mirror_name, 'Administratively fractured'),
self.testData.MIRROR_LIST_RESULT(
mirror_name)],
SUCCEED]
fake_cli = self.driverSetup(commands, results)
rep_volume.replication_driver_data = build_replication_data(
self.configuration)
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
self.driver.replication_enable(None, rep_volume)
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True),
mock.call(*self.testData.MIRROR_SYNC_IMAGE_CMD(
mirror_name, image_uid), poll=False),
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=False)])
def test_enable_already_synced(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
mirror_name = self._build_mirror_name(rep_volume.id)
commands = [self.testData.MIRROR_LIST_CMD(mirror_name)]
results = [self.testData.MIRROR_LIST_RESULT(mirror_name)]
fake_cli = self.driverSetup(commands, results)
rep_volume.replication_driver_data = build_replication_data(
self.configuration)
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
self.driver.replication_enable(None, rep_volume)
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True)])
def test_disable_replication(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
mirror_name = self._build_mirror_name(rep_volume.id)
image_uid = '50:06:01:60:88:60:05:FE'
commands = [self.testData.MIRROR_LIST_CMD(mirror_name),
self.testData.MIRROR_FRACTURE_IMAGE_CMD(
mirror_name, image_uid)]
results = [self.testData.MIRROR_LIST_RESULT(mirror_name),
SUCCEED]
fake_cli = self.driverSetup(commands, results)
rep_volume.replication_driver_data = build_replication_data(
self.configuration)
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
self.driver.replication_disable(None, rep_volume)
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True),
mock.call(*self.testData.MIRROR_FRACTURE_IMAGE_CMD(mirror_name,
image_uid), poll=False)])
@mock.patch(
"cinder.volume.drivers.emc.emc_vnx_cli.CommandLineHelper." +
"get_lun_by_name",
mock.Mock(return_value={'lun_id': 1}))
@mock.patch(
"cinder.volume.volume_types."
"get_volume_type_extra_specs",
mock.Mock(return_value={'replication_enabled': '<is> True'}))
def test_failover_replication_from_primary(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
@ -6132,26 +6080,24 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
self.configuration)
rep_volume.metadata = self.testData.replication_metadata
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
model_update = self.driver.replication_failover(
None, rep_volume,
self.target_device_id)
back_id, model_update = self.driver.failover_host(
None, [rep_volume],
self.backend_id)
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True),
mock.call(*self.testData.MIRROR_PROMOTE_IMAGE_CMD(mirror_name,
image_uid), poll=False)])
self.assertEqual(
self.configuration.replication_device[0]['managed_backend_name'],
model_update['host'])
expected = build_provider_location('1', 'lun', rep_volume.name,
self.target_device_id)
provider_location = model_update['provider_location']
# Don't compare the exact string but the set of items: dictionary
# items are rendered in a random order because of the hash
# randomization
self.assertSetEqual(set(expected.split('|')),
set(provider_location.split('|')))
build_provider_location(
'1', 'lun', rep_volume.name,
self.backend_id),
model_update[0]['updates']['provider_location'])
@mock.patch(
"cinder.volume.volume_types."
"get_volume_type_extra_specs",
mock.Mock(return_value={'replication_enabled': '<is> True'}))
def test_failover_replication_from_secondary(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
@ -6174,14 +6120,49 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
'cinder.volume.drivers.emc.emc_vnx_cli.CommandLineHelper') \
as fake_remote:
fake_remote.return_value = self.driver.cli._client
self.driver.replication_failover(None, rep_volume,
'FNM11111')
backend_id, data = self.driver.failover_host(
None, [rep_volume], 'default')
updates = data[0]['updates']
rep_status = updates['replication_status']
self.assertEqual('enabled', rep_status)
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True),
mock.call(*self.testData.MIRROR_PROMOTE_IMAGE_CMD(mirror_name,
image_uid), poll=False)])
@mock.patch(
"cinder.volume.volume_types."
"get_volume_type_extra_specs",
mock.Mock(return_value={'replication_enabled': '<is> True'}))
def test_failover_replication_invalid_backend_id(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
self._build_mirror_name(rep_volume.id)
fake_cli = self.driverSetup([], [])
rep_volume.replication_driver_data = build_replication_data(
self.configuration)
rep_volume.metadata = self.testData.replication_metadata
driver_data = json.loads(rep_volume.replication_driver_data)
driver_data['is_primary'] = False
rep_volume.replication_driver_data = json.dumps(driver_data)
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
with mock.patch(
'cinder.volume.drivers.emc.emc_vnx_cli.CommandLineHelper') \
as fake_remote:
fake_remote.return_value = self.driver.cli._client
invalid = 'invalid_backend_id'
self.assertRaisesRegex(exception.VolumeBackendAPIException,
"Invalid secondary_backend_id specified",
self.driver.failover_host,
None,
[rep_volume],
invalid)
@mock.patch(
"cinder.volume.volume_types."
"get_volume_type_extra_specs",
mock.Mock(return_value={'replication_enabled': '<is> True'}))
def test_failover_already_promoted(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
@ -6197,11 +6178,12 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
self.configuration)
rep_volume.metadata = self.testData.replication_metadata
self.driver.cli._mirror._secondary_client.command_execute = fake_cli
self.assertRaisesRegex(exception.EMCVnxCLICmdError,
'UID of the secondary image '
'to be promoted is not local',
self.driver.replication_failover,
None, rep_volume, self.target_device_id)
new_backend_id, model_updates = self.driver.failover_host(
None, [rep_volume], self.backend_id)
self.assertEqual(rep_volume.id, model_updates[0]['volume_id'])
self.assertEqual('error',
model_updates[0]['updates']['replication_status'])
fake_cli.assert_has_calls([
mock.call(*self.testData.MIRROR_LIST_CMD(mirror_name),
poll=True),
@ -6246,18 +6228,20 @@ class EMCVNXCLIDriverReplicationV2TestCase(DriverTestCaseBase):
poll=False)]
fake_cli.assert_has_calls(expected)
def test_list_replication_targets(self):
rep_volume = EMCVNXCLIDriverTestData.convert_volume(
self.testData.test_volume_replication)
rep_volume.replication_driver_data = build_replication_data(
self.configuration)
expect_targets = {'volume_id': rep_volume.id,
'targets':
[{'type': 'managed',
'target_device_id': self.target_device_id}]}
def test_build_client_with_invalid_id(self):
self.driverSetup([], [])
data = self.driver.list_replication_targets(None, rep_volume)
self.assertDictMatch(expect_targets, data)
self.assertRaisesRegex(
exception.VolumeBackendAPIException,
'replication_device with backend_id .* is missing.',
self.driver.cli._build_client,
'invalid_backend_id')
def test_build_client_with_id(self):
self.driverSetup([], [])
cli_client = self.driver.cli._build_client(
active_backend_id='fake_serial')
self.assertEqual('192.168.1.2', cli_client.active_storage_ip)
self.assertEqual('192.168.1.2', cli_client.primary_storage_ip)
VNXError = emc_vnx_cli.VNXError

View File

@ -63,11 +63,11 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
"""
def __init__(self, *args, **kwargs):
super(EMCCLIFCDriver, self).__init__(*args, **kwargs)
self.cli = emc_vnx_cli.getEMCVnxCli(
'FC',
configuration=self.configuration)
configuration=self.configuration,
active_backend_id=kwargs.get('active_backend_id'))
self.VERSION = self.cli.VERSION
def check_for_setup_error(self):
@ -301,18 +301,6 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
def backup_use_temp_snapshot(self):
return True
def replication_enable(self, context, volume):
"""Enables replication on a replication capable volume."""
return self.cli.replication_enable(context, volume)
def replication_disable(self, context, volume):
"""Disables replication on a replication-enabled volume."""
return self.cli.replication_disable(context, volume)
def replication_failover(self, context, volume, secondary):
def failover_host(self, context, volumes, secondary_backend_id):
"""Failovers volume from primary device to secondary."""
return self.cli.replication_failover(context, volume, secondary)
def list_replication_targets(self, context, volume):
"""Returns volume replication info."""
return self.cli.list_replication_targets(context, volume)
return self.cli.failover_host(context, volumes, secondary_backend_id)

View File

@ -61,11 +61,11 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
"""
def __init__(self, *args, **kwargs):
super(EMCCLIISCSIDriver, self).__init__(*args, **kwargs)
self.cli = emc_vnx_cli.getEMCVnxCli(
'iSCSI',
configuration=self.configuration)
configuration=self.configuration,
active_backend_id=kwargs.get('active_backend_id'))
self.VERSION = self.cli.VERSION
def check_for_setup_error(self):
@ -280,18 +280,6 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
def backup_use_temp_snapshot(self):
return True
def replication_enable(self, context, volume):
"""Enables replication on a replication capable volume."""
return self.cli.replication_enable(context, volume)
def replication_disable(self, context, volume):
"""Disables replication on a replication-enabled volume."""
return self.cli.replication_disable(context, volume)
def replication_failover(self, context, volume, secondary):
def failover_host(self, context, volumes, secondary_backend_id):
"""Failovers volume from primary device to secondary."""
return self.cli.replication_failover(context, volume, secondary)
def list_replication_targets(self, context, volume):
"""Returns volume replication info."""
return self.cli.list_replication_targets(context, volume)
return self.cli.failover_host(context, volumes, secondary_backend_id)

View File

@ -2124,7 +2124,7 @@ class EMCVnxCliBase(object):
tmp_smp_for_backup_prefix = 'tmp-smp-'
snap_as_vol_prefix = 'snap-as-vol-'
def __init__(self, prtcl, configuration=None):
def __init__(self, prtcl, configuration=None, active_backend_id=None):
self.protocol = prtcl
self.configuration = configuration
self.max_luns_per_sg = self.configuration.max_luns_per_storage_group
@ -2151,7 +2151,8 @@ class EMCVnxCliBase(object):
"Initiator auto registration is not enabled. "
"Please register initiator manually."))
self.hlu_set = set(range(1, self.max_luns_per_sg + 1))
self._client = CommandLineHelper(self.configuration)
self._client = self._build_client(active_backend_id)
self._active_backend_id = active_backend_id
# Create connection to the secondary storage device
self._mirror = self._build_mirror_view()
self.update_enabler_in_volume_stats()
@ -2817,12 +2818,16 @@ class EMCVnxCliBase(object):
pool_stats['max_over_subscription_ratio'] = (
self.max_over_subscription_ratio)
# Add replication V2 support
targets = []
if self._mirror:
pool_stats['replication_enabled'] = True
pool_stats['replication_count'] = 1
pool_stats['replication_type'] = ['sync']
for device in self.configuration.replication_device:
targets.append(device['backend_id'])
else:
pool_stats['replication_enabled'] = False
pool_stats['replication_targets'] = targets
return pool_stats
def update_enabler_in_volume_stats(self):
@ -3947,82 +3952,72 @@ class EMCVnxCliBase(object):
return specs
def replication_enable(self, context, volume):
"""Enables replication for the volume."""
mirror_name = self._construct_mirror_name(volume)
mirror_view = self._get_mirror_view(volume)
mirror_view.sync_image(mirror_name)
def replication_disable(self, context, volume):
"""Disables replication for the volume."""
mirror_name = self._construct_mirror_name(volume)
mirror_view = self._get_mirror_view(volume)
mirror_view.fracture_image(mirror_name)
def replication_failover(self, context, volume, secondary):
def failover_host(self, context, volumes, secondary_backend_id):
"""Fails over the volume back and forth.
Driver needs to update following info for this volume:
1. host: to point to the new host
2. provider_location: update serial number and lun id
"""
rep_data = json.loads(volume['replication_driver_data'])
is_primary = rep_data['is_primary']
if is_primary:
remote_device_id = (
self.configuration.replication_device[0]['target_device_id'])
volume_update_list = []
if secondary_backend_id != 'default':
rep_status = 'failed-over'
backend_id = (
self.configuration.replication_device[0]['backend_id'])
if secondary_backend_id != backend_id:
msg = (_('Invalid secondary_backend_id specified. '
'Valid backend id is %s.') % backend_id)
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
else:
remote_device_id = self._get_volume_metadata(volume)['system']
if secondary != remote_device_id:
msg = (_('Invalid secondary specified, choose from %s.')
% [remote_device_id])
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
rep_status = 'enabled'
mirror_name = self._construct_mirror_name(volume)
mirror_view = self._get_mirror_view(volume)
remote_client = mirror_view._secondary_client
if is_primary:
new_host = (
self.configuration.replication_device[0][
'managed_backend_name'])
else:
new_host = self._get_volume_metadata(volume)['host']
def failover_one(volume, new_status):
rep_data = json.loads(volume['replication_driver_data'])
is_primary = rep_data['is_primary']
mirror_name = self._construct_mirror_name(volume)
mirror_view = self._get_mirror_view(volume)
remote_client = mirror_view._secondary_client
rep_data.update({'is_primary': not is_primary})
# Transfer ownership to secondary and
# update provider_location field
provider_location = volume['provider_location']
provider_location = self._update_provider_location(
provider_location,
'system', remote_client.get_array_serial()['array_serial'])
self.get_array_serial()
provider_location = self._update_provider_location(
provider_location,
'id',
six.text_type(remote_client.get_lun_by_name(volume.name)['lun_id'])
)
mirror_view.promote_image(mirror_name)
model_update = {'host': new_host,
'replication_driver_data': json.dumps(rep_data),
'provider_location': provider_location}
return model_update
def list_replication_targets(self, context, volume):
"""Provides replication target(s) for a volume."""
targets = {'volume_id': volume.id, 'targets': []}
rep_data = json.loads(volume['replication_driver_data'])
is_primary = rep_data['is_primary']
if is_primary:
remote_device_id = (
self.configuration.replication_device[0]['target_device_id'])
else:
remote_device_id = self._get_volume_metadata(volume)['system']
targets['targets'] = [{'type': 'managed',
'target_device_id': remote_device_id}]
return targets
provider_location = volume['provider_location']
try:
mirror_view.promote_image(mirror_name)
except exception.EMCVnxCLICmdError as ex:
msg = _LE(
'Failed to failover volume %(volume_id)s '
'to %(target)s: %(error)s.')
LOG.error(msg, {'volume_id': volume.id,
'target': secondary_backend_id,
'error': ex},)
new_status = 'error'
else:
rep_data.update({'is_primary': not is_primary})
# Transfer ownership to secondary_backend_id and
# update provider_location field
provider_location = self._update_provider_location(
provider_location,
'system', remote_client.get_array_serial()['array_serial'])
provider_location = self._update_provider_location(
provider_location,
'id',
six.text_type(
remote_client.get_lun_by_name(volume.name)['lun_id'])
)
model_update = {'volume_id': volume.id,
'updates':
{'replication_driver_data':
json.dumps(rep_data),
'replication_status': new_status,
'provider_location': provider_location}}
volume_update_list.append(model_update)
for volume in volumes:
if self._is_replication_enabled(volume):
failover_one(volume, rep_status)
else:
volume_update_list.append({
'volume_id': volume.id,
'updates': {'status': 'error'}})
return secondary_backend_id, volume_update_list
def _is_replication_enabled(self, volume):
"""Return True if replication extra specs is specified.
@ -4045,8 +4040,10 @@ class EMCVnxCliBase(object):
'for volume: %s.', volume.id)
lun_size = volume['size']
mirror_name = self._construct_mirror_name(volume)
pool_name = vol_utils.extract_host(volume.host, 'pool')
self._mirror.create_mirror_workflow(
mirror_name, primary_lun_id, volume.name, lun_size,
mirror_name, primary_lun_id, pool_name,
volume.name, lun_size,
provisioning, tiering)
LOG.info(_LI('Successfully setup replication for %s.'), volume.id)
@ -4055,7 +4052,6 @@ class EMCVnxCliBase(object):
self.configuration),
'replication_status': 'enabled'})
metadata_update = {
'host': volume.host,
'system': self.get_array_serial()}
return rep_update, metadata_update
@ -4103,6 +4099,35 @@ class EMCVnxCliBase(object):
driver_data['is_primary'] = True
return json.dumps(driver_data)
def _build_client(self, active_backend_id=None):
"""Builds a client pointing to the right VNX."""
if not active_backend_id:
return CommandLineHelper(self.configuration)
else:
configuration = self.configuration
if not configuration.replication_device:
err_msg = (
_('replication_device should be configured '
'on backend: %s.') % configuration.config_group)
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
current_target = None
for target in configuration.replication_device:
if target['backend_id'] == active_backend_id:
current_target = target
break
if not current_target:
err_msg = (
_('replication_device with backend_id [%s] is missing.')
% active_backend_id)
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
target_conf = copy.copy(configuration)
for key in self.REPLICATION_KEYS:
if key in current_target:
setattr(target_conf, key, current_target[key])
return CommandLineHelper(target_conf)
def _build_mirror_view(self, volume=None):
"""Builds a client for remote storage device.
@ -4126,19 +4151,13 @@ class EMCVnxCliBase(object):
configuration.config_group)
return None
remote_info = configuration.replication_device[0]
pool_name = None
managed_backend_name = remote_info.get('managed_backend_name')
if managed_backend_name:
pool_name = vol_utils.extract_host(managed_backend_name, 'pool')
# Copy info to replica configuration for remote client
replica_conf = copy.copy(configuration)
for key in self.REPLICATION_KEYS:
if key in remote_info:
config.Configuration.__setattr__(replica_conf,
key, remote_info[key])
setattr(replica_conf, key, remote_info[key])
_remote_client = CommandLineHelper(replica_conf)
_mirror = MirrorView(self._client, _remote_client, pool_name)
_mirror = MirrorView(self._client, _remote_client)
return _mirror
def get_pool(self, volume):
@ -4359,9 +4378,10 @@ class EMCVnxCliBase(object):
return self.stats
def getEMCVnxCli(prtcl, configuration=None):
def getEMCVnxCli(prtcl, configuration=None, active_backend_id=None):
configuration.append_config_values(loc_opts)
return EMCVnxCliBase(prtcl, configuration=configuration)
return EMCVnxCliBase(prtcl, configuration=configuration,
active_backend_id=active_backend_id)
class CreateSMPTask(task.Task):
@ -4598,7 +4618,7 @@ class MirrorView(object):
SYNCHRONIZED_STATE = 'Synchronized'
CONSISTENT_STATE = 'Consistent'
def __init__(self, client, secondary_client, pool_name, mode='sync'):
def __init__(self, client, secondary_client, mode='sync'):
"""Caller needs to initialize MirrorView via this method.
:param client: client connecting to primary system
@ -4607,7 +4627,6 @@ class MirrorView(object):
"""
self._client = client
self._secondary_client = secondary_client
self.pool_name = pool_name
if mode not in self.SYNCHRONIZE_MODE:
msg = _('Invalid synchronize mode specified, allowed '
'mode is %s.') % self.SYNCHRONIZE_MODE
@ -4615,12 +4634,13 @@ class MirrorView(object):
data=msg)
self.mode = '-sync'
def create_mirror_workflow(self, mirror_name, lun_id,
def create_mirror_workflow(self, mirror_name, lun_id, pool_name,
lun_name, lun_size, provisioning, tiering):
"""Creates mirror view for LUN."""
store_spec = {'mirror': self}
work_flow = self._get_create_mirror_flow(
mirror_name, lun_id, lun_name, lun_size, provisioning, tiering)
mirror_name, lun_id, pool_name,
lun_name, lun_size, provisioning, tiering)
flow_engine = taskflow.engines.load(work_flow, store=store_spec)
flow_engine.run()
@ -4630,13 +4650,13 @@ class MirrorView(object):
self.destroy_mirror(mirror_name)
self.delete_secondary_lun(lun_name)
def _get_create_mirror_flow(self, mirror_name, lun_id,
def _get_create_mirror_flow(self, mirror_name, lun_id, pool_name,
lun_name, lun_size, provisioning, tiering):
"""Gets mirror create flow."""
flow_name = 'create_mirror_view'
work_flow = linear_flow.Flow(flow_name)
work_flow.add(MirrorCreateTask(mirror_name, lun_id),
MirrorSecLunCreateTask(lun_name, lun_size,
MirrorSecLunCreateTask(pool_name, lun_name, lun_size,
provisioning, tiering),
MirrorAddImageTask(mirror_name))
return work_flow
@ -4657,11 +4677,12 @@ class MirrorView(object):
out=out)
return rc
def create_secondary_lun(self, lun_name, lun_size, provisioning,
def create_secondary_lun(self, pool_name, lun_name, lun_size,
provisioning,
tiering, poll=False):
"""Creates secondary LUN in remote device."""
data = self._secondary_client.create_lun_with_advance_feature(
pool=self.pool_name,
pool=pool_name,
name=lun_name,
size=lun_size,
provisioning=provisioning,
@ -4886,8 +4907,9 @@ class MirrorSecLunCreateTask(task.Task):
Reversion strategy: Delete secondary LUN.
"""
def __init__(self, lun_name, lun_size, provisioning, tiering):
def __init__(self, pool_name, lun_name, lun_size, provisioning, tiering):
super(MirrorSecLunCreateTask, self).__init__(provides='sec_lun_id')
self.pool_name = pool_name
self.lun_name = lun_name
self.lun_size = lun_size
self.provisioning = provisioning
@ -4896,7 +4918,8 @@ class MirrorSecLunCreateTask(task.Task):
def execute(self, mirror, *args, **kwargs):
LOG.debug('%s.execute', self.__class__.__name__)
sec_lun_id = mirror.create_secondary_lun(
self.lun_name, self.lun_size, self.provisioning, self.tiering)
self.pool_name, self.lun_name, self.lun_size,
self.provisioning, self.tiering)
return sec_lun_id
def revert(self, result, mirror, *args, **kwargs):

View File

@ -1,3 +0,0 @@
---
features:
- Replication v2 has been added in VNX Cinder driver

View File

@ -0,0 +1,3 @@
---
features:
- Adds v2.1 replication support in VNX Cinder driver.