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:
parent
2c8b3d457d
commit
24e4c3ea68
File diff suppressed because it is too large
Load Diff
@ -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') %
|
||||
|
@ -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
@ -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'],
|
||||
|
@ -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'],
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add consistent replication group support in Storwize Cinder driver.
|
Loading…
Reference in New Issue
Block a user