Add Storwize replication group support

This patch adds consistent replication group support for
Storwize/SVC driver.

Supported operations:
* Create replication consistency group
* Add volume to existing replication consistency group
* Adjust existing replication consistency group
* Enable replication on group
* Disable replication on group
* Fail over replication group back and forth

DocImpact Implements: blueprint replication-cg-svc

Change-Id: Id8d886feca938e99df14c35b17826b24d7c2b584
This commit is contained in:
Xiaoqin Li 2017-05-31 17:22:02 +08:00
parent 2c8b3d457d
commit 24e4c3ea68
7 changed files with 2307 additions and 324 deletions

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,6 @@ import six
from cinder import exception
from cinder.i18n import _
from cinder.objects import fields
from cinder import ssh_utils
from cinder import utils
from cinder.volume.drivers.ibm.storwize_svc import storwize_const
@ -101,8 +100,7 @@ class StorwizeSVCReplicationGlobalMirror(StorwizeSVCReplication):
rel_info = self.target_helpers.get_relationship_info(target_vol)
# Reverse the role of the primary and secondary volumes
self.target_helpers.switch_relationship(rel_info['name'])
return {'replication_status':
fields.ReplicationStatus.FAILED_OVER}
return
except Exception as e:
LOG.exception('Unable to fail-over the volume %(id)s to the '
'secondary back-end by switchrcrelationship '
@ -113,8 +111,13 @@ class StorwizeSVCReplicationGlobalMirror(StorwizeSVCReplication):
try:
self.target_helpers.stop_relationship(target_vol,
access=True)
return {'replication_status':
fields.ReplicationStatus.FAILED_OVER}
try:
self.target_helpers.start_relationship(target_vol, 'aux')
except exception.VolumeBackendAPIException as e:
LOG.error(
'Error running startrcrelationship due to %(err)s.',
{'err': e})
return
except Exception as e:
msg = (_('Unable to fail-over the volume %(id)s to the '
'secondary back-end, error: %(error)s') %
@ -129,9 +132,7 @@ class StorwizeSVCReplicationGlobalMirror(StorwizeSVCReplication):
try:
self.target_helpers.switch_relationship(rel_info['name'],
aux=False)
return {'replication_status':
fields.ReplicationStatus.ENABLED,
'status': 'available'}
return
except Exception as e:
msg = (_('Unable to fail-back the volume:%(vol)s to the '
'master back-end, error:%(error)s') %
@ -253,11 +254,14 @@ class StorwizeSVCReplicationGMCV(StorwizeSVCReplicationGlobalMirror):
{'vref': vref['name']})
# Make the aux volume writeable.
try:
self.target_helpers.stop_relationship(
storwize_const.REPLICA_AUX_VOL_PREFIX + vref['name'],
access=True)
return {'replication_status':
fields.ReplicationStatus.FAILED_OVER}
tgt_volume = storwize_const.REPLICA_AUX_VOL_PREFIX + vref.name
self.target_helpers.stop_relationship(tgt_volume, access=True)
try:
self.target_helpers.start_relationship(tgt_volume, 'aux')
except exception.VolumeBackendAPIException as e:
LOG.error('Error running startrcrelationship due to %(err)s.',
{'err': e})
return
except Exception as e:
msg = (_('Unable to fail-over the volume %(id)s to the '
'secondary back-end, error: %(error)s') %
@ -274,9 +278,7 @@ class StorwizeSVCReplicationGMCV(StorwizeSVCReplicationGlobalMirror):
try:
self.target_helpers.stop_relationship(tgt_volume, access=True)
self.target_helpers.start_relationship(tgt_volume, 'master')
return {'replication_status':
fields.ReplicationStatus.ENABLED,
'status': 'available'}
return
except Exception as e:
msg = (_('Unable to fail-back the volume:%(vol)s to the '
'master back-end, error:%(error)s') %

View File

@ -40,9 +40,14 @@ FAILBACK_VALUE = 'default'
DEFAULT_RC_TIMEOUT = 3600 * 24 * 7
DEFAULT_RC_INTERVAL = 5
DEFAULT_RCCG_TIMEOUT = 60 * 30
DEFAULT_RCCG_INTERVAL = 2
REPLICA_AUX_VOL_PREFIX = 'aux_'
REPLICA_CHG_VOL_PREFIX = 'chg_'
RCCG_PREFIX = 'rccg-'
# remote mirror copy status
REP_CONSIS_SYNC = 'consistent_synchronized'
REP_CONSIS_COPYING = 'consistent_copying'

File diff suppressed because it is too large Load Diff

View File

@ -92,9 +92,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
2.2 - Add CG capability to generic volume groups
2.2.1 - Add vdisk mirror/stretch cluster support
2.2.2 - Add npiv support
2.2.3 - Add replication group support
"""
VERSION = "2.2.2"
VERSION = "2.2.3"
# ThirdPartySystems wiki page
CI_WIKI_NAME = "IBM_STORAGE_CI"
@ -136,15 +137,16 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
"""
LOG.debug('enter: initialize_connection: volume %(vol)s with connector'
' %(conn)s', {'vol': volume['id'], 'conn': connector})
volume_name = self._get_target_vol(volume)
volume_name, backend_helper, node_state = self._get_vol_sys_info(
volume)
# Check if a host object is defined for this host name
host_name = self._helpers.get_host_from_connector(connector)
host_name = backend_helper.get_host_from_connector(connector)
if host_name is None:
# Host does not exist - add a new host to Storwize/SVC
host_name = self._helpers.create_host(connector)
host_name = backend_helper.create_host(connector)
volume_attributes = self._helpers.get_vdisk_attributes(volume_name)
volume_attributes = backend_helper.get_vdisk_attributes(volume_name)
if volume_attributes is None:
msg = (_('initialize_connection: Failed to get attributes'
' for volume %s.') % volume_name)
@ -152,8 +154,8 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
raise exception.VolumeDriverException(message=msg)
multihostmap = self.configuration.storwize_svc_multihostmap_enabled
lun_id = self._helpers.map_vol_to_host(volume_name, host_name,
multihostmap)
lun_id = backend_helper.map_vol_to_host(volume_name, host_name,
multihostmap)
try:
preferred_node = volume_attributes['preferred_node_id']
IO_group = volume_attributes['IO_group_id']
@ -168,7 +170,7 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
# Get preferred node and other nodes in I/O group
preferred_node_entry = None
io_group_nodes = []
for node in self._state['storage_nodes'].values():
for node in node_state['storage_nodes'].values():
if node['id'] == preferred_node:
preferred_node_entry = node
if node['IO_group'] == IO_group:
@ -192,19 +194,19 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
properties['target_lun'] = lun_id
properties['volume_id'] = volume['id']
conn_wwpns = self._helpers.get_conn_fc_wwpns(host_name)
conn_wwpns = backend_helper.get_conn_fc_wwpns(host_name)
# If conn_wwpns is empty, then that means that there were
# no target ports with visibility to any of the initiators
# so we return all target ports.
if len(conn_wwpns) == 0:
for node in self._state['storage_nodes'].values():
for node in node_state['storage_nodes'].values():
# The Storwize/svc release 7.7.0.0 introduced NPIV feature,
# Different commands be used to get the wwpns for host I/O
if self._state['code_level'] < (7, 7, 0, 0):
if node_state['code_level'] < (7, 7, 0, 0):
conn_wwpns.extend(node['WWPN'])
else:
npiv_wwpns = self._helpers.get_npiv_wwpns(
npiv_wwpns = backend_helper.get_npiv_wwpns(
node_id=node['id'],
host_io="yes")
conn_wwpns.extend(npiv_wwpns)
@ -218,8 +220,11 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
# specific for z/VM, refer to cinder bug 1323993
if "zvm_fcp" in connector:
properties['zvm_fcp'] = connector['zvm_fcp']
except Exception:
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error('initialize_connection: Failed to export volume '
'%(vol)s due to %(ex)s.', {'vol': volume.name,
'ex': ex})
self._do_terminate_connection(volume, connector)
LOG.error('initialize_connection: Failed '
'to collect return '
@ -272,7 +277,8 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
"""
LOG.debug('enter: terminate_connection: volume %(vol)s with connector'
' %(conn)s', {'vol': volume['id'], 'conn': connector})
vol_name = self._get_target_vol(volume)
vol_name, backend_helper, node_state = self._get_vol_sys_info(volume)
info = {}
if 'host' in connector:
# get host according to FC protocol
@ -282,7 +288,7 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
info = {'driver_volume_type': 'fibre_channel',
'data': {}}
host_name = self._helpers.get_host_from_connector(
host_name = backend_helper.get_host_from_connector(
connector, volume_name=vol_name)
if host_name is None:
msg = (_('terminate_connection: Failed to get host name from'
@ -294,11 +300,11 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
host_name = None
# Unmap volumes, if hostname is None, need to get value from vdiskmap
host_name = self._helpers.unmap_vol_from_host(vol_name, host_name)
host_name = backend_helper.unmap_vol_from_host(vol_name, host_name)
# Host_name could be none
if host_name:
resp = self._helpers.check_host_mapped_vols(host_name)
resp = backend_helper.check_host_mapped_vols(host_name)
if not len(resp):
LOG.info("Need to remove FC Zone, building initiator "
"target map.")
@ -308,14 +314,14 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
# Returning all target_wwpns in storage_nodes, since
# we cannot determine which wwpns are logged in during
# a VM deletion.
for node in self._state['storage_nodes'].values():
for node in node_state['storage_nodes'].values():
target_wwpns.extend(node['WWPN'])
init_targ_map = (self._make_initiator_target_map
(connector['wwpns'],
target_wwpns))
info['data'] = {'initiator_target_map': init_targ_map}
# No volume mapped to the host, delete host from array
self._helpers.delete_host(host_name)
backend_helper.delete_host(host_name)
LOG.debug('leave: terminate_connection: volume %(vol)s with '
'connector %(conn)s', {'vol': volume['id'],

View File

@ -91,9 +91,10 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
2.1.1 - Update replication to version 2.1
2.2 - Add CG capability to generic volume groups
2.2.1 - Add vdisk mirror/stretch cluster support
2.2.2 - Add replication group support
"""
VERSION = "2.2.1"
VERSION = "2.2.2"
# ThirdPartySystems wiki page
CI_WIKI_NAME = "IBM_STORAGE_CI"
@ -133,25 +134,26 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
"""
LOG.debug('enter: initialize_connection: volume %(vol)s with connector'
' %(conn)s', {'vol': volume['id'], 'conn': connector})
volume_name = self._get_target_vol(volume)
volume_name, backend_helper, node_state = self._get_vol_sys_info(
volume)
# Check if a host object is defined for this host name
host_name = self._helpers.get_host_from_connector(connector,
iscsi=True)
host_name = backend_helper.get_host_from_connector(connector,
iscsi=True)
if host_name is None:
# Host does not exist - add a new host to Storwize/SVC
host_name = self._helpers.create_host(connector, iscsi=True)
host_name = backend_helper.create_host(connector, iscsi=True)
chap_secret = self._helpers.get_chap_secret_for_host(host_name)
chap_secret = backend_helper.get_chap_secret_for_host(host_name)
chap_enabled = self.configuration.storwize_svc_iscsi_chap_enabled
if chap_enabled and chap_secret is None:
chap_secret = self._helpers.add_chap_secret_to_host(host_name)
chap_secret = backend_helper.add_chap_secret_to_host(host_name)
elif not chap_enabled and chap_secret:
LOG.warning('CHAP secret exists for host but CHAP is disabled.')
multihostmap = self.configuration.storwize_svc_multihostmap_enabled
lun_id = self._helpers.map_vol_to_host(volume_name, host_name,
multihostmap)
lun_id = backend_helper.map_vol_to_host(volume_name, host_name,
multihostmap)
try:
properties = self._get_single_iscsi_data(volume, connector,
@ -159,9 +161,14 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
multipath = connector.get('multipath', False)
if multipath:
properties = self._get_multi_iscsi_data(volume, connector,
lun_id, properties)
except Exception:
lun_id, properties,
backend_helper,
node_state)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error('initialize_connection: Failed to export volume '
'%(vol)s due to %(ex)s.', {'vol': volume.name,
'ex': ex})
self._do_terminate_connection(volume, connector)
LOG.error('initialize_connection: Failed '
'to collect return '
@ -182,8 +189,9 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
{'vol': volume['id'], 'conn': connector,
'lun_id': lun_id})
volume_name = self._get_target_vol(volume)
volume_attributes = self._helpers.get_vdisk_attributes(volume_name)
volume_name, backend_helper, node_state = self._get_vol_sys_info(
volume)
volume_attributes = backend_helper.get_vdisk_attributes(volume_name)
if volume_attributes is None:
msg = (_('_get_single_iscsi_data: Failed to get attributes'
' for volume %s.') % volume_name)
@ -204,7 +212,7 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
# Get preferred node and other nodes in I/O group
preferred_node_entry = None
io_group_nodes = []
for node in self._state['storage_nodes'].values():
for node in node_state['storage_nodes'].values():
if self.protocol not in node['enabled_protocols']:
continue
@ -252,14 +260,15 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
'prop': properties})
return properties
def _get_multi_iscsi_data(self, volume, connector, lun_id, properties):
def _get_multi_iscsi_data(self, volume, connector, lun_id, properties,
backend_helper, node_state):
LOG.debug('enter: _get_multi_iscsi_data: volume %(vol)s with '
'connector %(conn)s lun_id %(lun_id)s',
{'vol': volume.id, 'conn': connector,
'lun_id': lun_id})
try:
resp = self._helpers.ssh.lsportip()
resp = backend_helper.ssh.lsportip()
except Exception as ex:
msg = (_('_get_multi_iscsi_data: Failed to '
'get port ip because of exception: '
@ -270,7 +279,7 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
properties['target_iqns'] = []
properties['target_portals'] = []
properties['target_luns'] = []
for node in self._state['storage_nodes'].values():
for node in node_state['storage_nodes'].values():
for ip_data in resp:
if ip_data['node_id'] != node['id']:
continue
@ -329,15 +338,15 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
"""
LOG.debug('enter: terminate_connection: volume %(vol)s with connector'
' %(conn)s', {'vol': volume['id'], 'conn': connector})
vol_name = self._get_target_vol(volume)
vol_name, backend_helper, node_state = self._get_vol_sys_info(volume)
info = {}
if 'host' in connector:
# get host according to iSCSI protocol
info = {'driver_volume_type': 'iscsi',
'data': {}}
host_name = self._helpers.get_host_from_connector(connector,
iscsi=True)
host_name = backend_helper.get_host_from_connector(connector,
iscsi=True)
if host_name is None:
msg = (_('terminate_connection: Failed to get host name from'
' connector.'))
@ -348,13 +357,13 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
host_name = None
# Unmap volumes, if hostname is None, need to get value from vdiskmap
host_name = self._helpers.unmap_vol_from_host(vol_name, host_name)
host_name = backend_helper.unmap_vol_from_host(vol_name, host_name)
# Host_name could be none
if host_name:
resp = self._helpers.check_host_mapped_vols(host_name)
resp = backend_helper.check_host_mapped_vols(host_name)
if not len(resp):
self._helpers.delete_host(host_name)
backend_helper.delete_host(host_name)
LOG.debug('leave: terminate_connection: volume %(vol)s with '
'connector %(conn)s', {'vol': volume['id'],

View File

@ -0,0 +1,3 @@
---
features:
- Add consistent replication group support in Storwize Cinder driver.