Storwize: Update replication to v2.1

This patch updates replication to match the v2.1 spec. This makes
it possible to replicate an entire backend, and upon failover, all
replicated volumes will be failed over together.

cinder.conf should have the replication config group:

The replication can be configured via either multi-backend on one
cinder volume node, or on separate cinder volume nodes.

Options to be put in cinder.conf, where the primary back-end is
located:

enabled_backends = sv1, sv2 (if enabling multi-backends)

[sv1]
san_login = admin
san_password = admin
san_ip = 192.168.0.11
volume_driver = cinder.volume.drivers.ibm.storwize_svc.\
                storwize_svc_iscsi.StorwizeSVCISCSIDriver
volume_backend_name = sv1
storwize_svc_volpool_name=cinder
replication_device = managed_backend_name:second_host@sv2#sv2,
                     backend_id:svc_backend_id,
                     replication_mode:global,
                     san_ip:192.168.0.12,san_login:admin,
                     san_password:admin,pool_name:cinder_target

Options to be put in cinder.conf, where the secondary back-end is
connected:

[sv2]
san_login = admin
san_password = admin
san_ip = 192.168.0.12
volume_driver = cinder.volume.drivers.ibm.storwize_svc.\
                storwize_svc_iscsi.StorwizeSVCISCSIDriver
volume_backend_name = sv2
storwize_svc_volpool_name=cinder_target

DocImpact
Closes-Bug: #1544611

Change-Id: I8a4963fa4b30f2df1903697909deece762228257
This commit is contained in:
Vincent Hou 2016-03-01 14:26:52 -05:00
parent b043410f39
commit c8cf5504cc
7 changed files with 294 additions and 198 deletions

View File

@ -4769,7 +4769,7 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
extra_spec_rep_type = '<in> ' + self.rep_type extra_spec_rep_type = '<in> ' + self.rep_type
fake_target = {"managed_backend_name": "second_host@sv2#sv2", fake_target = {"managed_backend_name": "second_host@sv2#sv2",
"replication_mode": self.rep_type, "replication_mode": self.rep_type,
"target_device_id": "svc_id_target", "backend_id": "svc_id_target",
"san_ip": "192.168.10.23", "san_ip": "192.168.10.23",
"san_login": "admin", "san_login": "admin",
"san_password": "admin", "san_password": "admin",
@ -4784,10 +4784,10 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
self.svc_driver.replications[self.rep_type] = ( self.svc_driver.replications[self.rep_type] = (
self.svc_driver.replication_factory(self.rep_type, fake_target)) self.svc_driver.replication_factory(self.rep_type, fake_target))
self.ctxt = context.get_admin_context() self.ctxt = context.get_admin_context()
rand_id = six.text_type(uuid.uuid4()) self.fake_volume_id = six.text_type(uuid.uuid4())
pool = _get_test_pool() pool = _get_test_pool()
self.volume = {'name': 'volume-%s' % rand_id, self.volume = {'name': 'volume-%s' % self.fake_volume_id,
'size': 10, 'id': '%s' % rand_id, 'size': 10, 'id': '%s' % self.fake_volume_id,
'volume_type_id': None, 'volume_type_id': None,
'mdisk_grp_name': 'openstack', 'mdisk_grp_name': 'openstack',
'replication_status': 'disabled', 'replication_status': 'disabled',
@ -4801,6 +4801,7 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
type_ref['id']) type_ref['id'])
self.volume['volume_type_id'] = self.replication_type['id'] self.volume['volume_type_id'] = self.replication_type['id']
self.volume['volume_type'] = self.replication_type self.volume['volume_type'] = self.replication_type
self.volumes = [self.volume]
def test_storwize_do_replication_setup(self): def test_storwize_do_replication_setup(self):
self.svc_driver.configuration.set_override('san_ip', "192.168.10.23") self.svc_driver.configuration.set_override('san_ip', "192.168.10.23")
@ -4810,7 +4811,7 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
def test_storwize_do_replication_setup_unmanaged(self): def test_storwize_do_replication_setup_unmanaged(self):
fake_target = {"replication_mode": self.rep_type, fake_target = {"replication_mode": self.rep_type,
"target_device_id": "svc_id_target", "backend_id": "svc_id_target",
"san_ip": "192.168.10.23", "san_ip": "192.168.10.23",
"san_login": "admin", "san_login": "admin",
"san_password": "admin", "san_password": "admin",
@ -4880,48 +4881,21 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
rep_setup.assert_called_once_with(self.ctxt, target_volume) rep_setup.assert_called_once_with(self.ctxt, target_volume)
self.assertEqual({'replication_status': 'enabled'}, model_update) self.assertEqual({'replication_status': 'enabled'}, model_update)
@mock.patch.object(mirror_class, 'replication_enable')
@mock.patch.object(mirror_class, 'volume_replication_setup')
def test_storwize_replication_enable(self, rep_setup,
replication_enable):
self.svc_driver.replication_enable(self.ctxt, self.volume)
replication_enable.assert_called_once_with(self.ctxt, self.volume)
@mock.patch.object(mirror_class, @mock.patch.object(mirror_class,
'replication_disable') 'failover_volume_host')
@mock.patch.object(mirror_class, def test_storwize_failover_host(self, failover_volume_host):
'volume_replication_setup')
def test_storwize_replication_disable(self, rep_setup,
replication_disable):
self.svc_driver.replication_disable(self.ctxt, self.volume)
replication_disable.assert_called_once_with(self.ctxt, self.volume)
@mock.patch.object(mirror_class,
'replication_failover')
@mock.patch.object(mirror_class,
'volume_replication_setup')
def test_storwize_replication_failover(self, rep_setup,
replication_failover):
fake_secondary = 'svc_id_target' fake_secondary = 'svc_id_target'
self.svc_driver.replication_failover(self.ctxt, self.volume, target_id, volume_list = self.svc_driver.failover_host(self.ctxt,
fake_secondary) self.volumes,
replication_failover.assert_called_once_with(self.ctxt, self.volume, fake_secondary)
fake_secondary) expected_list = [{'updates': {'replication_status': 'failed-over'},
'volume_id': self.fake_volume_id}]
@mock.patch.object(mirror_class, expected_calls = [mock.call(self.ctxt, self.volume,
'list_replication_targets') fake_secondary)]
def test_storwize_list_replication_targets(self, list_targets): failover_volume_host.assert_has_calls(expected_calls)
fake_targets = [{"managed_backend_name": "second_host@sv2#sv2", self.assertEqual(fake_secondary, target_id)
"type": "managed", self.assertEqual(expected_list, volume_list)
"target_device_id": "svc_id_target",
"pool_name": "cinder_target"}]
list_targets.return_value = fake_targets
expected_resp = {'targets': fake_targets,
'volume_id': self.volume['id']}
targets = self.svc_driver.list_replication_targets(self.ctxt,
self.volume)
list_targets.assert_called_once_with(self.ctxt, self.volume)
self.assertEqual(expected_resp, targets)
@mock.patch.object(mirror_class, @mock.patch.object(mirror_class,
'_partnership_validate_create') '_partnership_validate_create')
@ -4944,85 +4918,110 @@ class StorwizeSVCReplicationMirrorTestCase(test.TestCase):
partnership_validate_create.assert_has_calls(expected_calls) partnership_validate_create.assert_has_calls(expected_calls)
@mock.patch.object(storwize_svc_common.StorwizeHelpers, @mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_relationship') 'switch_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_system_info')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_vdisk_params')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_vdisk_attributes')
@mock.patch.object(storwize_svc_common.StorwizeHelpers, @mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info') 'get_relationship_info')
def test_replication_enable(self, get_relationship_info, def test_failover_volume_host(self, get_relationship_info,
get_vdisk_attributes, switch_relationship):
get_vdisk_params, fake_vol = {'id': '21345678-1234-5678-1234-567812345683'}
create_vdisk, context = mock.Mock
get_system_info,
create_relationship):
fake_system = 'fake_system'
fake_params = mock.Mock()
get_relationship_info.return_value = None
get_vdisk_attributes.return_value = None
get_vdisk_params.return_value = fake_params
get_system_info.return_value = {'system_name': fake_system}
model_update = self.driver.replication_enable(self.ctxt,
self.volume)
get_relationship_info.assert_called_once_with(self.volume)
get_vdisk_attributes.assert_called_once_with(self.volume['name'])
create_vdisk.assert_called_once_with(self.volume['name'],
'10', 'gb', 'cinder_target',
fake_params)
create_relationship.assert_called_once_with(self.volume['name'],
self.volume['name'],
fake_system,
self.driver.asyncmirror)
self.assertEqual({'replication_status': 'enabled'}, model_update)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_replication_disable(self, get_relationship_info,
delete_relationship,
delete_vdisk):
fake_target_vol_name = 'fake_target_vol_name'
get_relationship_info.return_value = {'aux_vdisk_name':
fake_target_vol_name}
model_update = self.driver.replication_disable(self.ctxt,
self.volume)
delete_relationship.assert_called_once_with(self.volume['name'])
delete_vdisk.assert_called_once_with(fake_target_vol_name,
False)
self.assertEqual({'replication_status': 'disabled'}, model_update)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_replication_failover(self, get_relationship_info,
delete_relationship):
secondary = 'svc_id_target' secondary = 'svc_id_target'
fake_id = '546582b2-bafb-43cc-b765-bd738ab148c8' get_relationship_info.return_value = (
expected_model_update = {'host': 'second_host@sv2#sv2', {'aux_vdisk_name': 'replica-12345678-1234-5678-1234-567812345678',
'_name_id': fake_id} 'name': 'RC_name'})
fake_name = 'volume-' + fake_id self.driver.failover_volume_host(context, fake_vol, secondary)
get_relationship_info.return_value = {'aux_vdisk_name': get_relationship_info.assert_called_once_with(fake_vol)
fake_name} switch_relationship.assert_called_once_with('RC_name')
model_update = self.driver.replication_failover(self.ctxt,
self.volume,
secondary)
delete_relationship.assert_called_once_with(self.volume['name'])
self.assertEqual(expected_model_update, model_update)
def test_list_replication_targets(self): @mock.patch.object(storwize_svc_common.StorwizeHelpers,
fake_targets = [{'target_device_id': 'svc_id_target'}] 'switch_relationship')
targets = self.driver.list_replication_targets(self.ctxt, @mock.patch.object(storwize_svc_common.StorwizeHelpers,
self.volume) 'get_relationship_info')
self.assertEqual(fake_targets, targets) def test_failover_volume_host_relation_error(self, get_relationship_info,
switch_relationship):
fake_vol = {'id': '21345678-1234-5678-1234-567812345683'}
context = mock.Mock
get_relationship_info.side_effect = Exception
secondary = 'svc_id_target'
self.assertRaises(exception.VolumeDriverException,
self.driver.failover_volume_host,
context, fake_vol, secondary)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'switch_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_failover_volume_host_switch_error(self, get_relationship_info,
switch_relationship):
fake_vol = {'id': '21345678-1234-5678-1234-567812345683'}
context = mock.Mock
secondary = 'svc_id_target'
get_relationship_info.return_value = (
{'aux_vdisk_name': 'replica-12345678-1234-5678-1234-567812345678',
'RC_name': 'RC_name'})
switch_relationship.side_effect = Exception
self.assertRaises(exception.VolumeDriverException,
self.driver.failover_volume_host,
context, fake_vol, secondary)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'switch_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_failover_volume_host_backend_mismatch(self,
get_relationship_info,
switch_relationship):
fake_vol = {'id': '21345678-1234-5678-1234-567812345683'}
context = mock.Mock
secondary = 'wrong_id'
get_relationship_info.return_value = (
{'aux_vdisk_name': 'replica-12345678-1234-5678-1234-567812345678',
'RC_name': 'RC_name'})
updates = self.driver.failover_volume_host(context, fake_vol,
secondary)
self.assertFalse(get_relationship_info.called)
self.assertFalse(switch_relationship.called)
self.assertIsNone(updates)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'switch_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_replication_failback(self, get_relationship_info,
switch_relationship):
fake_vol = mock.Mock()
get_relationship_info.return_value = {'id': 'rel_id',
'name': 'rc_name'}
self.driver.replication_failback(fake_vol)
get_relationship_info.assert_called_once_with(fake_vol)
switch_relationship.assert_called_once_with('rc_name', aux=False)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_get_relationship_status_valid(self, get_relationship_info):
fake_vol = mock.Mock()
get_relationship_info.return_value = {'state': 'synchronized'}
status = self.driver.get_relationship_status(fake_vol)
get_relationship_info.assert_called_once_with(fake_vol)
self.assertEqual('synchronized', status)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_get_relationship_status_none(self, get_relationship_info):
fake_vol = mock.Mock()
get_relationship_info.return_value = None
status = self.driver.get_relationship_status(fake_vol)
get_relationship_info.assert_called_once_with(fake_vol)
self.assertIsNone(status)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_get_relationship_status_exception(self, get_relationship_info):
fake_vol = {'id': 'vol-id'}
get_relationship_info.side_effect = exception.VolumeDriverException
status = self.driver.get_relationship_status(fake_vol)
get_relationship_info.assert_called_once_with(fake_vol)
self.assertIsNone(status)
class StorwizeSVCReplicationMetroMirrorTestCase( class StorwizeSVCReplicationMetroMirrorTestCase(

View File

@ -374,29 +374,19 @@ class StorwizeSVCReplicationGlobalMirror(
LOG.error(msg) LOG.error(msg)
raise exception.VolumeDriverException(message=msg) raise exception.VolumeDriverException(message=msg)
# #### Implementing V2 replication methods #### # def get_relationship_status(self, volume):
def replication_enable(self, context, vref): rel_info = {}
try: try:
rel_info = self.driver._helpers.get_relationship_info(vref) rel_info = self.target_helpers.get_relationship_info(volume)
except Exception as e: except Exception:
msg = (_('Failed to get remote copy information for %(volume)s ' msg = (_LE('Unable to access the Storwize back-end '
'due to %(err)s'), {'volume': vref['id'], 'err': e}) 'for volume %s.'), volume['id'])
LOG.error(msg) LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
if not rel_info or not rel_info.get('aux_vdisk_name', None): return rel_info.get('state') if rel_info else None
self.volume_replication_setup(context, vref)
model_update = {'replication_status': 'enabled'} def failover_volume_host(self, context, vref, secondary):
return model_update if not self.target or self.target.get('backend_id') != secondary:
def replication_disable(self, context, vref):
self.delete_target_volume(vref)
model_update = {'replication_status': 'disabled'}
return model_update
def replication_failover(self, context, vref, secondary):
if not self.target or self.target.get('target_device_id') != secondary:
msg = _LE("A valid secondary target MUST be specified in order " msg = _LE("A valid secondary target MUST be specified in order "
"to failover.") "to failover.")
LOG.error(msg) LOG.error(msg)
@ -405,29 +395,42 @@ class StorwizeSVCReplicationGlobalMirror(
# The admin can still issue another failover request. That is # The admin can still issue another failover request. That is
# why we tentatively put return None instead of raising an # why we tentatively put return None instead of raising an
# exception. # exception.
return None return
try: try:
rel_info = self.driver._helpers.get_relationship_info(vref) rel_info = self.target_helpers.get_relationship_info(vref)
target_vol_name = rel_info.get('aux_vdisk_name')
target_vol_id = target_vol_name[-self.UUID_LEN:]
if rel_info:
self.driver._helpers.delete_relationship(vref['name'])
if target_vol_id == vref['id']:
target_vol_id = None
except Exception: except Exception:
msg = (_('Unable to failover the replication for volume %s.'), msg = (_('Unable to access the Storwize back-end for volume %s.'),
vref['id']) vref['id'])
LOG.error(msg) LOG.error(msg)
raise exception.VolumeDriverException(message=msg) raise exception.VolumeDriverException(message=msg)
model_update = {'host': self.target.get('managed_backend_name'), if not rel_info:
'_name_id': target_vol_id} msg = (_('Unable to get the replication relationship for volume '
return model_update '%s.'),
vref['id'])
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
else:
try:
# Reverse the role of the primary and secondary volumes,
# because the secondary volume becomes the primary in the
# fail-over status.
self.target_helpers.switch_relationship(
rel_info.get('name'))
except Exception as e:
msg = (_('Unable to fail-over the volume %(id)s to the '
'secondary back-end, because the replication '
'relationship is unable to switch: %(error)s'),
{"id": vref['id'], "error": e})
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
def list_replication_targets(self, context, vref): def replication_failback(self, volume):
# For the mode of global mirror, there is only one replication target. rel_info = self.target_helpers.get_relationship_info(volume)
return [{'target_device_id': self.target.get('target_device_id')}] if rel_info:
self.target_helpers.switch_relationship(rel_info.get('name'),
aux=False)
class StorwizeSVCReplicationMetroMirror( class StorwizeSVCReplicationMetroMirror(

View File

@ -283,6 +283,12 @@ class StorwizeSSH(object):
ssh_cmd = ['svctask', 'rmrcrelationship', relationship] ssh_cmd = ['svctask', 'rmrcrelationship', relationship]
self.run_ssh_assert_no_output(ssh_cmd) self.run_ssh_assert_no_output(ssh_cmd)
def switchrelationship(self, relationship, aux=True):
primary = 'aux' if aux else 'master'
ssh_cmd = ['svctask', 'switchrcrelationship', '-primary',
primary, relationship]
self.run_ssh_assert_no_output(ssh_cmd)
def startrcrelationship(self, rc_rel, primary=None): def startrcrelationship(self, rc_rel, primary=None):
ssh_cmd = ['svctask', 'startrcrelationship', '-force'] ssh_cmd = ['svctask', 'startrcrelationship', '-force']
if primary: if primary:
@ -1510,6 +1516,9 @@ class StorwizeHelpers(object):
relationship = self.ssh.lsrcrelationship(vol_attrs['RC_name']) relationship = self.ssh.lsrcrelationship(vol_attrs['RC_name'])
return relationship[0] if len(relationship) > 0 else None return relationship[0] if len(relationship) > 0 else None
def switch_relationship(self, relationship, aux=True):
self.ssh.switchrelationship(relationship, aux)
def get_partnership_info(self, system_name): def get_partnership_info(self, system_name):
partnership = self.ssh.lspartnership(system_name) partnership = self.ssh.lspartnership(system_name)
return partnership[0] if len(partnership) > 0 else None return partnership[0] if len(partnership) > 0 else None
@ -1843,18 +1852,21 @@ class StorwizeSVCCommonDriver(san.SanDriver,
FC and iSCSI within the StorwizeSVCCommonDriver class FC and iSCSI within the StorwizeSVCCommonDriver class
2.1 - Added replication V2 support to the global/metro mirror 2.1 - Added replication V2 support to the global/metro mirror
mode mode
2.1.1 - Update replication to version 2.1
""" """
VERSION = "2.1" VERSION = "2.1.1"
VDISKCOPYOPS_INTERVAL = 600 VDISKCOPYOPS_INTERVAL = 600
GLOBAL = 'global' GLOBAL = 'global'
METRO = 'metro' METRO = 'metro'
VALID_REP_TYPES = (GLOBAL, METRO) VALID_REP_TYPES = (GLOBAL, METRO)
FAILBACK_VALUE = 'default'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(StorwizeSVCCommonDriver, self).__init__(*args, **kwargs) super(StorwizeSVCCommonDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(storwize_svc_opts) self.configuration.append_config_values(storwize_svc_opts)
self._backend_name = self.configuration.safe_get('volume_backend_name')
self._helpers = StorwizeHelpers(self._run_ssh) self._helpers = StorwizeHelpers(self._run_ssh)
self._vdiskcopyops = {} self._vdiskcopyops = {}
self._vdiskcopyops_loop = None self._vdiskcopyops_loop = None
@ -1868,6 +1880,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
'system_id': None, 'system_id': None,
'code_level': None, 'code_level': None,
} }
self._active_backend_id = kwargs.get('active_backend_id')
# Since there are three replication modes supported by Storwize, # Since there are three replication modes supported by Storwize,
# this dictionary is used to map the replication types to certain # this dictionary is used to map the replication types to certain
@ -2427,46 +2440,115 @@ class StorwizeSVCCommonDriver(san.SanDriver,
copy_op[1]) copy_op[1])
LOG.debug("Exit: update volume copy status.") LOG.debug("Exit: update volume copy status.")
# #### V2 replication methods #### # # #### V2.1 replication methods #### #
def replication_enable(self, context, vref): def failover_host(self, context, volumes, secondary_id=None):
"""Enable replication on a replication capable volume."""
rep_type = self._validate_volume_rep_type(context, vref)
if rep_type not in self.replications:
msg = _("Driver does not support re-enabling replication for a "
"failed over volume.")
LOG.error(msg)
raise exception.ReplicationError(volume_id=vref['id'],
reason=msg)
return self.replications.get(rep_type).replication_enable(
context, vref)
def replication_disable(self, context, vref):
"""Disable replication on a replication capable volume."""
rep_type = self._validate_volume_rep_type(context, vref)
return self.replications[rep_type].replication_disable(
context, vref)
def replication_failover(self, context, vref, secondary):
"""Force failover to a secondary replication target.""" """Force failover to a secondary replication target."""
rep_type = self._validate_volume_rep_type(context, vref) self._validate_replication_enabled()
return self.replications[rep_type].replication_failover( if self.FAILBACK_VALUE == secondary_id:
context, vref, secondary) # In this case the administrator would like to fail back.
volume_update_list = self._replication_failback(context,
volumes)
return None, volume_update_list
def list_replication_targets(self, context, vref): # In this case the administrator would like to fail over.
"""Return the list of replication targets for a volume.""" failover_target = None
rep_type = self._validate_volume_rep_type(context, vref) for target in self._replication_targets:
if target['backend_id'] == secondary_id:
failover_target = target
break
if not failover_target:
msg = _("A valid secondary target MUST be specified in order "
"to failover.")
LOG.error(msg)
raise exception.InvalidReplicationTarget(reason=msg)
# When a volume is failed over, the secondary volume driver will not target_id = failover_target['backend_id']
# have replication configured, so in this case, gracefully handle volume_update_list = []
# request by returning no target volumes for volume in volumes:
if rep_type not in self.replications: rep_type = self._get_volume_replicated_type(context, volume)
targets = [] if rep_type:
else: replication = self.replications.get(rep_type)
targets = self.replications[rep_type].list_replication_targets( if replication.target.get('backend_id') == target_id:
context, vref) # Check if the target backend matches the replication type.
# If so, fail over the volume.
try:
replication.failover_volume_host(context,
volume, target_id)
volume_update_list.append(
{'volume_id': volume['id'],
'updates': {'replication_status': 'failed-over'}})
except exception.VolumeDriverException:
msg = (_LE('Unable to failover to the secondary. '
'Please make sure that the secondary '
'back-end is ready.'))
LOG.error(msg)
volume_update_list.append(
{'volume_id': volume['id'],
'updates': {'replication_status': 'error'}})
else:
# If the volume is not of replicated type, we need to
# force the status into error state so a user knows they
# do not have access to the volume.
volume_update_list.append(
{'volume_id': volume['id'],
'updates': {'status': 'error'}})
return {'volume_id': vref['id'], return target_id, volume_update_list
'targets': targets}
def _is_host_ready_for_failback(self, ctxt, volumes):
valid_sync_status = ('consistent_synchronized', 'consistent_stopped',
'synchronized', 'idling')
# Check the status of each volume to see if it is in
# a consistent status.
for volume in volumes:
rep_type = self._get_volume_replicated_type(ctxt, volume)
if rep_type:
replication = self.replications.get(rep_type)
if replication:
status = replication.get_relationship_status(volume)
# We need to make sure of that all the volumes are
# in the valid status to trigger a successful
# fail-back. False will be be returned even if only
# one volume is not ready.
if status not in valid_sync_status:
return False
else:
return False
else:
return False
return True
def _replication_failback(self, ctxt, volumes):
"""Fail back all the volume on the secondary backend."""
if not self._is_host_ready_for_failback(ctxt, volumes):
msg = _("The host is not ready to be failed back. Please "
"resynchronize the volumes and resume replication on the "
"Storwize backends.")
LOG.error(msg)
raise exception.VolumeDriverException(data=msg)
volume_update_list = []
for volume in volumes:
rep_type = self._get_volume_replicated_type(ctxt, volume)
if rep_type:
replication = self.replications.get(rep_type)
replication.replication_failback(volume)
volume_update_list.append(
{'volume_id': volume['id'],
'updates': {'replication_status': 'available'}})
else:
volume_update_list.append(
{'volume_id': volume['id'],
'updates': {'status': 'available'}})
return volume_update_list
def _validate_replication_enabled(self):
if not self._replication_enabled:
msg = _("Issuing a fail-over failed because replication is "
"not properly configured.")
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
def _validate_volume_rep_type(self, ctxt, volume): def _validate_volume_rep_type(self, ctxt, volume):
rep_type = self._get_volume_replicated_type(ctxt, volume) rep_type = self._get_volume_replicated_type(ctxt, volume)
@ -2535,8 +2617,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
remote_array['replication_mode'] = rep_mode remote_array['replication_mode'] = rep_mode
remote_array['san_ip'] = ( remote_array['san_ip'] = (
dev.get('san_ip')) dev.get('san_ip'))
remote_array['target_device_id'] = ( remote_array['backend_id'] = (
dev.get('target_device_id')) dev.get('backend_id'))
remote_array['san_login'] = ( remote_array['san_login'] = (
dev.get('san_login')) dev.get('san_login'))
remote_array['san_password'] = ( remote_array['san_password'] = (
@ -2564,7 +2646,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
'successfully established partnership ' 'successfully established partnership '
'with the replica Storwize target %(stor)s.'), 'with the replica Storwize target %(stor)s.'),
{'type': rep_type, {'type': rep_type,
'stor': target['target_device_id']}) 'stor': target['backend_id']})
LOG.error(msg) LOG.error(msg)
continue continue
@ -3021,6 +3103,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
data['pools'] = [self._build_pool_stats(pool) data['pools'] = [self._build_pool_stats(pool)
for pool in for pool in
self.configuration.storwize_svc_volpool_name] self.configuration.storwize_svc_volpool_name]
data['replication_enabled'] = self._replication_enabled
data['replication_targets'] = self._get_replication_targets(),
self._stats = data self._stats = data
def _build_pool_stats(self, pool): def _build_pool_stats(self, pool):
@ -3056,6 +3140,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
pool_stats.update({ pool_stats.update({
'replication_enabled': self._replication_enabled, 'replication_enabled': self._replication_enabled,
'replication_type': self._supported_replication_types, 'replication_type': self._supported_replication_types,
'replication_targets': self._get_replication_targets(),
'replication_count': len(self._replication_targets) 'replication_count': len(self._replication_targets)
}) })
elif self.replication: elif self.replication:
@ -3067,6 +3152,9 @@ class StorwizeSVCCommonDriver(san.SanDriver,
return pool_stats return pool_stats
def _get_replication_targets(self):
return [target['backend_id'] for target in self._replication_targets]
def _manage_input_check(self, ref): def _manage_input_check(self, ref):
"""Verify the input of manage function.""" """Verify the input of manage function."""
# Check that the reference is valid # Check that the reference is valid

View File

@ -80,9 +80,12 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
2.0 - Code refactor, split init file and placed shared methods for 2.0 - Code refactor, split init file and placed shared methods for
FC and iSCSI within the StorwizeSVCCommonDriver class FC and iSCSI within the StorwizeSVCCommonDriver class
2.0.1 - Added support for multiple pools with model update 2.0.1 - Added support for multiple pools with model update
2.1 - Added replication V2 support to the global/metro mirror
mode
2.1.1 - Update replication to version 2.1
""" """
VERSION = "2.0.1" VERSION = "2.1.1"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(StorwizeSVCFCDriver, self).__init__(*args, **kwargs) super(StorwizeSVCFCDriver, self).__init__(*args, **kwargs)

View File

@ -80,9 +80,12 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
2.0 - Code refactor, split init file and placed shared methods for 2.0 - Code refactor, split init file and placed shared methods for
FC and iSCSI within the StorwizeSVCCommonDriver class FC and iSCSI within the StorwizeSVCCommonDriver class
2.0.1 - Added support for multiple pools with model update 2.0.1 - Added support for multiple pools with model update
2.1 - Added replication V2 support to the global/metro mirror
mode
2.1.1 - Update replication to version 2.1
""" """
VERSION = "2.0.1" VERSION = "2.1.1"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(StorwizeSVCISCSIDriver, self).__init__(*args, **kwargs) super(StorwizeSVCISCSIDriver, self).__init__(*args, **kwargs)

View File

@ -0,0 +1,3 @@
---
features:
- Added replication v2.1 support to the IBM Storwize driver.

View File

@ -1,3 +0,0 @@
---
features:
- Adds managed v2 replication global and metro mirror modes support to the IBM Storwize driver.