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_'
|
||||
|
||||
RCCG_PREFIX = 'rccg-'
|
||||
HYPERCG_PREFIX = 'hycg-'
|
||||
|
||||
# remote mirror copy status
|
||||
REP_CONSIS_SYNC = 'consistent_synchronized'
|
||||
|
@ -125,6 +125,13 @@ storwize_svc_opts = [
|
||||
default=None,
|
||||
help='Specifies the name of the pool in which mirrored copy '
|
||||
'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',
|
||||
default=300,
|
||||
min=60, max=86400,
|
||||
@ -237,9 +244,11 @@ class StorwizeSSH(object):
|
||||
port.append(port_name)
|
||||
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)
|
||||
ssh_cmd = ['svctask', 'mkhost', '-force'] + port
|
||||
if site:
|
||||
ssh_cmd += ['-site', '"%s"' % site]
|
||||
ssh_cmd += ['-name', '"%s"' % host_name]
|
||||
return self.run_ssh_check_created(ssh_cmd)
|
||||
|
||||
@ -261,6 +270,10 @@ class StorwizeSSH(object):
|
||||
log_cmd = 'svctask chhost -chapsecret *** %s' % host
|
||||
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):
|
||||
ssh_cmd = ['svcinfo', 'lsiscsiauth', '-delim', '!']
|
||||
return self.run_ssh_info(ssh_cmd, with_header=True)
|
||||
@ -703,6 +716,29 @@ class StorwizeSSH(object):
|
||||
copy_id, '-vdisk', vdisk]
|
||||
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):
|
||||
|
||||
@ -770,6 +806,7 @@ class StorwizeHelpers(object):
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
code_level = match_obj.group().split('.')
|
||||
return {'code_level': tuple([int(x) for x in code_level]),
|
||||
'topology': resp['topology'],
|
||||
'system_name': resp['name'],
|
||||
'system_id': resp['id']}
|
||||
|
||||
@ -815,7 +852,7 @@ class StorwizeHelpers(object):
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return res
|
||||
|
||||
def select_io_group(self, state, opts):
|
||||
def select_io_group(self, state, opts, pool):
|
||||
selected_iog = 0
|
||||
iog_list = StorwizeHelpers._get_valid_requested_io_groups(state, opts)
|
||||
if len(iog_list) == 0:
|
||||
@ -824,6 +861,25 @@ class StorwizeHelpers(object):
|
||||
'I/O groups are %(avail)s.')
|
||||
% {'iogrp': opts['iogrp'],
|
||||
'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()
|
||||
LOG.debug("IO group current balance %s", iog_vdc)
|
||||
min_vdisk_count = iog_vdc[iog_list[0]]
|
||||
@ -864,6 +920,8 @@ class StorwizeHelpers(object):
|
||||
node['ipv6'] = []
|
||||
node['enabled_protocols'] = []
|
||||
nodes[node['id']] = node
|
||||
node['site_id'] = (node_data['site_id']
|
||||
if 'site_id' in node_data else None)
|
||||
except KeyError:
|
||||
self.handle_keyerror('lsnode', node_data)
|
||||
return nodes
|
||||
@ -1037,7 +1095,7 @@ class StorwizeHelpers(object):
|
||||
LOG.debug('Leave: get_host_from_connector: host %s.', 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.
|
||||
|
||||
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
|
||||
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
|
||||
for port in ports:
|
||||
@ -1101,6 +1160,9 @@ class StorwizeHelpers(object):
|
||||
{'host': connector['host'], 'host_name': 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):
|
||||
self.ssh.rmhost(host_name)
|
||||
|
||||
@ -1184,6 +1246,9 @@ class StorwizeHelpers(object):
|
||||
'replication': False,
|
||||
'nofmtdisk': config.storwize_svc_vol_nofmtdisk,
|
||||
'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}
|
||||
return opt
|
||||
|
||||
@ -1426,6 +1491,65 @@ class StorwizeHelpers(object):
|
||||
self.ssh.mkvdisk(name, size, units, mdiskgrp, opts, params)
|
||||
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):
|
||||
attrs = self.ssh.lsvdisk(vdisk)
|
||||
return attrs
|
||||
@ -1737,6 +1861,7 @@ class StorwizeHelpers(object):
|
||||
for map_id in mapping_ids:
|
||||
attrs = self._get_flashcopy_mapping_attributes(map_id)
|
||||
# We should ignore GMCV flash copies
|
||||
# Hyperswap flash copies are also ignored.
|
||||
if not attrs or 'yes' == attrs['rc_controlled']:
|
||||
continue
|
||||
source = attrs['source_vdisk_name']
|
||||
@ -2094,15 +2219,16 @@ class StorwizeHelpers(object):
|
||||
self.ssh.chvdisk(vdisk, ['-' + param, value])
|
||||
|
||||
def change_vdisk_options(self, vdisk, changes, opts, state):
|
||||
change_value = {'warning': '', 'easytier': '', 'autoexpand': ''}
|
||||
if 'warning' in opts:
|
||||
opts['warning'] = '%s%%' % str(opts['warning'])
|
||||
change_value['warning'] = '%s%%' % str(opts['warning'])
|
||||
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:
|
||||
opts['autoexpand'] = 'on' if opts['autoexpand'] else 'off'
|
||||
change_value['autoexpand'] = 'on' if opts['autoexpand'] else 'off'
|
||||
|
||||
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):
|
||||
if state['code_level'] < (6, 4, 0, 0):
|
||||
@ -2150,6 +2276,60 @@ class StorwizeHelpers(object):
|
||||
def migratevdisk(self, vdisk, dest_pool, copy_id='0'):
|
||||
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):
|
||||
"""Parse SVC CLI output and generate iterable."""
|
||||
@ -2628,13 +2808,38 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
rep_type = self._get_volume_replicated_type(ctxt, volume)
|
||||
|
||||
pool = utils.extract_host(volume['host'], 'pool')
|
||||
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)
|
||||
self._helpers.create_vdisk(volume['name'], str(volume['size']),
|
||||
'gb', pool, opts)
|
||||
model_update = None
|
||||
|
||||
if opts['volume_topology'] == 'hyperswap':
|
||||
LOG.debug('Volume %s to be created is a hyperswap volume.',
|
||||
volume.name)
|
||||
if not self._helpers.is_system_topology_hyperswap(self._state):
|
||||
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']:
|
||||
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'])
|
||||
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)
|
||||
if rep_type:
|
||||
if self._aux_backend_helpers:
|
||||
@ -2714,6 +2926,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
|
||||
pool = utils.extract_host(source_vol['host'], 'pool')
|
||||
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'],
|
||||
snapshot['volume_id'], self.configuration,
|
||||
opts, False, pool=pool)
|
||||
@ -2787,6 +3006,19 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
if 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()
|
||||
model_update = {'replication_status':
|
||||
fields.ReplicationStatus.NOT_CAPABLE}
|
||||
@ -2806,6 +3038,12 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
def _extend_volume_op(self, volume, new_size, old_size=None):
|
||||
LOG.debug('enter: _extend_volume_op: volume %s', volume['id'])
|
||||
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,
|
||||
allow_snaps=False)
|
||||
if not ret:
|
||||
@ -3930,6 +4168,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
LOG.debug('enter: migrate_volume: id=%(id)s, host=%(host)s',
|
||||
{'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)
|
||||
dest_pool = self._helpers.can_migrate_to_host(host, self._state)
|
||||
if dest_pool is None:
|
||||
@ -3948,8 +4193,14 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
self._helpers.migratevdisk(volume.name, dest_pool,
|
||||
copies['primary']['copy_id'])
|
||||
else:
|
||||
self.add_vdisk_copy(volume.name, dest_pool, vol_type,
|
||||
auto_delete=True)
|
||||
self._check_volume_copy_ops()
|
||||
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',
|
||||
{'id': volume.id, 'host': host['host']})
|
||||
@ -4021,6 +4272,98 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
self._helpers.change_relationship_cycleperiod(volume.name,
|
||||
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):
|
||||
"""Convert the volume to be of the new type.
|
||||
|
||||
@ -4066,8 +4409,9 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
elif key in no_copy_keys:
|
||||
vdisk_changes.append(key)
|
||||
|
||||
if (utils.extract_host(volume['host'], 'pool') !=
|
||||
utils.extract_host(host['host'], 'pool')):
|
||||
old_pool = utils.extract_host(volume['host'], 'pool')
|
||||
new_pool = utils.extract_host(host['host'], 'pool')
|
||||
if old_pool != new_pool:
|
||||
need_copy = True
|
||||
|
||||
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)
|
||||
old_rep_type = self._get_volume_replicated_type(ctxt, volume)
|
||||
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,
|
||||
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,
|
||||
new_io_grp, old_io_grp)
|
||||
try:
|
||||
self.add_vdisk_copy(volume['name'], dest_pool, new_type,
|
||||
auto_delete=True)
|
||||
except exception.VolumeDriverException:
|
||||
# roll back changing iogrp property
|
||||
retype_iogrp_property(volume, old_io_grp, new_io_grp)
|
||||
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)
|
||||
if old_opts['volume_topology'] or new_opts['volume_topology']:
|
||||
self._check_hyperswap_retype_params(volume, new_opts, old_opts,
|
||||
change_mirror, new_rep_type,
|
||||
old_rep_type, old_pool,
|
||||
new_pool, old_io_grp)
|
||||
self._retype_hyperswap_volume(volume, host, old_opts, new_opts,
|
||||
old_pool, new_pool, vdisk_changes,
|
||||
need_copy, new_type)
|
||||
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,
|
||||
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'])
|
||||
retype_iogrp_property(volume,
|
||||
new_io_grp, old_io_grp)
|
||||
try:
|
||||
if self._state['code_level'] < (7, 6, 0, 0):
|
||||
new_op = self.add_vdisk_copy(volume.name, dest_pool,
|
||||
new_type)
|
||||
self._add_vdisk_copy_op(ctxt, volume, new_op)
|
||||
else:
|
||||
self.add_vdisk_copy(volume.name, dest_pool, new_type,
|
||||
auto_delete=True)
|
||||
except exception.VolumeDriverException:
|
||||
# roll back changing iogrp property
|
||||
retype_iogrp_property(volume, old_io_grp, new_io_grp)
|
||||
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:
|
||||
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']:
|
||||
# Add the new QoS setting to the volume. If the volume has an
|
||||
# old QoS setting, it will be overwritten.
|
||||
@ -4222,7 +4585,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
vol_rep_type = None
|
||||
rel_info = self._helpers.get_relationship_info(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 = (
|
||||
storwize_const.GMCV if
|
||||
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'],
|
||||
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'])
|
||||
expected_copy_num = 2 if opts['mirror_pool'] else 1
|
||||
if len(resp) != expected_copy_num:
|
||||
@ -4318,7 +4709,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
msg = (_("Failed to manage existing volume due to "
|
||||
"I/O group mismatch. The I/O group of the "
|
||||
"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'],
|
||||
'opt_iogrp': opts['iogrp']})
|
||||
raise exception.ManageExistingVolumeTypeMismatch(reason=msg)
|
||||
@ -4393,9 +4784,11 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
return self._stats
|
||||
|
||||
@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
|
||||
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
|
||||
def create_group(self, context, group):
|
||||
@ -4405,7 +4798,6 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
:param group: the group object.
|
||||
:returns: model_update
|
||||
"""
|
||||
|
||||
LOG.debug("Creating group.")
|
||||
|
||||
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||
@ -4418,7 +4810,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
|
||||
support_grps = ['group_snapshot_enabled',
|
||||
'consistent_group_snapshot_enabled',
|
||||
'consistent_group_replication_enabled']
|
||||
'consistent_group_replication_enabled',
|
||||
'hyperswap_group_enabled']
|
||||
supported_grp = False
|
||||
for grp_spec in support_grps:
|
||||
if utils.is_group_a_type(group, grp_spec):
|
||||
@ -4442,6 +4835,14 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
'not supported.')
|
||||
model_update = {'status': fields.GroupStatus.ERROR}
|
||||
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
|
||||
# a non-consistent snapshot group.
|
||||
@ -4489,6 +4890,33 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
"Exception: %(exception)s.",
|
||||
{'rccg': rccg_name, 'exception': err})
|
||||
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
|
||||
|
||||
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
|
||||
# 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
|
||||
utils.is_group_a_type(group,
|
||||
"consistent_group_replication_enabled")):
|
||||
utils.is_group_a_type(group,
|
||||
"consistent_group_replication_enabled")
|
||||
and not utils.is_group_a_type(group,
|
||||
"hyperswap_group_enabled")):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupStatus.DELETED}
|
||||
@ -4515,6 +4945,11 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
"consistent_group_replication_enabled"):
|
||||
model_update, volumes_model_update = self._delete_replication_grp(
|
||||
group, volumes)
|
||||
|
||||
if utils.is_group_a_type(group, "hyperswap_group_enabled"):
|
||||
model_update, volumes_model_update = self._delete_hyperswap_grp(
|
||||
group, volumes)
|
||||
|
||||
else:
|
||||
for volume in volumes:
|
||||
try:
|
||||
@ -4547,10 +4982,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
LOG.debug("Updating group.")
|
||||
|
||||
# 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
|
||||
utils.is_group_a_type(group,
|
||||
"consistent_group_replication_enabled")):
|
||||
utils.is_group_a_type(group,
|
||||
"consistent_group_replication_enabled")
|
||||
and not utils.is_group_a_type(group,
|
||||
"hyperswap_group_enabled")):
|
||||
raise NotImplementedError()
|
||||
|
||||
if utils.is_group_a_type(group,
|
||||
@ -4558,6 +4996,10 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
return self._update_replication_grp(context, group, add_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):
|
||||
return None, None, None
|
||||
|
||||
@ -4585,6 +5027,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
LOG.exception(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):
|
||||
# we'll rely on the generic volume groups implementation if it is
|
||||
# not a consistency group request.
|
||||
@ -4929,3 +5378,82 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
||||
"from group. Exception: %(exception)s.",
|
||||
{'vol': volume.name, 'exception': err})
|
||||
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.3 - Add replication group 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
|
||||
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
||||
@ -123,10 +124,11 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
# attach the snapshot will be failed.
|
||||
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 = Volume(id=snapshot.id,
|
||||
name=snapshot.name,
|
||||
volume_type_id=snapshot.volume_type_id,
|
||||
display_name='backup-snapshot')
|
||||
|
||||
return self.initialize_connection(volume, connector)
|
||||
@ -163,12 +165,36 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
else:
|
||||
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
||||
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
|
||||
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 = 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)
|
||||
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.2 - Add replication group 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
|
||||
CI_WIKI_NAME = "IBM_STORAGE_CI"
|
||||
@ -123,10 +124,11 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
# attach the snapshot will be failed.
|
||||
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 = Volume(id=snapshot.id,
|
||||
name=snapshot.name,
|
||||
volume_type_id=snapshot.volume_type_id,
|
||||
display_name='backup-snapshot')
|
||||
|
||||
return self.initialize_connection(volume, connector)
|
||||
@ -161,13 +163,38 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
else:
|
||||
volume_name, backend_helper, node_state = self._get_vol_sys_info(
|
||||
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
|
||||
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 = 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_enabled = self.configuration.storwize_svc_iscsi_chap_enabled
|
||||
|
@ -315,6 +315,9 @@ driver:
|
||||
- multipath
|
||||
- iogrp
|
||||
- mirror_pool
|
||||
- volume_topology
|
||||
- peer_pool
|
||||
- host_site
|
||||
|
||||
These keys have the same semantics as their counterparts in the
|
||||
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
|
||||
|
||||
- volume_topology
|
||||
|
||||
- peer_pool
|
||||
|
||||
- host_site
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
service driver will perform the synchronization and do the failback
|
||||
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