Storwize: add hyperswap volume support
This patch adds hyperswap volume support for Storwize/SVC driver. New properties are added in volume type configuration: drivers:volume_topology sets to "hyperswap" would specify a hyperswap volume. drivers:peer_pool specifies the pool that hyperswap remote copy volume is stored. drivers:host_site specifies the site name of the host. New property is added in group type configuration: hyperswap_group_enabled="<is> True" would setup a hyperswap group. DocImpact Implements: blueprint svc-hyperswap-support Change-Id: I989d22d9580c1f44546f371af1fffded14ddcbe3
This commit is contained in:
parent
ada2ab7dac
commit
c0d471a424
File diff suppressed because it is too large
Load Diff
@ -47,6 +47,7 @@ REPLICA_AUX_VOL_PREFIX = 'aux_'
|
|||||||
REPLICA_CHG_VOL_PREFIX = 'chg_'
|
REPLICA_CHG_VOL_PREFIX = 'chg_'
|
||||||
|
|
||||||
RCCG_PREFIX = 'rccg-'
|
RCCG_PREFIX = 'rccg-'
|
||||||
|
HYPERCG_PREFIX = 'hycg-'
|
||||||
|
|
||||||
# remote mirror copy status
|
# remote mirror copy status
|
||||||
REP_CONSIS_SYNC = 'consistent_synchronized'
|
REP_CONSIS_SYNC = 'consistent_synchronized'
|
||||||
|
@ -125,6 +125,13 @@ storwize_svc_opts = [
|
|||||||
default=None,
|
default=None,
|
||||||
help='Specifies the name of the pool in which mirrored copy '
|
help='Specifies the name of the pool in which mirrored copy '
|
||||||
'is stored. Example: "pool2"'),
|
'is stored. Example: "pool2"'),
|
||||||
|
cfg.StrOpt('storwize_peer_pool',
|
||||||
|
default=None,
|
||||||
|
help='Specifies the name of the peer pool for hyperswap '
|
||||||
|
'volume, the peer pool must exist on the other site.'),
|
||||||
|
cfg.StrOpt('storwize_preferred_host_site',
|
||||||
|
default=None,
|
||||||
|
help='Specifies the preferred host site name.'),
|
||||||
cfg.IntOpt('cycle_period_seconds',
|
cfg.IntOpt('cycle_period_seconds',
|
||||||
default=300,
|
default=300,
|
||||||
min=60, max=86400,
|
min=60, max=86400,
|
||||||
@ -237,9 +244,11 @@ class StorwizeSSH(object):
|
|||||||
port.append(port_name)
|
port.append(port_name)
|
||||||
return port
|
return port
|
||||||
|
|
||||||
def mkhost(self, host_name, port_type, port_name):
|
def mkhost(self, host_name, port_type, port_name, site=None):
|
||||||
port = self._create_port_arg(port_type, port_name)
|
port = self._create_port_arg(port_type, port_name)
|
||||||
ssh_cmd = ['svctask', 'mkhost', '-force'] + port
|
ssh_cmd = ['svctask', 'mkhost', '-force'] + port
|
||||||
|
if site:
|
||||||
|
ssh_cmd += ['-site', '"%s"' % site]
|
||||||
ssh_cmd += ['-name', '"%s"' % host_name]
|
ssh_cmd += ['-name', '"%s"' % host_name]
|
||||||
return self.run_ssh_check_created(ssh_cmd)
|
return self.run_ssh_check_created(ssh_cmd)
|
||||||
|
|
||||||
@ -261,6 +270,10 @@ class StorwizeSSH(object):
|
|||||||
log_cmd = 'svctask chhost -chapsecret *** %s' % host
|
log_cmd = 'svctask chhost -chapsecret *** %s' % host
|
||||||
self.run_ssh_assert_no_output(ssh_cmd, log_cmd)
|
self.run_ssh_assert_no_output(ssh_cmd, log_cmd)
|
||||||
|
|
||||||
|
def chhost(self, host, site):
|
||||||
|
ssh_cmd = ['svctask', 'chhost', '-site', '"%s"' % site, '"%s"' % host]
|
||||||
|
self.run_ssh_assert_no_output(ssh_cmd)
|
||||||
|
|
||||||
def lsiscsiauth(self):
|
def lsiscsiauth(self):
|
||||||
ssh_cmd = ['svcinfo', 'lsiscsiauth', '-delim', '!']
|
ssh_cmd = ['svcinfo', 'lsiscsiauth', '-delim', '!']
|
||||||
return self.run_ssh_info(ssh_cmd, with_header=True)
|
return self.run_ssh_info(ssh_cmd, with_header=True)
|
||||||
@ -703,6 +716,29 @@ class StorwizeSSH(object):
|
|||||||
copy_id, '-vdisk', vdisk]
|
copy_id, '-vdisk', vdisk]
|
||||||
self.run_ssh_assert_no_output(ssh_cmd)
|
self.run_ssh_assert_no_output(ssh_cmd)
|
||||||
|
|
||||||
|
def mkvolume(self, name, size, units, pool, params):
|
||||||
|
ssh_cmd = ['svctask', 'mkvolume', '-name', name, '-pool',
|
||||||
|
'"%s"' % pool, '-size', size, '-unit', units] + params
|
||||||
|
return self.run_ssh_check_created(ssh_cmd)
|
||||||
|
|
||||||
|
def rmvolume(self, volume, force=True):
|
||||||
|
ssh_cmd = ['svctask', 'rmvolume']
|
||||||
|
if force:
|
||||||
|
ssh_cmd += ['-removehostmappings', '-removefcmaps',
|
||||||
|
'-removercrelationships']
|
||||||
|
ssh_cmd += ['"%s"' % volume]
|
||||||
|
self.run_ssh_assert_no_output(ssh_cmd)
|
||||||
|
|
||||||
|
def addvolumecopy(self, name, pool, params):
|
||||||
|
ssh_cmd = ['svctask', 'addvolumecopy', '-pool',
|
||||||
|
'"%s"' % pool] + params + ['"%s"' % name]
|
||||||
|
self.run_ssh_assert_no_output(ssh_cmd)
|
||||||
|
|
||||||
|
def rmvolumecopy(self, name, pool):
|
||||||
|
ssh_cmd = ['svctask', 'rmvolumecopy', '-pool',
|
||||||
|
'"%s"' % pool, '"%s"' % name]
|
||||||
|
self.run_ssh_assert_no_output(ssh_cmd)
|
||||||
|
|
||||||
|
|
||||||
class StorwizeHelpers(object):
|
class StorwizeHelpers(object):
|
||||||
|
|
||||||
@ -770,6 +806,7 @@ class StorwizeHelpers(object):
|
|||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
code_level = match_obj.group().split('.')
|
code_level = match_obj.group().split('.')
|
||||||
return {'code_level': tuple([int(x) for x in code_level]),
|
return {'code_level': tuple([int(x) for x in code_level]),
|
||||||
|
'topology': resp['topology'],
|
||||||
'system_name': resp['name'],
|
'system_name': resp['name'],
|
||||||
'system_id': resp['id']}
|
'system_id': resp['id']}
|
||||||
|
|
||||||
@ -815,7 +852,7 @@ class StorwizeHelpers(object):
|
|||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def select_io_group(self, state, opts):
|
def select_io_group(self, state, opts, pool):
|
||||||
selected_iog = 0
|
selected_iog = 0
|
||||||
iog_list = StorwizeHelpers._get_valid_requested_io_groups(state, opts)
|
iog_list = StorwizeHelpers._get_valid_requested_io_groups(state, opts)
|
||||||
if len(iog_list) == 0:
|
if len(iog_list) == 0:
|
||||||
@ -824,6 +861,25 @@ class StorwizeHelpers(object):
|
|||||||
'I/O groups are %(avail)s.')
|
'I/O groups are %(avail)s.')
|
||||||
% {'iogrp': opts['iogrp'],
|
% {'iogrp': opts['iogrp'],
|
||||||
'avail': state['available_iogrps']})
|
'avail': state['available_iogrps']})
|
||||||
|
|
||||||
|
site_iogrp = []
|
||||||
|
pool_data = self.get_pool_attrs(pool)
|
||||||
|
if 'site_id' in pool_data and pool_data['site_id']:
|
||||||
|
for node in state['storage_nodes'].values():
|
||||||
|
if pool_data['site_id'] == node['site_id']:
|
||||||
|
site_iogrp.append(node['IO_group'])
|
||||||
|
site_iogrp = list(map(int, site_iogrp))
|
||||||
|
iog_list = list(set(site_iogrp).intersection(iog_list))
|
||||||
|
if len(iog_list) == 0:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The storage system topology is hyperswap or '
|
||||||
|
'stretched, The site_id of pool %(pool)s is '
|
||||||
|
'%(site_id)s, the available I/O groups on this '
|
||||||
|
'site is %(site_iogrp)s, but the given I/O'
|
||||||
|
' group(s) is %(iogrp)s.')
|
||||||
|
% {'pool': pool, 'site_id': pool_data['site_id'],
|
||||||
|
'site_iogrp': site_iogrp, 'iogrp': opts['iogrp']})
|
||||||
|
|
||||||
iog_vdc = self.get_vdisk_count_by_io_group()
|
iog_vdc = self.get_vdisk_count_by_io_group()
|
||||||
LOG.debug("IO group current balance %s", iog_vdc)
|
LOG.debug("IO group current balance %s", iog_vdc)
|
||||||
min_vdisk_count = iog_vdc[iog_list[0]]
|
min_vdisk_count = iog_vdc[iog_list[0]]
|
||||||
@ -864,6 +920,8 @@ class StorwizeHelpers(object):
|
|||||||
node['ipv6'] = []
|
node['ipv6'] = []
|
||||||
node['enabled_protocols'] = []
|
node['enabled_protocols'] = []
|
||||||
nodes[node['id']] = node
|
nodes[node['id']] = node
|
||||||
|
node['site_id'] = (node_data['site_id']
|
||||||
|
if 'site_id' in node_data else None)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.handle_keyerror('lsnode', node_data)
|
self.handle_keyerror('lsnode', node_data)
|
||||||
return nodes
|
return nodes
|
||||||
@ -1037,7 +1095,7 @@ class StorwizeHelpers(object):
|
|||||||
LOG.debug('Leave: get_host_from_connector: host %s.', host_name)
|
LOG.debug('Leave: get_host_from_connector: host %s.', host_name)
|
||||||
return host_name
|
return host_name
|
||||||
|
|
||||||
def create_host(self, connector, iscsi=False):
|
def create_host(self, connector, iscsi=False, site=None):
|
||||||
"""Create a new host on the storage system.
|
"""Create a new host on the storage system.
|
||||||
|
|
||||||
We create a host name and associate it with the given connection
|
We create a host name and associate it with the given connection
|
||||||
@ -1091,7 +1149,8 @@ class StorwizeHelpers(object):
|
|||||||
|
|
||||||
# Create a host with one port
|
# Create a host with one port
|
||||||
port = ports.pop(0)
|
port = ports.pop(0)
|
||||||
self.ssh.mkhost(host_name, port[0], port[1])
|
# Host site_id is necessary for hyperswap volume.
|
||||||
|
self.ssh.mkhost(host_name, port[0], port[1], site)
|
||||||
|
|
||||||
# Add any additional ports to the host
|
# Add any additional ports to the host
|
||||||
for port in ports:
|
for port in ports:
|
||||||
@ -1101,6 +1160,9 @@ class StorwizeHelpers(object):
|
|||||||
{'host': connector['host'], 'host_name': host_name})
|
{'host': connector['host'], 'host_name': host_name})
|
||||||
return host_name
|
return host_name
|
||||||
|
|
||||||
|
def update_host(self, host_name, site_name):
|
||||||
|
self.ssh.chhost(host_name, site=site_name)
|
||||||
|
|
||||||
def delete_host(self, host_name):
|
def delete_host(self, host_name):
|
||||||
self.ssh.rmhost(host_name)
|
self.ssh.rmhost(host_name)
|
||||||
|
|
||||||
@ -1184,6 +1246,9 @@ class StorwizeHelpers(object):
|
|||||||
'replication': False,
|
'replication': False,
|
||||||
'nofmtdisk': config.storwize_svc_vol_nofmtdisk,
|
'nofmtdisk': config.storwize_svc_vol_nofmtdisk,
|
||||||
'mirror_pool': config.storwize_svc_mirror_pool,
|
'mirror_pool': config.storwize_svc_mirror_pool,
|
||||||
|
'volume_topology': None,
|
||||||
|
'peer_pool': config.storwize_peer_pool,
|
||||||
|
'host_site': config.storwize_preferred_host_site,
|
||||||
'cycle_period_seconds': config.cycle_period_seconds}
|
'cycle_period_seconds': config.cycle_period_seconds}
|
||||||
return opt
|
return opt
|
||||||
|
|
||||||
@ -1426,6 +1491,65 @@ class StorwizeHelpers(object):
|
|||||||
self.ssh.mkvdisk(name, size, units, mdiskgrp, opts, params)
|
self.ssh.mkvdisk(name, size, units, mdiskgrp, opts, params)
|
||||||
LOG.debug('Leave: _create_vdisk: volume %s.', name)
|
LOG.debug('Leave: _create_vdisk: volume %s.', name)
|
||||||
|
|
||||||
|
def _get_hyperswap_volume_create_params(self, opts):
|
||||||
|
# Storwize/svc use cli command mkvolume to create hyperswap volume.
|
||||||
|
# You must specify -thin with grainsize.
|
||||||
|
# You must specify either -thin or -compressed with warning.
|
||||||
|
params = []
|
||||||
|
LOG.debug('The I/O groups of a hyperswap volume will be selected by '
|
||||||
|
'storage.')
|
||||||
|
if opts['rsize'] != -1:
|
||||||
|
params.extend(['-buffersize', '%s%%' % str(opts['rsize']),
|
||||||
|
'-warning',
|
||||||
|
'%s%%' % six.text_type(opts['warning'])])
|
||||||
|
if not opts['autoexpand']:
|
||||||
|
params.append('-noautoexpand')
|
||||||
|
if opts['compression']:
|
||||||
|
params.append('-compressed')
|
||||||
|
else:
|
||||||
|
params.append('-thin')
|
||||||
|
params.extend(['-grainsize', six.text_type(opts['grainsize'])])
|
||||||
|
return params
|
||||||
|
|
||||||
|
def create_hyperswap_volume(self, vol_name, size, units, pool, opts):
|
||||||
|
vol_name = '"%s"' % vol_name
|
||||||
|
params = self._get_hyperswap_volume_create_params(opts)
|
||||||
|
self.ssh.mkvolume(vol_name, six.text_type(size), units, pool, params)
|
||||||
|
|
||||||
|
def convert_volume_to_hyperswap(self, vol_name, opts, state):
|
||||||
|
vol_name = '%s' % vol_name
|
||||||
|
if not self.is_system_topology_hyperswap(state):
|
||||||
|
reason = _('Convert volume to hyperswap failed, the system is '
|
||||||
|
'below release 7.6.0.0 or it is not hyperswap '
|
||||||
|
'topology.')
|
||||||
|
raise exception.VolumeDriverException(reason=reason)
|
||||||
|
else:
|
||||||
|
attr = self.get_vdisk_attributes(vol_name)
|
||||||
|
if attr is None:
|
||||||
|
msg = (_('convert_volume_to_hyperswap: Failed to get '
|
||||||
|
'attributes for volume %s.') % vol_name)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
pool = attr['mdisk_grp_name']
|
||||||
|
self.check_hyperswap_pool(pool, opts['peer_pool'])
|
||||||
|
hyper_pool = '%s' % opts['peer_pool']
|
||||||
|
params = self._get_hyperswap_volume_create_params(opts)
|
||||||
|
self.ssh.addvolumecopy(vol_name, hyper_pool, params)
|
||||||
|
|
||||||
|
def convert_hyperswap_volume_to_normal(self, vol_name, peer_pool):
|
||||||
|
vol_name = '%s' % vol_name
|
||||||
|
hyper_pool = '%s' % peer_pool
|
||||||
|
self.ssh.rmvolumecopy(vol_name, hyper_pool)
|
||||||
|
|
||||||
|
def delete_hyperswap_volume(self, volume, force):
|
||||||
|
"""Ensures that vdisk is not part of FC mapping and deletes it."""
|
||||||
|
if not self.is_vdisk_defined(volume):
|
||||||
|
LOG.warning('Tried to delete non-existent volume %s.', volume)
|
||||||
|
return
|
||||||
|
self.ensure_vdisk_no_fc_mappings(volume, allow_snaps=True,
|
||||||
|
allow_fctgt = True)
|
||||||
|
self.ssh.rmvolume(volume, force=force)
|
||||||
|
|
||||||
def get_vdisk_attributes(self, vdisk):
|
def get_vdisk_attributes(self, vdisk):
|
||||||
attrs = self.ssh.lsvdisk(vdisk)
|
attrs = self.ssh.lsvdisk(vdisk)
|
||||||
return attrs
|
return attrs
|
||||||
@ -1737,6 +1861,7 @@ class StorwizeHelpers(object):
|
|||||||
for map_id in mapping_ids:
|
for map_id in mapping_ids:
|
||||||
attrs = self._get_flashcopy_mapping_attributes(map_id)
|
attrs = self._get_flashcopy_mapping_attributes(map_id)
|
||||||
# We should ignore GMCV flash copies
|
# We should ignore GMCV flash copies
|
||||||
|
# Hyperswap flash copies are also ignored.
|
||||||
if not attrs or 'yes' == attrs['rc_controlled']:
|
if not attrs or 'yes' == attrs['rc_controlled']:
|
||||||
continue
|
continue
|
||||||
source = attrs['source_vdisk_name']
|
source = attrs['source_vdisk_name']
|
||||||
@ -2094,15 +2219,16 @@ class StorwizeHelpers(object):
|
|||||||
self.ssh.chvdisk(vdisk, ['-' + param, value])
|
self.ssh.chvdisk(vdisk, ['-' + param, value])
|
||||||
|
|
||||||
def change_vdisk_options(self, vdisk, changes, opts, state):
|
def change_vdisk_options(self, vdisk, changes, opts, state):
|
||||||
|
change_value = {'warning': '', 'easytier': '', 'autoexpand': ''}
|
||||||
if 'warning' in opts:
|
if 'warning' in opts:
|
||||||
opts['warning'] = '%s%%' % str(opts['warning'])
|
change_value['warning'] = '%s%%' % str(opts['warning'])
|
||||||
if 'easytier' in opts:
|
if 'easytier' in opts:
|
||||||
opts['easytier'] = 'on' if opts['easytier'] else 'off'
|
change_value['easytier'] = 'on' if opts['easytier'] else 'off'
|
||||||
if 'autoexpand' in opts:
|
if 'autoexpand' in opts:
|
||||||
opts['autoexpand'] = 'on' if opts['autoexpand'] else 'off'
|
change_value['autoexpand'] = 'on' if opts['autoexpand'] else 'off'
|
||||||
|
|
||||||
for key in changes:
|
for key in changes:
|
||||||
self.ssh.chvdisk(vdisk, ['-' + key, opts[key]])
|
self.ssh.chvdisk(vdisk, ['-' + key, change_value[key]])
|
||||||
|
|
||||||
def change_vdisk_iogrp(self, vdisk, state, iogrp):
|
def change_vdisk_iogrp(self, vdisk, state, iogrp):
|
||||||
if state['code_level'] < (6, 4, 0, 0):
|
if state['code_level'] < (6, 4, 0, 0):
|
||||||
@ -2150,6 +2276,60 @@ class StorwizeHelpers(object):
|
|||||||
def migratevdisk(self, vdisk, dest_pool, copy_id='0'):
|
def migratevdisk(self, vdisk, dest_pool, copy_id='0'):
|
||||||
self.ssh.migratevdisk(vdisk, dest_pool, copy_id)
|
self.ssh.migratevdisk(vdisk, dest_pool, copy_id)
|
||||||
|
|
||||||
|
def is_system_topology_hyperswap(self, state):
|
||||||
|
"""Returns True if the system version higher than 7.5 and the system
|
||||||
|
|
||||||
|
topology is hyperswap.
|
||||||
|
"""
|
||||||
|
if state['code_level'] < (7, 6, 0, 0):
|
||||||
|
LOG.debug('Hyperswap failure as the storage'
|
||||||
|
'code_level is %(code_level)s, below '
|
||||||
|
'the required 7.6.0.0.',
|
||||||
|
{'code_level': state['code_level']})
|
||||||
|
else:
|
||||||
|
if state['topology'] == 'hyperswap':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LOG.debug('Hyperswap failure as the storage system '
|
||||||
|
'topology is not hyperswap.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_hyperswap_pool(self, pool, peer_pool):
|
||||||
|
# Check the hyperswap pools.
|
||||||
|
if not peer_pool:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The peer pool is necessary for hyperswap volume, '
|
||||||
|
'please configure the peer pool.'))
|
||||||
|
pool_attr = self.get_pool_attrs(pool)
|
||||||
|
peer_pool_attr = self.get_pool_attrs(peer_pool)
|
||||||
|
if not peer_pool_attr:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The hyperswap peer pool %s '
|
||||||
|
'is invalid.') % peer_pool)
|
||||||
|
|
||||||
|
if not pool_attr['site_id'] or not peer_pool_attr['site_id']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The site_id of pools is necessary for hyperswap '
|
||||||
|
'volume, but there is no site_id in the pool or '
|
||||||
|
'peer pool.'))
|
||||||
|
|
||||||
|
if pool_attr['site_id'] == peer_pool_attr['site_id']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The hyperswap volume must be configured in two '
|
||||||
|
'independent sites, the pool %(pool)s is on the '
|
||||||
|
'same site as peer_pool %(peer_pool)s. ') %
|
||||||
|
{'pool': pool, 'peer_pool': peer_pool})
|
||||||
|
|
||||||
|
def is_volume_hyperswap(self, vol_name):
|
||||||
|
"""Returns True if the volume rcrelationship is activeactive."""
|
||||||
|
is_hyper_volume = False
|
||||||
|
vol_attrs = self.get_vdisk_attributes(vol_name)
|
||||||
|
if vol_attrs and vol_attrs['RC_name']:
|
||||||
|
relationship = self.ssh.lsrcrelationship(vol_attrs['RC_name'])
|
||||||
|
if relationship[0]['copy_type'] == 'activeactive':
|
||||||
|
is_hyper_volume = True
|
||||||
|
return is_hyper_volume
|
||||||
|
|
||||||
|
|
||||||
class CLIResponse(object):
|
class CLIResponse(object):
|
||||||
"""Parse SVC CLI output and generate iterable."""
|
"""Parse SVC CLI output and generate iterable."""
|
||||||
@ -2628,13 +2808,38 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
rep_type = self._get_volume_replicated_type(ctxt, volume)
|
rep_type = self._get_volume_replicated_type(ctxt, volume)
|
||||||
|
|
||||||
pool = utils.extract_host(volume['host'], 'pool')
|
pool = utils.extract_host(volume['host'], 'pool')
|
||||||
if opts['mirror_pool'] and rep_type:
|
model_update = None
|
||||||
reason = _('Create mirror volume with replication enabled is '
|
|
||||||
'not supported.')
|
if opts['volume_topology'] == 'hyperswap':
|
||||||
raise exception.InvalidInput(reason=reason)
|
LOG.debug('Volume %s to be created is a hyperswap volume.',
|
||||||
opts['iogrp'] = self._helpers.select_io_group(self._state, opts)
|
volume.name)
|
||||||
self._helpers.create_vdisk(volume['name'], str(volume['size']),
|
if not self._helpers.is_system_topology_hyperswap(self._state):
|
||||||
'gb', pool, opts)
|
reason = _('Create hyperswap volume failed, the system is '
|
||||||
|
'below release 7.6.0.0 or it is not hyperswap '
|
||||||
|
'topology.')
|
||||||
|
raise exception.InvalidInput(reason=reason)
|
||||||
|
if opts['mirror_pool'] or rep_type:
|
||||||
|
reason = _('Create hyperswap volume with streched cluster or '
|
||||||
|
'replication enabled is not supported.')
|
||||||
|
raise exception.InvalidInput(reason=reason)
|
||||||
|
if not opts['easytier']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The default easytier of hyperswap volume is '
|
||||||
|
'on, it does not support easytier off.'))
|
||||||
|
self._helpers.check_hyperswap_pool(pool, opts['peer_pool'])
|
||||||
|
hyperpool = '%s:%s' % (pool, opts['peer_pool'])
|
||||||
|
self._helpers.create_hyperswap_volume(volume.name,
|
||||||
|
volume.size, 'gb',
|
||||||
|
hyperpool, opts)
|
||||||
|
else:
|
||||||
|
if opts['mirror_pool'] and rep_type:
|
||||||
|
reason = _('Create mirror volume with replication enabled is '
|
||||||
|
'not supported.')
|
||||||
|
raise exception.InvalidInput(reason=reason)
|
||||||
|
opts['iogrp'] = self._helpers.select_io_group(self._state,
|
||||||
|
opts, pool)
|
||||||
|
self._helpers.create_vdisk(volume['name'], str(volume['size']),
|
||||||
|
'gb', pool, opts)
|
||||||
if opts['qos']:
|
if opts['qos']:
|
||||||
self._helpers.add_vdisk_qos(volume['name'], opts['qos'])
|
self._helpers.add_vdisk_qos(volume['name'], opts['qos'])
|
||||||
|
|
||||||
@ -2657,6 +2862,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
LOG.debug('enter: delete_volume: volume %s', volume['name'])
|
LOG.debug('enter: delete_volume: volume %s', volume['name'])
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
|
|
||||||
|
hyper_volume = self._helpers.is_volume_hyperswap(volume.name)
|
||||||
|
if hyper_volume:
|
||||||
|
LOG.debug('Volume %s to be deleted is a hyperswap '
|
||||||
|
'volume.', volume.name)
|
||||||
|
self._helpers.delete_hyperswap_volume(volume.name, False)
|
||||||
|
return
|
||||||
|
|
||||||
rep_type = self._get_volume_replicated_type(ctxt, volume)
|
rep_type = self._get_volume_replicated_type(ctxt, volume)
|
||||||
if rep_type:
|
if rep_type:
|
||||||
if self._aux_backend_helpers:
|
if self._aux_backend_helpers:
|
||||||
@ -2714,6 +2926,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
|
|
||||||
pool = utils.extract_host(source_vol['host'], 'pool')
|
pool = utils.extract_host(source_vol['host'], 'pool')
|
||||||
opts = self._get_vdisk_params(source_vol['volume_type_id'])
|
opts = self._get_vdisk_params(source_vol['volume_type_id'])
|
||||||
|
|
||||||
|
if opts['volume_topology'] == 'hyperswap':
|
||||||
|
msg = _('create_snapshot: Create snapshot to a '
|
||||||
|
'hyperswap volume is not allowed.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
self._helpers.create_copy(snapshot['volume_name'], snapshot['name'],
|
self._helpers.create_copy(snapshot['volume_name'], snapshot['name'],
|
||||||
snapshot['volume_id'], self.configuration,
|
snapshot['volume_id'], self.configuration,
|
||||||
opts, False, pool=pool)
|
opts, False, pool=pool)
|
||||||
@ -2787,6 +3006,19 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
if opts['qos']:
|
if opts['qos']:
|
||||||
self._helpers.add_vdisk_qos(tgt_volume['name'], opts['qos'])
|
self._helpers.add_vdisk_qos(tgt_volume['name'], opts['qos'])
|
||||||
|
|
||||||
|
if opts['volume_topology'] == 'hyperswap':
|
||||||
|
LOG.debug('The source volume %s to be cloned is a hyperswap '
|
||||||
|
'volume.', src_volume.name)
|
||||||
|
# Ensures the vdisk is not part of FC mapping.
|
||||||
|
# Otherwize convert it to hyperswap volume will be failed.
|
||||||
|
self._helpers.ensure_vdisk_no_fc_mappings(tgt_volume['name'],
|
||||||
|
allow_snaps=True,
|
||||||
|
allow_fctgt=False)
|
||||||
|
|
||||||
|
self._helpers.convert_volume_to_hyperswap(tgt_volume['name'],
|
||||||
|
opts,
|
||||||
|
self._state)
|
||||||
|
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
model_update = {'replication_status':
|
model_update = {'replication_status':
|
||||||
fields.ReplicationStatus.NOT_CAPABLE}
|
fields.ReplicationStatus.NOT_CAPABLE}
|
||||||
@ -2806,6 +3038,12 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
def _extend_volume_op(self, volume, new_size, old_size=None):
|
def _extend_volume_op(self, volume, new_size, old_size=None):
|
||||||
LOG.debug('enter: _extend_volume_op: volume %s', volume['id'])
|
LOG.debug('enter: _extend_volume_op: volume %s', volume['id'])
|
||||||
volume_name = self._get_target_vol(volume)
|
volume_name = self._get_target_vol(volume)
|
||||||
|
if self._helpers.is_volume_hyperswap(volume_name):
|
||||||
|
msg = _('_extend_volume_op: Extending a hyperswap volume is '
|
||||||
|
'not supported.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
|
||||||
ret = self._helpers.ensure_vdisk_no_fc_mappings(volume_name,
|
ret = self._helpers.ensure_vdisk_no_fc_mappings(volume_name,
|
||||||
allow_snaps=False)
|
allow_snaps=False)
|
||||||
if not ret:
|
if not ret:
|
||||||
@ -3930,6 +4168,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
LOG.debug('enter: migrate_volume: id=%(id)s, host=%(host)s',
|
LOG.debug('enter: migrate_volume: id=%(id)s, host=%(host)s',
|
||||||
{'id': volume['id'], 'host': host['host']})
|
{'id': volume['id'], 'host': host['host']})
|
||||||
|
|
||||||
|
# hyperswap volume doesn't support migrate
|
||||||
|
if self._helpers.is_volume_hyperswap(volume['name']):
|
||||||
|
msg = _('migrate_volume: Migrating a hyperswap volume is '
|
||||||
|
'not supported.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
|
||||||
false_ret = (False, None)
|
false_ret = (False, None)
|
||||||
dest_pool = self._helpers.can_migrate_to_host(host, self._state)
|
dest_pool = self._helpers.can_migrate_to_host(host, self._state)
|
||||||
if dest_pool is None:
|
if dest_pool is None:
|
||||||
@ -3948,8 +4193,14 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
self._helpers.migratevdisk(volume.name, dest_pool,
|
self._helpers.migratevdisk(volume.name, dest_pool,
|
||||||
copies['primary']['copy_id'])
|
copies['primary']['copy_id'])
|
||||||
else:
|
else:
|
||||||
self.add_vdisk_copy(volume.name, dest_pool, vol_type,
|
self._check_volume_copy_ops()
|
||||||
auto_delete=True)
|
if self._state['code_level'] < (7, 6, 0, 0):
|
||||||
|
new_op = self.add_vdisk_copy(volume.name, dest_pool,
|
||||||
|
vol_type)
|
||||||
|
self._add_vdisk_copy_op(ctxt, volume, new_op)
|
||||||
|
else:
|
||||||
|
self.add_vdisk_copy(volume.name, dest_pool, vol_type,
|
||||||
|
auto_delete=True)
|
||||||
|
|
||||||
LOG.debug('leave: migrate_volume: id=%(id)s, host=%(host)s',
|
LOG.debug('leave: migrate_volume: id=%(id)s, host=%(host)s',
|
||||||
{'id': volume.id, 'host': host['host']})
|
{'id': volume.id, 'host': host['host']})
|
||||||
@ -4021,6 +4272,98 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
self._helpers.change_relationship_cycleperiod(volume.name,
|
self._helpers.change_relationship_cycleperiod(volume.name,
|
||||||
new_cps)
|
new_cps)
|
||||||
|
|
||||||
|
def _check_hyperswap_retype_params(self, volume, new_opts, old_opts,
|
||||||
|
change_mirror, new_rep_type,
|
||||||
|
old_rep_type, old_pool,
|
||||||
|
new_pool, old_io_grp):
|
||||||
|
if new_opts['mirror_pool'] or old_opts['mirror_pool']:
|
||||||
|
msg = (_('Unable to retype volume %s: current action needs '
|
||||||
|
'volume-copy, it is not allowed for hyperswap '
|
||||||
|
'type.') % volume.name)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if new_rep_type or old_rep_type:
|
||||||
|
msg = _('Retype between replicated volume and hyperswap volume'
|
||||||
|
' is not allowed.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if (old_io_grp not in
|
||||||
|
StorwizeHelpers._get_valid_requested_io_groups(
|
||||||
|
self._state, new_opts)):
|
||||||
|
msg = _('Unable to retype: it is not allowed to change '
|
||||||
|
'hyperswap type and IO group at the same time.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if new_opts['volume_topology'] == 'hyperswap':
|
||||||
|
if old_pool != new_pool:
|
||||||
|
msg = (_('Unable to retype volume %s: current action needs '
|
||||||
|
'volume pool change, hyperswap volume does not '
|
||||||
|
'support pool change.') % volume.name)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if volume.previous_status == 'in-use':
|
||||||
|
msg = _('Retype an in-use volume to a hyperswap '
|
||||||
|
'volume is not allowed.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if not new_opts['easytier']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('The default easytier of hyperswap volume is '
|
||||||
|
'on, it does not support easytier off.'))
|
||||||
|
if (old_opts['volume_topology'] != 'hyperswap' and
|
||||||
|
self._helpers._get_vdisk_fc_mappings(volume.name)):
|
||||||
|
msg = _('Unable to retype: it is not allowed to change a '
|
||||||
|
'normal volume with snapshot to a hyperswap '
|
||||||
|
'volume.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
if (old_opts['volume_topology'] == 'hyperswap' and
|
||||||
|
old_opts['peer_pool'] != new_opts['peer_pool']):
|
||||||
|
msg = _('Unable to retype: it is not allowed to change a '
|
||||||
|
'hyperswap volume peer_pool.')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.InvalidInput(message=msg)
|
||||||
|
|
||||||
|
def _retype_hyperswap_volume(self, volume, host, old_opts, new_opts,
|
||||||
|
old_pool, new_pool, vdisk_changes,
|
||||||
|
need_copy, new_type):
|
||||||
|
if (old_opts['volume_topology'] != 'hyperswap' and
|
||||||
|
new_opts['volume_topology'] == 'hyperswap'):
|
||||||
|
LOG.debug('retype: Convert a normal volume %s to hyperswap '
|
||||||
|
'volume.', volume.name)
|
||||||
|
self._helpers.convert_volume_to_hyperswap(volume.name,
|
||||||
|
new_opts,
|
||||||
|
self._state)
|
||||||
|
elif (old_opts['volume_topology'] == 'hyperswap' and
|
||||||
|
new_opts['volume_topology'] != 'hyperswap'):
|
||||||
|
LOG.debug('retype: Convert a hyperswap volume %s to normal '
|
||||||
|
'volume.', volume.name)
|
||||||
|
if new_pool == old_pool:
|
||||||
|
self._helpers.convert_hyperswap_volume_to_normal(
|
||||||
|
volume.name,
|
||||||
|
old_opts['peer_pool'])
|
||||||
|
elif new_pool == old_opts['peer_pool']:
|
||||||
|
self._helpers.convert_hyperswap_volume_to_normal(
|
||||||
|
volume.name,
|
||||||
|
old_pool)
|
||||||
|
else:
|
||||||
|
rel_info = self._helpers.get_relationship_info(volume.name)
|
||||||
|
aux_vdisk = rel_info['aux_vdisk_name']
|
||||||
|
if need_copy:
|
||||||
|
self.add_vdisk_copy(aux_vdisk, old_opts['peer_pool'], new_type,
|
||||||
|
auto_delete=True)
|
||||||
|
elif vdisk_changes:
|
||||||
|
self._helpers.change_vdisk_options(aux_vdisk,
|
||||||
|
vdisk_changes,
|
||||||
|
new_opts, self._state)
|
||||||
|
if need_copy:
|
||||||
|
self.add_vdisk_copy(volume.name, old_pool, new_type,
|
||||||
|
auto_delete=True)
|
||||||
|
elif vdisk_changes:
|
||||||
|
self._helpers.change_vdisk_options(volume.name,
|
||||||
|
vdisk_changes,
|
||||||
|
new_opts, self._state)
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type, diff, host):
|
def retype(self, ctxt, volume, new_type, diff, host):
|
||||||
"""Convert the volume to be of the new type.
|
"""Convert the volume to be of the new type.
|
||||||
|
|
||||||
@ -4066,8 +4409,9 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
elif key in no_copy_keys:
|
elif key in no_copy_keys:
|
||||||
vdisk_changes.append(key)
|
vdisk_changes.append(key)
|
||||||
|
|
||||||
if (utils.extract_host(volume['host'], 'pool') !=
|
old_pool = utils.extract_host(volume['host'], 'pool')
|
||||||
utils.extract_host(host['host'], 'pool')):
|
new_pool = utils.extract_host(host['host'], 'pool')
|
||||||
|
if old_pool != new_pool:
|
||||||
need_copy = True
|
need_copy = True
|
||||||
|
|
||||||
if old_opts['mirror_pool'] != new_opts['mirror_pool']:
|
if old_opts['mirror_pool'] != new_opts['mirror_pool']:
|
||||||
@ -4078,50 +4422,69 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
new_rep_type = self._get_specs_replicated_type(new_type)
|
new_rep_type = self._get_specs_replicated_type(new_type)
|
||||||
old_rep_type = self._get_volume_replicated_type(ctxt, volume)
|
old_rep_type = self._get_volume_replicated_type(ctxt, volume)
|
||||||
old_io_grp = self._helpers.get_volume_io_group(volume['name'])
|
old_io_grp = self._helpers.get_volume_io_group(volume['name'])
|
||||||
new_io_grp = self._helpers.select_io_group(self._state, new_opts)
|
new_io_grp = self._helpers.select_io_group(self._state,
|
||||||
|
new_opts, new_pool)
|
||||||
|
|
||||||
self._verify_retype_params(volume, new_opts, old_opts, need_copy,
|
self._verify_retype_params(volume, new_opts, old_opts, need_copy,
|
||||||
change_mirror, new_rep_type, old_rep_type)
|
change_mirror, new_rep_type, old_rep_type)
|
||||||
if need_copy:
|
|
||||||
self._check_volume_copy_ops()
|
|
||||||
dest_pool = self._helpers.can_migrate_to_host(host, self._state)
|
|
||||||
if dest_pool is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
retype_iogrp_property(volume,
|
if old_opts['volume_topology'] or new_opts['volume_topology']:
|
||||||
new_io_grp, old_io_grp)
|
self._check_hyperswap_retype_params(volume, new_opts, old_opts,
|
||||||
try:
|
change_mirror, new_rep_type,
|
||||||
self.add_vdisk_copy(volume['name'], dest_pool, new_type,
|
old_rep_type, old_pool,
|
||||||
auto_delete=True)
|
new_pool, old_io_grp)
|
||||||
except exception.VolumeDriverException:
|
self._retype_hyperswap_volume(volume, host, old_opts, new_opts,
|
||||||
# roll back changing iogrp property
|
old_pool, new_pool, vdisk_changes,
|
||||||
retype_iogrp_property(volume, old_io_grp, new_io_grp)
|
need_copy, new_type)
|
||||||
msg = (_('Unable to retype: A copy of volume %s exists. '
|
|
||||||
'Retyping would exceed the limit of 2 copies.'),
|
|
||||||
volume['id'])
|
|
||||||
raise exception.VolumeDriverException(message=msg)
|
|
||||||
else:
|
else:
|
||||||
retype_iogrp_property(volume, new_io_grp, old_io_grp)
|
if need_copy:
|
||||||
|
self._check_volume_copy_ops()
|
||||||
|
dest_pool = self._helpers.can_migrate_to_host(host,
|
||||||
|
self._state)
|
||||||
|
if dest_pool is None:
|
||||||
|
return False
|
||||||
|
|
||||||
self._helpers.change_vdisk_options(volume['name'], vdisk_changes,
|
retype_iogrp_property(volume,
|
||||||
new_opts, self._state)
|
new_io_grp, old_io_grp)
|
||||||
if change_mirror:
|
try:
|
||||||
copies = self._helpers.get_vdisk_copies(volume.name)
|
if self._state['code_level'] < (7, 6, 0, 0):
|
||||||
if not old_opts['mirror_pool'] and new_opts['mirror_pool']:
|
new_op = self.add_vdisk_copy(volume.name, dest_pool,
|
||||||
# retype from non mirror vol to mirror vol
|
new_type)
|
||||||
self.add_vdisk_copy(volume['name'],
|
self._add_vdisk_copy_op(ctxt, volume, new_op)
|
||||||
new_opts['mirror_pool'], new_type)
|
else:
|
||||||
elif old_opts['mirror_pool'] and not new_opts['mirror_pool']:
|
self.add_vdisk_copy(volume.name, dest_pool, new_type,
|
||||||
# retype from mirror vol to non mirror vol
|
auto_delete=True)
|
||||||
secondary = copies['secondary']
|
except exception.VolumeDriverException:
|
||||||
if secondary:
|
# roll back changing iogrp property
|
||||||
self._helpers.rm_vdisk_copy(
|
retype_iogrp_property(volume, old_io_grp, new_io_grp)
|
||||||
volume.name, secondary['copy_id'])
|
msg = (_('Unable to retype: A copy of volume %s exists. '
|
||||||
else:
|
'Retyping would exceed the limit of 2 copies.'),
|
||||||
# migrate the second copy to another pool.
|
volume['id'])
|
||||||
self._helpers.migratevdisk(
|
raise exception.VolumeDriverException(message=msg)
|
||||||
volume.name, new_opts['mirror_pool'],
|
else:
|
||||||
copies['secondary']['copy_id'])
|
retype_iogrp_property(volume, new_io_grp, old_io_grp)
|
||||||
|
|
||||||
|
self._helpers.change_vdisk_options(volume['name'],
|
||||||
|
vdisk_changes,
|
||||||
|
new_opts, self._state)
|
||||||
|
if change_mirror:
|
||||||
|
copies = self._helpers.get_vdisk_copies(volume.name)
|
||||||
|
if not old_opts['mirror_pool'] and new_opts['mirror_pool']:
|
||||||
|
# retype from non mirror vol to mirror vol
|
||||||
|
self.add_vdisk_copy(volume['name'],
|
||||||
|
new_opts['mirror_pool'], new_type)
|
||||||
|
elif (old_opts['mirror_pool'] and
|
||||||
|
not new_opts['mirror_pool']):
|
||||||
|
# retype from mirror vol to non mirror vol
|
||||||
|
secondary = copies['secondary']
|
||||||
|
if secondary:
|
||||||
|
self._helpers.rm_vdisk_copy(
|
||||||
|
volume.name, secondary['copy_id'])
|
||||||
|
else:
|
||||||
|
# migrate the second copy to another pool.
|
||||||
|
self._helpers.migratevdisk(
|
||||||
|
volume.name, new_opts['mirror_pool'],
|
||||||
|
copies['secondary']['copy_id'])
|
||||||
if new_opts['qos']:
|
if new_opts['qos']:
|
||||||
# Add the new QoS setting to the volume. If the volume has an
|
# Add the new QoS setting to the volume. If the volume has an
|
||||||
# old QoS setting, it will be overwritten.
|
# old QoS setting, it will be overwritten.
|
||||||
@ -4222,7 +4585,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
vol_rep_type = None
|
vol_rep_type = None
|
||||||
rel_info = self._helpers.get_relationship_info(vdisk['name'])
|
rel_info = self._helpers.get_relationship_info(vdisk['name'])
|
||||||
copies = self._helpers.get_vdisk_copies(vdisk['name'])
|
copies = self._helpers.get_vdisk_copies(vdisk['name'])
|
||||||
if rel_info:
|
if rel_info and rel_info['copy_type'] != 'activeactive':
|
||||||
vol_rep_type = (
|
vol_rep_type = (
|
||||||
storwize_const.GMCV if
|
storwize_const.GMCV if
|
||||||
storwize_const.GMCV_MULTI == rel_info['cycling_mode']
|
storwize_const.GMCV_MULTI == rel_info['cycling_mode']
|
||||||
@ -4264,6 +4627,34 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
opts = self._get_vdisk_params(volume['volume_type_id'],
|
opts = self._get_vdisk_params(volume['volume_type_id'],
|
||||||
volume_metadata=
|
volume_metadata=
|
||||||
volume.get('volume_metadata'))
|
volume.get('volume_metadata'))
|
||||||
|
# Manage hyperswap volume
|
||||||
|
if rel_info and rel_info['copy_type'] == 'activeactive':
|
||||||
|
if opts['volume_topology'] != 'hyperswap':
|
||||||
|
msg = _("Failed to manage existing volume due to "
|
||||||
|
"the hyperswap volume to be managed is "
|
||||||
|
"mismatched with the provided non-hyperswap type.")
|
||||||
|
raise exception.ManageExistingVolumeTypeMismatch(
|
||||||
|
reason=msg)
|
||||||
|
aux_vdisk = rel_info['aux_vdisk_name']
|
||||||
|
aux_vol_attr = self._helpers.get_vdisk_attributes(aux_vdisk)
|
||||||
|
peer_pool = aux_vol_attr['mdisk_grp_name']
|
||||||
|
if opts['peer_pool'] != peer_pool:
|
||||||
|
msg = (_("Failed to manage existing hyperswap volume due "
|
||||||
|
"to peer pool mismatch. The peer pool of the "
|
||||||
|
"volume to be managed is %(vol_pool)s, but the "
|
||||||
|
"peer_pool of the chosen type is %(peer_pool)s.")
|
||||||
|
% {'vol_pool': peer_pool,
|
||||||
|
'peer_pool': opts['peer_pool']})
|
||||||
|
raise exception.ManageExistingVolumeTypeMismatch(
|
||||||
|
reason=msg)
|
||||||
|
else:
|
||||||
|
if opts['volume_topology'] == 'hyperswap':
|
||||||
|
msg = _("Failed to manage existing volume, the volume to "
|
||||||
|
"be managed is not a hyperswap volume, "
|
||||||
|
"mismatch with the provided hyperswap type.")
|
||||||
|
raise exception.ManageExistingVolumeTypeMismatch(
|
||||||
|
reason=msg)
|
||||||
|
|
||||||
resp = self._helpers.lsvdiskcopy(vdisk['name'])
|
resp = self._helpers.lsvdiskcopy(vdisk['name'])
|
||||||
expected_copy_num = 2 if opts['mirror_pool'] else 1
|
expected_copy_num = 2 if opts['mirror_pool'] else 1
|
||||||
if len(resp) != expected_copy_num:
|
if len(resp) != expected_copy_num:
|
||||||
@ -4318,7 +4709,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
msg = (_("Failed to manage existing volume due to "
|
msg = (_("Failed to manage existing volume due to "
|
||||||
"I/O group mismatch. The I/O group of the "
|
"I/O group mismatch. The I/O group of the "
|
||||||
"volume to be managed is %(vdisk_iogrp)s. I/O group"
|
"volume to be managed is %(vdisk_iogrp)s. I/O group"
|
||||||
"of the chosen type is %(opt_iogrp)s.") %
|
" of the chosen type is %(opt_iogrp)s.") %
|
||||||
{'vdisk_iogrp': vdisk['IO_group_name'],
|
{'vdisk_iogrp': vdisk['IO_group_name'],
|
||||||
'opt_iogrp': opts['iogrp']})
|
'opt_iogrp': opts['iogrp']})
|
||||||
raise exception.ManageExistingVolumeTypeMismatch(reason=msg)
|
raise exception.ManageExistingVolumeTypeMismatch(reason=msg)
|
||||||
@ -4393,9 +4784,11 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
return self._stats
|
return self._stats
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_rccg_name(group, grp_id=None):
|
def _get_rccg_name(group, grp_id=None, hyper_grp=False):
|
||||||
group_id = group.id if group else grp_id
|
group_id = group.id if group else grp_id
|
||||||
return storwize_const.RCCG_PREFIX + group_id[0:4] + '-' + group_id[-5:]
|
rccg = (storwize_const.HYPERCG_PREFIX
|
||||||
|
if hyper_grp else storwize_const.RCCG_PREFIX)
|
||||||
|
return rccg + group_id[0:4] + '-' + group_id[-5:]
|
||||||
|
|
||||||
# Add CG capability to generic volume groups
|
# Add CG capability to generic volume groups
|
||||||
def create_group(self, context, group):
|
def create_group(self, context, group):
|
||||||
@ -4405,7 +4798,6 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
:param group: the group object.
|
:param group: the group object.
|
||||||
:returns: model_update
|
:returns: model_update
|
||||||
"""
|
"""
|
||||||
|
|
||||||
LOG.debug("Creating group.")
|
LOG.debug("Creating group.")
|
||||||
|
|
||||||
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||||
@ -4418,7 +4810,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
|
|
||||||
support_grps = ['group_snapshot_enabled',
|
support_grps = ['group_snapshot_enabled',
|
||||||
'consistent_group_snapshot_enabled',
|
'consistent_group_snapshot_enabled',
|
||||||
'consistent_group_replication_enabled']
|
'consistent_group_replication_enabled',
|
||||||
|
'hyperswap_group_enabled']
|
||||||
supported_grp = False
|
supported_grp = False
|
||||||
for grp_spec in support_grps:
|
for grp_spec in support_grps:
|
||||||
if utils.is_group_a_type(group, grp_spec):
|
if utils.is_group_a_type(group, grp_spec):
|
||||||
@ -4442,6 +4835,14 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
'not supported.')
|
'not supported.')
|
||||||
model_update = {'status': fields.GroupStatus.ERROR}
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
return model_update
|
return model_update
|
||||||
|
opts = self._get_vdisk_params(vol_type_id)
|
||||||
|
if opts['volume_topology']:
|
||||||
|
# An unsupported configuration
|
||||||
|
LOG.error('Unable to create group: create consistent '
|
||||||
|
'snapshot group with a hyperswap volume type'
|
||||||
|
' is not supported.')
|
||||||
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
|
return model_update
|
||||||
|
|
||||||
# We'll rely on the generic group implementation if it is
|
# We'll rely on the generic group implementation if it is
|
||||||
# a non-consistent snapshot group.
|
# a non-consistent snapshot group.
|
||||||
@ -4489,6 +4890,33 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
"Exception: %(exception)s.",
|
"Exception: %(exception)s.",
|
||||||
{'rccg': rccg_name, 'exception': err})
|
{'rccg': rccg_name, 'exception': err})
|
||||||
model_update = {'status': fields.GroupStatus.ERROR}
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
|
return model_update
|
||||||
|
|
||||||
|
if utils.is_group_a_type(group, "hyperswap_group_enabled"):
|
||||||
|
if not self._helpers.is_system_topology_hyperswap(self._state):
|
||||||
|
LOG.error('Unable to create group: create group on '
|
||||||
|
'a system that does not support hyperswap.')
|
||||||
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
|
|
||||||
|
for vol_type_id in group.volume_type_ids:
|
||||||
|
opts = self._get_vdisk_params(vol_type_id)
|
||||||
|
if not opts['volume_topology']:
|
||||||
|
# An unsupported configuration
|
||||||
|
LOG.error('Unable to create group: create consistent '
|
||||||
|
'hyperswap group with non-hyperswap volume'
|
||||||
|
' type is not supported.')
|
||||||
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
|
return model_update
|
||||||
|
|
||||||
|
rccg_name = self._get_rccg_name(group, hyper_grp=True)
|
||||||
|
try:
|
||||||
|
self._helpers.create_rccg(
|
||||||
|
rccg_name, self._state['system_name'])
|
||||||
|
except exception.VolumeBackendAPIException as err:
|
||||||
|
LOG.error("Failed to create rccg %(rccg)s. "
|
||||||
|
"Exception: %(exception)s.",
|
||||||
|
{'rccg': group.name, 'exception': err})
|
||||||
|
model_update = {'status': fields.GroupStatus.ERROR}
|
||||||
return model_update
|
return model_update
|
||||||
|
|
||||||
def delete_group(self, context, group, volumes):
|
def delete_group(self, context, group, volumes):
|
||||||
@ -4503,10 +4931,12 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
|
|
||||||
# we'll rely on the generic group implementation if it is
|
# we'll rely on the generic group implementation if it is
|
||||||
# not a consistency group and not a consistency replication
|
# not a consistency group and not a consistency replication
|
||||||
# request.
|
# request and not a hyperswap group request.
|
||||||
if (not utils.is_group_a_cg_snapshot_type(group) and not
|
if (not utils.is_group_a_cg_snapshot_type(group) and not
|
||||||
utils.is_group_a_type(group,
|
utils.is_group_a_type(group,
|
||||||
"consistent_group_replication_enabled")):
|
"consistent_group_replication_enabled")
|
||||||
|
and not utils.is_group_a_type(group,
|
||||||
|
"hyperswap_group_enabled")):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
model_update = {'status': fields.GroupStatus.DELETED}
|
model_update = {'status': fields.GroupStatus.DELETED}
|
||||||
@ -4515,6 +4945,11 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
"consistent_group_replication_enabled"):
|
"consistent_group_replication_enabled"):
|
||||||
model_update, volumes_model_update = self._delete_replication_grp(
|
model_update, volumes_model_update = self._delete_replication_grp(
|
||||||
group, volumes)
|
group, volumes)
|
||||||
|
|
||||||
|
if utils.is_group_a_type(group, "hyperswap_group_enabled"):
|
||||||
|
model_update, volumes_model_update = self._delete_hyperswap_grp(
|
||||||
|
group, volumes)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
try:
|
try:
|
||||||
@ -4547,10 +4982,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
LOG.debug("Updating group.")
|
LOG.debug("Updating group.")
|
||||||
|
|
||||||
# we'll rely on the generic group implementation if it is not a
|
# we'll rely on the generic group implementation if it is not a
|
||||||
# consistency group request and not consistency replication request.
|
# consistency group request and not consistency replication request
|
||||||
|
# and not a hyperswap group request.
|
||||||
if (not utils.is_group_a_cg_snapshot_type(group) and not
|
if (not utils.is_group_a_cg_snapshot_type(group) and not
|
||||||
utils.is_group_a_type(group,
|
utils.is_group_a_type(group,
|
||||||
"consistent_group_replication_enabled")):
|
"consistent_group_replication_enabled")
|
||||||
|
and not utils.is_group_a_type(group,
|
||||||
|
"hyperswap_group_enabled")):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
if utils.is_group_a_type(group,
|
if utils.is_group_a_type(group,
|
||||||
@ -4558,6 +4996,10 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
return self._update_replication_grp(context, group, add_volumes,
|
return self._update_replication_grp(context, group, add_volumes,
|
||||||
remove_volumes)
|
remove_volumes)
|
||||||
|
|
||||||
|
if utils.is_group_a_type(group, "hyperswap_group_enabled"):
|
||||||
|
return self._update_hyperswap_group(context, group,
|
||||||
|
add_volumes, remove_volumes)
|
||||||
|
|
||||||
if utils.is_group_a_cg_snapshot_type(group):
|
if utils.is_group_a_cg_snapshot_type(group):
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
@ -4585,6 +5027,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
if utils.is_group_a_type(group, "hyperswap_group_enabled"):
|
||||||
|
# An unsupported configuration
|
||||||
|
msg = _('Unable to create hyperswap group: create hyperswap '
|
||||||
|
'group from a hyperswap group is not supported.')
|
||||||
|
LOG.exception(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
if not utils.is_group_a_cg_snapshot_type(group):
|
if not utils.is_group_a_cg_snapshot_type(group):
|
||||||
# we'll rely on the generic volume groups implementation if it is
|
# we'll rely on the generic volume groups implementation if it is
|
||||||
# not a consistency group request.
|
# not a consistency group request.
|
||||||
@ -4929,3 +5378,82 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||||||
"from group. Exception: %(exception)s.",
|
"from group. Exception: %(exception)s.",
|
||||||
{'vol': volume.name, 'exception': err})
|
{'vol': volume.name, 'exception': err})
|
||||||
return model_update, None, None
|
return model_update, None, None
|
||||||
|
|
||||||
|
def _delete_hyperswap_grp(self, group, volumes):
|
||||||
|
model_update = {'status': fields.GroupStatus.DELETED}
|
||||||
|
volumes_model_update = []
|
||||||
|
try:
|
||||||
|
rccg_name = self._get_rccg_name(group, hyper_grp=True)
|
||||||
|
self._helpers.delete_rccg(rccg_name)
|
||||||
|
except exception.VolumeBackendAPIException as err:
|
||||||
|
LOG.error("Failed to delete rccg %(rccg)s. "
|
||||||
|
"Exception: %(exception)s.",
|
||||||
|
{'rccg': group.name, 'exception': err})
|
||||||
|
model_update = {'status': fields.GroupStatus.ERROR_DELETING}
|
||||||
|
|
||||||
|
for volume in volumes:
|
||||||
|
try:
|
||||||
|
self._helpers.delete_hyperswap_volume(volume.name, True)
|
||||||
|
volumes_model_update.append(
|
||||||
|
{'id': volume.id, 'status': 'deleted'})
|
||||||
|
except exception.VolumeDriverException as err:
|
||||||
|
LOG.error("Failed to delete the volume %(vol)s of CG. "
|
||||||
|
"Exception: %(exception)s.",
|
||||||
|
{'vol': volume.name, 'exception': err})
|
||||||
|
volumes_model_update.append(
|
||||||
|
{'id': volume.id,
|
||||||
|
'status': 'error_deleting'})
|
||||||
|
return model_update, volumes_model_update
|
||||||
|
|
||||||
|
def _update_hyperswap_group(self, context, group,
|
||||||
|
add_volumes=None, remove_volumes=None):
|
||||||
|
LOG.info("Update hyperswap group: %(group)s. ", {'group': group.id})
|
||||||
|
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||||
|
rccg_name = self._get_rccg_name(group, hyper_grp=True)
|
||||||
|
if not self._helpers.get_rccg(rccg_name):
|
||||||
|
LOG.error("Failed to update rccg: %(grp)s does not exist in "
|
||||||
|
"backend.", {'grp': group.id})
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
return model_update, None, None
|
||||||
|
|
||||||
|
# Add remote copy relationship to rccg
|
||||||
|
for volume in add_volumes:
|
||||||
|
hyper_volume = self._helpers.is_volume_hyperswap(volume.name)
|
||||||
|
if not hyper_volume:
|
||||||
|
LOG.error("Failed to update rccg: the non hyperswap volume"
|
||||||
|
" of %(vol)s can't be added to hyperswap group.",
|
||||||
|
{'vol': volume.id})
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
return model_update, None, None
|
||||||
|
try:
|
||||||
|
rcrel = self._helpers.get_relationship_info(volume.name)
|
||||||
|
if not rcrel:
|
||||||
|
LOG.error("Failed to update rccg: remote copy relationship"
|
||||||
|
" of %(vol)s does not exist in backend.",
|
||||||
|
{'vol': volume.id})
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
else:
|
||||||
|
self._helpers.chrcrelationship(rcrel['name'], rccg_name)
|
||||||
|
except exception.VolumeBackendAPIException as err:
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
LOG.error("Failed to add the remote copy of volume %(vol)s to "
|
||||||
|
"rccg. Exception: %(exception)s.",
|
||||||
|
{'vol': volume.name, 'exception': err})
|
||||||
|
|
||||||
|
# Remove remote copy relationship from rccg
|
||||||
|
for volume in remove_volumes:
|
||||||
|
try:
|
||||||
|
rcrel = self._helpers.get_relationship_info(volume.name)
|
||||||
|
if not rcrel:
|
||||||
|
LOG.error("Failed to update rccg: remote copy relationship"
|
||||||
|
" of %(vol)s does not exit in backend.",
|
||||||
|
{'vol': volume.id})
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
else:
|
||||||
|
self._helpers.chrcrelationship(rcrel['name'])
|
||||||
|
except exception.VolumeBackendAPIException as err:
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
LOG.error("Failed to remove the remote copy of volume %(vol)s "
|
||||||
|
"from rccg. Exception: %(exception)s.",
|
||||||
|
{'vol': volume.name, 'exception': err})
|
||||||
|
return model_update, None, None
|
||||||
|
@ -95,9 +95,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
2.2.2 - Add npiv support
|
2.2.2 - Add npiv support
|
||||||
2.2.3 - Add replication group support
|
2.2.3 - Add replication group support
|
||||||
2.2.4 - Add backup snapshots support
|
2.2.4 - Add backup snapshots support
|
||||||
|
2.2.5 - Add hyperswap support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "2.2.3"
|
VERSION = "2.2.5"
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
# ThirdPartySystems wiki page
|
||||||
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
||||||
@ -123,10 +124,11 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
# attach the snapshot will be failed.
|
# attach the snapshot will be failed.
|
||||||
self._check_snapshot_replica_volume_status(snapshot)
|
self._check_snapshot_replica_volume_status(snapshot)
|
||||||
|
|
||||||
vol_attrs = ['id', 'name', 'display_name']
|
vol_attrs = ['id', 'name', 'volume_type_id', 'display_name']
|
||||||
Volume = collections.namedtuple('Volume', vol_attrs)
|
Volume = collections.namedtuple('Volume', vol_attrs)
|
||||||
volume = Volume(id=snapshot.id,
|
volume = Volume(id=snapshot.id,
|
||||||
name=snapshot.name,
|
name=snapshot.name,
|
||||||
|
volume_type_id=snapshot.volume_type_id,
|
||||||
display_name='backup-snapshot')
|
display_name='backup-snapshot')
|
||||||
|
|
||||||
return self.initialize_connection(volume, connector)
|
return self.initialize_connection(volume, connector)
|
||||||
@ -163,12 +165,36 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
else:
|
else:
|
||||||
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
||||||
volume)
|
volume)
|
||||||
|
opts = self._get_vdisk_params(volume.volume_type_id)
|
||||||
|
host_site = opts['host_site']
|
||||||
|
|
||||||
# Check if a host object is defined for this host name
|
# Check if a host object is defined for this host name
|
||||||
host_name = backend_helper.get_host_from_connector(connector)
|
host_name = backend_helper.get_host_from_connector(connector)
|
||||||
if host_name is None:
|
if host_name is None:
|
||||||
# Host does not exist - add a new host to Storwize/SVC
|
# Host does not exist - add a new host to Storwize/SVC
|
||||||
host_name = backend_helper.create_host(connector)
|
# The host_site is necessary for hyperswap volume.
|
||||||
|
if self._helpers.is_volume_hyperswap(
|
||||||
|
volume_name) and host_site is None:
|
||||||
|
msg = (_('There is no host_site configured for a hyperswap'
|
||||||
|
' volume %s.') % volume_name)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
|
host_name = backend_helper.create_host(connector, host_site)
|
||||||
|
else:
|
||||||
|
host_info = self._helpers.ssh.lshost(host=host_name)
|
||||||
|
if 'site_name' in host_info[0]:
|
||||||
|
if not host_info[0]['site_name'] and host_site:
|
||||||
|
self._helpers.update_host(host_name, host_site)
|
||||||
|
elif host_info[0]['site_name']:
|
||||||
|
ref_host_site = host_info[0]['site_name']
|
||||||
|
if host_site and host_site != ref_host_site:
|
||||||
|
msg = (_('The existing host site is %(ref_host_site)s,'
|
||||||
|
' but the new host site is %(host_site)s.') %
|
||||||
|
{'ref_host_site': ref_host_site,
|
||||||
|
'host_site': host_site})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
volume_attributes = backend_helper.get_vdisk_attributes(volume_name)
|
volume_attributes = backend_helper.get_vdisk_attributes(volume_name)
|
||||||
if volume_attributes is None:
|
if volume_attributes is None:
|
||||||
|
@ -95,9 +95,10 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
2.2.1 - Add vdisk mirror/stretch cluster support
|
2.2.1 - Add vdisk mirror/stretch cluster support
|
||||||
2.2.2 - Add replication group support
|
2.2.2 - Add replication group support
|
||||||
2.2.3 - Add backup snapshots support
|
2.2.3 - Add backup snapshots support
|
||||||
|
2.2.4 - Add hyperswap support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "2.2.2"
|
VERSION = "2.2.4"
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
# ThirdPartySystems wiki page
|
||||||
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
||||||
@ -123,10 +124,11 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
# attach the snapshot will be failed.
|
# attach the snapshot will be failed.
|
||||||
self._check_snapshot_replica_volume_status(snapshot)
|
self._check_snapshot_replica_volume_status(snapshot)
|
||||||
|
|
||||||
vol_attrs = ['id', 'name', 'display_name']
|
vol_attrs = ['id', 'name', 'volume_type_id', 'display_name']
|
||||||
Volume = collections.namedtuple('Volume', vol_attrs)
|
Volume = collections.namedtuple('Volume', vol_attrs)
|
||||||
volume = Volume(id=snapshot.id,
|
volume = Volume(id=snapshot.id,
|
||||||
name=snapshot.name,
|
name=snapshot.name,
|
||||||
|
volume_type_id=snapshot.volume_type_id,
|
||||||
display_name='backup-snapshot')
|
display_name='backup-snapshot')
|
||||||
|
|
||||||
return self.initialize_connection(volume, connector)
|
return self.initialize_connection(volume, connector)
|
||||||
@ -161,13 +163,38 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
|||||||
else:
|
else:
|
||||||
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
||||||
volume)
|
volume)
|
||||||
|
opts = self._get_vdisk_params(volume.volume_type_id)
|
||||||
|
host_site = opts['host_site']
|
||||||
|
|
||||||
# Check if a host object is defined for this host name
|
# Check if a host object is defined for this host name
|
||||||
host_name = backend_helper.get_host_from_connector(connector,
|
host_name = backend_helper.get_host_from_connector(connector,
|
||||||
iscsi=True)
|
iscsi=True)
|
||||||
if host_name is None:
|
if host_name is None:
|
||||||
# Host does not exist - add a new host to Storwize/SVC
|
# Host does not exist - add a new host to Storwize/SVC
|
||||||
host_name = backend_helper.create_host(connector, iscsi=True)
|
# The host_site is necessary for hyperswap volume
|
||||||
|
if self._helpers.is_volume_hyperswap(
|
||||||
|
volume_name) and host_site is None:
|
||||||
|
msg = (_('There is no host_site configured for a hyperswap'
|
||||||
|
' volume %s.') % volume_name)
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
|
host_name = backend_helper.create_host(connector, iscsi=True,
|
||||||
|
site = host_site)
|
||||||
|
else:
|
||||||
|
host_info = self._helpers.ssh.lshost(host=host_name)
|
||||||
|
if 'site_name' in host_info[0]:
|
||||||
|
if not host_info[0]['site_name'] and host_site:
|
||||||
|
self._helpers.update_host(host_name, host_site)
|
||||||
|
elif host_info[0]['site_name']:
|
||||||
|
ref_host_site = host_info[0]['site_name']
|
||||||
|
if host_site and host_site != ref_host_site:
|
||||||
|
msg = (_('The existing host site is %(ref_host_site)s,'
|
||||||
|
' but the new host site is %(host_site)s.') %
|
||||||
|
{'ref_host_site': ref_host_site,
|
||||||
|
'host_site': host_site})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
chap_secret = backend_helper.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
|
chap_enabled = self.configuration.storwize_svc_iscsi_chap_enabled
|
||||||
|
@ -315,6 +315,9 @@ driver:
|
|||||||
- multipath
|
- multipath
|
||||||
- iogrp
|
- iogrp
|
||||||
- mirror_pool
|
- mirror_pool
|
||||||
|
- volume_topology
|
||||||
|
- peer_pool
|
||||||
|
- host_site
|
||||||
|
|
||||||
These keys have the same semantics as their counterparts in the
|
These keys have the same semantics as their counterparts in the
|
||||||
configuration file. They are set similarly; for example, ``rsize=2`` or
|
configuration file. They are set similarly; for example, ``rsize=2`` or
|
||||||
@ -452,6 +455,12 @@ modify volume types, you can also change these extra specs properties:
|
|||||||
|
|
||||||
- mirror_pool
|
- mirror_pool
|
||||||
|
|
||||||
|
- volume_topology
|
||||||
|
|
||||||
|
- peer_pool
|
||||||
|
|
||||||
|
- host_site
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
When you change the ``rsize``, ``grainsize`` or ``compression``
|
When you change the ``rsize``, ``grainsize`` or ``compression``
|
||||||
@ -515,3 +524,26 @@ default as the ``backend_id``:
|
|||||||
If the synchronization is not done manually, Storwize Block Storage
|
If the synchronization is not done manually, Storwize Block Storage
|
||||||
service driver will perform the synchronization and do the failback
|
service driver will perform the synchronization and do the failback
|
||||||
after the synchronization is finished.
|
after the synchronization is finished.
|
||||||
|
|
||||||
|
Hyperswap Volumes
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A hyperswap volume is created with a volume-type that has the extra spec
|
||||||
|
``drivers:volume_topology`` set to ``hyperswap``.
|
||||||
|
To support hyperswap volumes, IBM Storwize/SVC firmware version 7.6.0 or
|
||||||
|
later is required.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ cinder type-create hyper_type
|
||||||
|
$ cinder type-key hyper_type set drivers:volume_topology=hyperswap \
|
||||||
|
drivers:peer_pool=Pool_site2 drivers:host_site=site1
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The property ``rsize`` is considered as ``buffersize`` for hyperswap
|
||||||
|
volume.
|
||||||
|
The hyperswap property ``iogrp`` is selected by storage.
|
||||||
|
|
||||||
|
A group is created as a hyperswap group with a group-type that has the
|
||||||
|
group spec ``hyperswap_group_enabled`` set to ``<is> True``.
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added hyperswap volume and group support in Storwize cinder driver.
|
||||||
|
Storwize/svc versions prior to 7.6 do not support this feature.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user