Merge "Autoselection of IBM SVC/Storwize IO group"
This commit is contained in:
commit
898c4a768c
|
@ -3754,7 +3754,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
|||
'grainsize': 256,
|
||||
'compression': False,
|
||||
'easytier': True,
|
||||
'iogrp': 0,
|
||||
'iogrp': '0',
|
||||
'qos': None,
|
||||
'replication': False,
|
||||
'stretched_cluster': None,
|
||||
|
@ -4114,7 +4114,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
|||
|
||||
opts_list = []
|
||||
chck_list = []
|
||||
opts_list.append({'rsize': -1, 'easytier': True, 'iogrp': 0})
|
||||
opts_list.append({'rsize': -1, 'easytier': True, 'iogrp': '0'})
|
||||
chck_list.append({'free_capacity': '0', 'easy_tier': 'on',
|
||||
'IO_group_id': '0'})
|
||||
|
||||
|
@ -4124,14 +4124,14 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
|||
opts_list.append({'rsize': -1, 'nofmtdisk': True})
|
||||
chck_list.append({'formatted': 'no'})
|
||||
|
||||
test_iogrp = 1 if self.USESIM else 0
|
||||
test_iogrp = '1' if self.USESIM else '0'
|
||||
opts_list.append({'rsize': 2, 'compression': False, 'warning': 0,
|
||||
'autoexpand': True, 'grainsize': 32,
|
||||
'easytier': False, 'iogrp': test_iogrp})
|
||||
chck_list.append({'-free_capacity': '0', 'compressed_copy': 'no',
|
||||
'warning': '0', 'autoexpand': 'on',
|
||||
'grainsize': '32', 'easy_tier': 'off',
|
||||
'IO_group_id': six.text_type(test_iogrp)})
|
||||
'IO_group_id': (test_iogrp)})
|
||||
opts_list.append({'rsize': 2, 'compression': False, 'warning': 80,
|
||||
'autoexpand': False, 'grainsize': 256,
|
||||
'easytier': True})
|
||||
|
@ -5673,6 +5673,61 @@ class StorwizeHelpersTestCase(test.TestCase):
|
|||
for i in range(7):
|
||||
self.assertTrue(self.storwize_svc_common.replication_licensed())
|
||||
|
||||
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
|
||||
'get_vdisk_count_by_io_group')
|
||||
def test_select_io_group(self, get_vdisk_count_by_io_group):
|
||||
# given io groups
|
||||
opts = {}
|
||||
# system io groups
|
||||
state = {}
|
||||
|
||||
fake_iog_vdc1 = {0: 100, 1: 50, 2: 50, 3: 300}
|
||||
fake_iog_vdc2 = {0: 2, 1: 1, 2: 200}
|
||||
fake_iog_vdc3 = {0: 2, 2: 200}
|
||||
fake_iog_vdc4 = {0: 100, 1: 100, 2: 100, 3: 100}
|
||||
fake_iog_vdc5 = {0: 10, 1: 1, 2: 200, 3: 300}
|
||||
|
||||
get_vdisk_count_by_io_group.side_effect = [fake_iog_vdc1,
|
||||
fake_iog_vdc2,
|
||||
fake_iog_vdc3,
|
||||
fake_iog_vdc4,
|
||||
fake_iog_vdc5]
|
||||
opts['iogrp'] = '0,2'
|
||||
state['available_iogrps'] = [0, 1, 2, 3]
|
||||
|
||||
iog = self.storwize_svc_common.select_io_group(state, opts)
|
||||
self.assertTrue(iog in state['available_iogrps'])
|
||||
self.assertEqual(2, iog)
|
||||
|
||||
opts['iogrp'] = '0'
|
||||
state['available_iogrps'] = [0, 1, 2]
|
||||
|
||||
iog = self.storwize_svc_common.select_io_group(state, opts)
|
||||
self.assertTrue(iog in state['available_iogrps'])
|
||||
self.assertEqual(0, iog)
|
||||
|
||||
opts['iogrp'] = '1,2'
|
||||
state['available_iogrps'] = [0, 2]
|
||||
|
||||
iog = self.storwize_svc_common.select_io_group(state, opts)
|
||||
self.assertTrue(iog in state['available_iogrps'])
|
||||
self.assertEqual(2, iog)
|
||||
|
||||
opts['iogrp'] = ' 0, 1, 2 '
|
||||
state['available_iogrps'] = [0, 1, 2, 3]
|
||||
|
||||
iog = self.storwize_svc_common.select_io_group(state, opts)
|
||||
self.assertTrue(iog in state['available_iogrps'])
|
||||
# since vdisk count in all iogroups is same, it will pick the first
|
||||
self.assertEqual(0, iog)
|
||||
|
||||
opts['iogrp'] = '0,1,2, 3'
|
||||
state['available_iogrps'] = [0, 1, 2, 3]
|
||||
|
||||
iog = self.storwize_svc_common.select_io_group(state, opts)
|
||||
self.assertTrue(iog in state['available_iogrps'])
|
||||
self.assertEqual(1, iog)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class StorwizeSSHTestCase(test.TestCase):
|
||||
|
|
|
@ -237,6 +237,9 @@ class StorwizeSVCReplicationGlobalMirror(
|
|||
if not attr:
|
||||
opts = self.driver._get_vdisk_params(vref['volume_type_id'])
|
||||
pool = self.target.get('pool_name')
|
||||
src_attr = self.driver._helpers.get_vdisk_attributes(
|
||||
vref['name'])
|
||||
opts['iogrp'] = src_attr['IO_group_id']
|
||||
self.target_helpers.create_vdisk(target_vol_name,
|
||||
six.text_type(vref['size']),
|
||||
'gb', pool, opts)
|
||||
|
|
|
@ -82,9 +82,12 @@ storwize_svc_opts = [
|
|||
cfg.BoolOpt('storwize_svc_vol_easytier',
|
||||
default=True,
|
||||
help='Enable Easy Tier for volumes'),
|
||||
cfg.IntOpt('storwize_svc_vol_iogrp',
|
||||
default=0,
|
||||
help='The I/O group in which to allocate volumes'),
|
||||
cfg.StrOpt('storwize_svc_vol_iogrp',
|
||||
default='0',
|
||||
help='The I/O group in which to allocate volumes. It can be a '
|
||||
'comma-separated list in which case the driver will select an '
|
||||
'io_group based on least number of volumes associated with the '
|
||||
'io_group.'),
|
||||
cfg.IntOpt('storwize_svc_flashcopy_timeout',
|
||||
default=120,
|
||||
min=1, max=600,
|
||||
|
@ -665,10 +668,45 @@ class StorwizeHelpers(object):
|
|||
msg = (_('Expected integer for node_count, '
|
||||
'svcinfo lsiogrp returned: %(node)s.') %
|
||||
{'node': iogrp['node_count']})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return iogrps
|
||||
|
||||
def get_vdisk_count_by_io_group(self):
|
||||
res = {}
|
||||
resp = self.ssh.lsiogrp()
|
||||
for iogrp in resp:
|
||||
try:
|
||||
if int(iogrp['node_count']) > 0:
|
||||
res[int(iogrp['id'])] = int(iogrp['vdisk_count'])
|
||||
except KeyError:
|
||||
self.handle_keyerror('lsiogrp', iogrp)
|
||||
except ValueError:
|
||||
msg = (_('Expected integer for node_count, '
|
||||
'svcinfo lsiogrp returned: %(node)s') %
|
||||
{'node': iogrp['node_count']})
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return res
|
||||
|
||||
def select_io_group(self, state, opts):
|
||||
selected_iog = 0
|
||||
iog_list = StorwizeHelpers._get_valid_requested_io_groups(state, opts)
|
||||
if len(iog_list) == 0:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Given I/O group(s) %(iogrp)s not valid; available '
|
||||
'I/O groups are %(avail)s.')
|
||||
% {'iogrp': opts['iogrp'],
|
||||
'avail': state['available_iogrps']})
|
||||
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]]
|
||||
selected_iog = iog_list[0]
|
||||
for iog in iog_list:
|
||||
if iog_vdc[iog] < min_vdisk_count:
|
||||
min_vdisk_count = iog_vdc[iog]
|
||||
selected_iog = iog
|
||||
LOG.debug("Selected io_group is %d", selected_iog)
|
||||
return selected_iog
|
||||
|
||||
def get_volume_io_group(self, vol_name):
|
||||
vdisk = self.ssh.lsvdisk(vol_name)
|
||||
if vdisk:
|
||||
|
@ -1016,19 +1054,33 @@ class StorwizeHelpers(object):
|
|||
reason=_('If compression is set to True, rsize must '
|
||||
'also be set (not equal to -1).'))
|
||||
|
||||
if opts['iogrp'] not in state['available_iogrps']:
|
||||
avail_grps = ''.join(str(e) for e in state['available_iogrps'])
|
||||
iogs = StorwizeHelpers._get_valid_requested_io_groups(state, opts)
|
||||
|
||||
if len(iogs) == 0:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('I/O group %(iogrp)d is not valid; available '
|
||||
reason=_('Given I/O group(s) %(iogrp)s not valid; available '
|
||||
'I/O groups are %(avail)s.')
|
||||
% {'iogrp': opts['iogrp'],
|
||||
'avail': avail_grps})
|
||||
'avail': state['available_iogrps']})
|
||||
|
||||
if opts['nofmtdisk'] and opts['rsize'] != -1:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('If nofmtdisk is set to True, rsize must '
|
||||
'also be set to -1.'))
|
||||
|
||||
@staticmethod
|
||||
def _get_valid_requested_io_groups(state, opts):
|
||||
given_iogs = str(opts['iogrp'])
|
||||
iog_list = given_iogs.split(',')
|
||||
# convert to int
|
||||
iog_list = list(map(int, iog_list))
|
||||
LOG.debug("Requested iogroups %s", iog_list)
|
||||
LOG.debug("Available iogroups %s", state['available_iogrps'])
|
||||
filtiog = set(iog_list).intersection(state['available_iogrps'])
|
||||
iog_list = list(filtiog)
|
||||
LOG.debug("Filtered (valid) requested iogroups %s", iog_list)
|
||||
return iog_list
|
||||
|
||||
def _get_opts_from_specs(self, opts, specs):
|
||||
qos = {}
|
||||
for k, value in specs.items():
|
||||
|
@ -1298,6 +1350,7 @@ class StorwizeHelpers(object):
|
|||
for snapshot in snapshots:
|
||||
opts = self.get_vdisk_params(config, state,
|
||||
snapshot['volume_type_id'])
|
||||
|
||||
self.create_flashcopy_to_consistgrp(snapshot['volume_name'],
|
||||
snapshot['name'],
|
||||
fc_consistgrp,
|
||||
|
@ -1459,6 +1512,7 @@ class StorwizeHelpers(object):
|
|||
# In case we need to use a specific pool
|
||||
if not pool:
|
||||
pool = src_attrs['mdisk_grp_name']
|
||||
opts['iogrp'] = src_attrs['IO_group_id']
|
||||
self.create_vdisk(target, src_size, 'b', pool, opts)
|
||||
|
||||
self.ssh.mkfcmap(source, target, full_copy,
|
||||
|
@ -1673,6 +1727,8 @@ class StorwizeHelpers(object):
|
|||
# In case we need to use a specific pool
|
||||
if not pool:
|
||||
pool = src_attrs['mdisk_grp_name']
|
||||
|
||||
opts['iogrp'] = src_attrs['IO_group_id']
|
||||
self.create_vdisk(tgt, src_size, 'b', pool, opts)
|
||||
timeout = config.storwize_svc_flashcopy_timeout
|
||||
try:
|
||||
|
@ -2285,6 +2341,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
volume_metadata=
|
||||
volume.get('volume_metadata'))
|
||||
pool = utils.extract_host(volume['host'], 'pool')
|
||||
|
||||
opts['iogrp'] = self._helpers.select_io_group(self._state, opts)
|
||||
self._helpers.create_vdisk(volume['name'], str(volume['size']),
|
||||
'gb', pool, opts)
|
||||
if opts['qos']:
|
||||
|
@ -3167,15 +3225,17 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
model_update = None
|
||||
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'])
|
||||
|
||||
# There are three options for rep_type: None, metro, global
|
||||
if new_rep_type != old_rep_type:
|
||||
if new_opts['iogrp'] != old_opts['iogrp']:
|
||||
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 '
|
||||
'replication type and io group at the same time.'))
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeDriverException(message=msg)
|
||||
|
||||
if new_rep_type and old_rep_type:
|
||||
msg = (_('Unable to retype: it is not allowed to change '
|
||||
'%(old_rep_type)s volume to %(new_rep_type)s '
|
||||
|
@ -3191,6 +3251,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
' Volume = %s') % volume['id'])
|
||||
raise exception.VolumeDriverException(message=msg)
|
||||
|
||||
new_io_grp = self._helpers.select_io_group(self._state, new_opts)
|
||||
|
||||
if need_copy:
|
||||
self._check_volume_copy_ops()
|
||||
dest_pool = self._helpers.can_migrate_to_host(host, self._state)
|
||||
|
@ -3198,8 +3260,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
return False
|
||||
|
||||
retype_iogrp_property(volume,
|
||||
new_opts['iogrp'],
|
||||
old_opts['iogrp'])
|
||||
new_io_grp, old_io_grp)
|
||||
try:
|
||||
new_op = self.add_vdisk_copy(volume['name'],
|
||||
dest_pool,
|
||||
|
@ -3207,14 +3268,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
self._add_vdisk_copy_op(ctxt, volume, new_op)
|
||||
except exception.VolumeDriverException:
|
||||
# roll back changing iogrp property
|
||||
retype_iogrp_property(volume, old_opts['iogrp'],
|
||||
new_opts['iogrp'])
|
||||
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_opts['iogrp'], old_opts['iogrp'])
|
||||
retype_iogrp_property(volume, new_io_grp, old_io_grp)
|
||||
|
||||
self._helpers.change_vdisk_options(volume['name'], vdisk_changes,
|
||||
new_opts, self._state)
|
||||
|
@ -3350,7 +3410,9 @@ class StorwizeSVCCommonDriver(san.SanDriver,
|
|||
"the volume type chosen is not compress."))
|
||||
raise exception.ManageExistingVolumeTypeMismatch(reason=msg)
|
||||
|
||||
if vdisk_io_grp != opts['iogrp']:
|
||||
if (vdisk_io_grp not in
|
||||
StorwizeHelpers._get_valid_requested_io_groups(
|
||||
self._state, opts)):
|
||||
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"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
In IBM Storwize_SVC driver, user could specify only one IO
|
||||
group per backend definition. The user now may specify a comma separated
|
||||
list of IO groups, and at the time of creating the volume, the driver will
|
||||
select an IO group which has the least number of volumes associated with
|
||||
it. The change is backward compatible, meaning single value is still
|
||||
supported.
|
Loading…
Reference in New Issue