[SVf] As part of Flashcopy 2.0 adding support for volumegroup

[Spectrum Virtualize family] As part of Flashcopy 2.0 implementation,
added support for VolumeGroup. Volumegroup creation, modification and
deletion is now supported using the existing Cinder CLI for group
operations.

Implements: blueprint ibm-svf-volumegroup
Change-Id: I72041e0c3c530a732b6024c036a2a8f7083259d4
This commit is contained in:
haailani 2022-08-08 14:08:42 +00:00
parent 6dd5e20c2e
commit ac9debdf35
4 changed files with 439 additions and 8 deletions

View File

@ -81,6 +81,7 @@ class StorwizeSVCManagementSimulator(object):
self._partnership_list = {} self._partnership_list = {}
self._partnershipcandidate_list = {} self._partnershipcandidate_list = {}
self._rcconsistgrp_list = {} self._rcconsistgrp_list = {}
self._volumegroup_list = {}
self._system_list = {'storwize-svc-sim': {'id': '0123456789ABCDEF', self._system_list = {'storwize-svc-sim': {'id': '0123456789ABCDEF',
'name': 'storwize-svc-sim'}, 'name': 'storwize-svc-sim'},
'aux-svc-sim': {'id': 'ABCDEF0123456789', 'aux-svc-sim': {'id': 'ABCDEF0123456789',
@ -375,7 +376,8 @@ class StorwizeSVCManagementSimulator(object):
'thin', 'thin',
'removehostmappings', 'removehostmappings',
'removefcmaps', 'removefcmaps',
'removercrelationships' 'removercrelationships',
'novolumegroup'
] ]
one_param_args = [ one_param_args = [
'chapsecret', 'chapsecret',
@ -416,6 +418,7 @@ class StorwizeSVCManagementSimulator(object):
'pool', 'pool',
'site', 'site',
'buffersize', 'buffersize',
'volumegroup'
] ]
no_or_one_param_args = [ no_or_one_param_args = [
'autoexpand', 'autoexpand',
@ -2039,7 +2042,8 @@ port_speed!N/A
kwargs.pop('obj') kwargs.pop('obj')
params = ['name', 'warning', 'udid', params = ['name', 'warning', 'udid',
'autoexpand', 'easytier', 'primary'] 'autoexpand', 'easytier', 'primary',
'volumegroup', 'novolumegroup']
for key, value in kwargs.items(): for key, value in kwargs.items():
if key == 'easytier': if key == 'easytier':
vol['easy_tier'] = value vol['easy_tier'] = value
@ -2067,6 +2071,8 @@ port_speed!N/A
else: else:
err = self._errors['CMMVC6353E'][1] % {'VALUE': key} err = self._errors['CMMVC6353E'][1] % {'VALUE': key}
return ('', err) return ('', err)
if key == 'volumegroup':
self._volumes_list[vol_name]['volume_group_id'] = value
if key in params: if key in params:
vol[key] = value vol[key] = value
if key == 'autoexpand': if key == 'autoexpand':
@ -2531,6 +2537,121 @@ port_speed!N/A
except Exception: except Exception:
return self._errors['CMMVC5982E'] return self._errors['CMMVC5982E']
def _cmd_mkvolumegroup(self, **kwargs):
# Create a Volume group
volumegroup_info = {}
volumegroup_info['id'] = self._find_unused_id(self._volumegroup_list)
if 'name' in kwargs:
volumegroup_info['name'] = kwargs["name"].strip('\'\"')
else:
volumegroup_info['name'] = self.driver._get_volumegroup_name(
None, volumegroup_info['id'])
volumegroup_info['volume_count'] = '0'
volumegroup_info['backup_status'] = 'empty'
volumegroup_info['last_backup_time'] = ''
volumegroup_info['owner_id'] = ''
volumegroup_info['owner_name'] = ''
volumegroup_info['safeguarded_policy_id'] = ''
volumegroup_info['safeguarded_policy_name'] = ''
volumegroup_info['safeguarded_policy_start_time'] = ''
volumegroup_info['volume_group_type'] = ''
volumegroup_info['uid'] = (('ABCDEF' * 3) + ('0' * 14) +
volumegroup_info['id'])
volumegroup_info['source_volume_group_id'] = ''
volumegroup_info['source_volume_group_name'] = ''
volumegroup_info['parent_uid'] = ''
volumegroup_info['source_snapshot_id'] = ''
volumegroup_info['source_snapshot'] = ''
volumegroup_info['snapshot_count'] = '0'
volumegroup_info['protection_provisioned_capacity'] = '0.00MB'
volumegroup_info['protection_written_capacity'] = '0.00MB'
volumegroup_info['snapshot_policy_id'] = ''
volumegroup_info['snapshot_policy_name'] = ''
self._volumegroup_list[volumegroup_info['name']] = volumegroup_info
return ('Volume Group, id [' + volumegroup_info['id'] +
'], successfully created', '')
def _cmd_lsvolumegroup(self, **kwargs):
# List the volume group
if 'obj' not in kwargs:
rows = []
rows.append(['id', 'name', 'volume_count', 'backup_status',
'last_backup_time', 'owner_id', 'owner_name',
'safeguarded_policy_id', 'safeguarded_policy_name',
'safeguarded_policy_start_time', 'volume_group_type',
'uid', 'source_volume_group_id',
'source_volume_group_name', 'parent_uid',
'source_snapshot_id', 'source_snapshot',
'snapshot_count', 'protection_provisioned_capacity',
'protection_written_capacity', 'snapshot_policy_id',
'snapshot_policy_name'])
found = False
for volumegroup_name in sorted(self._volumegroup_list.keys()):
volumegroup = self._volumegroup_list[volumegroup_name]
filterstr = 'name=' + volumegroup['name']
if (('filtervalue' not in kwargs) or
(kwargs['filtervalue'] == filterstr)):
rows.append(['0', 'empty', '', '', '', '', '', '', '',
'((\'ABCDEF\' * 3) + (\'0\' * 14) +\
vg_info[\'id\'])', '', '', '', '', '', '0',
'0.00MB', '0.00MB', '', ''])
found = True
if found:
return self._print_info_cmd(rows=rows, **kwargs)
else:
return ('', '')
else:
volumegroup_info = kwargs['obj'].strip('\'\"')
if volumegroup_info not in self._volumegroup_list:
return self._errors['CMMVC5804E']
volumegroup_info = self._volumegroup_list[volumegroup_info]
rows = []
rows.append(['id', volumegroup_info['id']])
rows.append(['name', volumegroup_info['name']])
rows.append(['volume_count', '1'])
rows.append(['backup_status', 'off'])
rows.append(['last_backup_time',
volumegroup_info['last_backup_time']])
rows.append(['owner_id', volumegroup_info['owner_id']])
rows.append(['owner_name', volumegroup_info['owner_name']])
rows.append(['safeguarded_policy_id',
volumegroup_info['safeguarded_policy_id']])
rows.append(['safeguarded_policy_name',
volumegroup_info['safeguarded_policy_name']])
rows.append(['safeguarded_policy_start_time',
volumegroup_info['safeguarded_policy_start_time']])
rows.append(['volume_group_type',
volumegroup_info['volume_group_type']])
rows.append(['source_volume_group_id',
volumegroup_info['source_volume_group_id']])
rows.append(['source_volume_group_name',
volumegroup_info['source_volume_group_name']])
rows.append(['parent_uid', volumegroup_info['parent_uid']])
rows.append(['source_snapshot_id',
volumegroup_info['source_snapshot_id']])
rows.append(['source_snapshot',
volumegroup_info['source_snapshot']])
rows.append(['snapshot_count', volumegroup_info['snapshot_count']])
rows.append(['protection_provisioned_capacity', '1.00GB'])
rows.append(['protection_written_capacity', '0.75MB'])
rows.append(['snapshot_policy_id',
volumegroup_info['snapshot_policy_id']])
rows.append(['snapshot_policy_name',
volumegroup_info['snapshot_policy_name']])
if 'delim' in kwargs:
for index in range(len(rows)):
rows[index] = kwargs['delim'].join(rows[index])
return ('%s' % '\n'.join(rows), '')
def _cmd_rmvolumegroup(self, **kwargs):
# Delete a Volume Group
if 'obj' not in kwargs:
return self._errors['CMMVC5701E']
volumegroup_name = kwargs['obj'].strip('\'\"')
del self._volumegroup_list[volumegroup_name]
return ('', '')
def _cmd_mkrcconsistgrp(self, **kwargs): def _cmd_mkrcconsistgrp(self, **kwargs):
master_sys = self._system_list['storwize-svc-sim'] master_sys = self._system_list['storwize-svc-sim']
aux_sys = self._system_list['aux-svc-sim'] aux_sys = self._system_list['aux-svc-sim']
@ -6901,6 +7022,120 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
model_update = self.driver.create_group(self.ctxt, group) model_update = self.driver.create_group(self.ctxt, group)
self.assertEqual(fields.GroupStatus.ERROR, model_update['status']) self.assertEqual(fields.GroupStatus.ERROR, model_update['status'])
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_volumegroup')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_volumegroup')
def test_storwize_create_and_delete_volumegroup(self, delete_volumegroup,
create_volumegroup):
"""Test volume group creation and deletion"""
with mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_system_info') as get_system_info:
fake_system_info = {'code_level': (8, 5, 1, 0),
'system_name': 'storwize-svc-sim',
'system_id': '0123456789ABCDEF'}
get_system_info.return_value = fake_system_info
self.driver.do_setup(None)
volumegroup_spec = {'volume_group_enabled': '<is> True'}
volumegroup_type_ref = group_types.create(self.ctxt,
'volumegroup_type',
volumegroup_spec)
volumegroup_type = objects.GroupType.get_by_id(
self.ctxt, volumegroup_type_ref['id'])
vol_type_ref = volume_types.create(self.ctxt, 'non_rep_type', {})
volumegroup = testutils.create_group(
self.ctxt, group_type_id=volumegroup_type.id,
volume_type_ids=[vol_type_ref['id']])
# Create Volume Group
model_update = self.driver.create_group(self.ctxt, volumegroup)
self.assertTrue(create_volumegroup.called)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# Delete Volume Group
model_update = self.driver.delete_group(self.ctxt, volumegroup, None)
self.assertTrue(delete_volumegroup.called)
self.assertEqual(fields.GroupStatus.DELETED,
model_update[0]['status'])
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_volumegroup')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_volumegroup')
def test_storwize_update_volumegroup(self, delete_volumegroup,
create_volumegroup):
"""Test volume group updation"""
with mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_system_info') as get_system_info:
fake_system_info = {'code_level': (8, 5, 1, 0),
'system_name': 'storwize-svc-sim',
'system_id': '0123456789ABCDEF'}
get_system_info.return_value = fake_system_info
self.driver.do_setup(None)
# Create volumegroup type
volumegroup_spec = {'volume_group_enabled': '<is> True'}
volumegroup_type_ref = group_types.create(self.ctxt,
'volumegroup_type',
volumegroup_spec)
volumegroup_type = objects.GroupType.get_by_id(
self.ctxt, volumegroup_type_ref['id'])
# Create volume
vol_type_ref = volume_types.create(self.ctxt, 'non_rep_type', {})
vol_type = objects.VolumeType.get_by_id(self.ctxt,
vol_type_ref['id'])
volume = self._generate_vol_info(vol_type)
self.driver.create_volume(volume)
# Create volumegroup
volumegroup = testutils.create_group(
self.ctxt, group_type_id=volumegroup_type.id,
volume_type_ids=[vol_type_ref['id']])
model_update = self.driver.create_group(self.ctxt, volumegroup)
self.assertTrue(create_volumegroup.called)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
add_vols = [volume]
remove_vols = [volume]
with mock.patch.object(
storwize_svc_common.StorwizeSVCCommonDriver,
'_update_volumegroup') as _update_volumegroup:
model_update = {'status': 'available'}
fake_update_volumegroup_info = [model_update, add_vols, None]
_update_volumegroup.return_value = fake_update_volumegroup_info
(model_update, add_volumes_update,
remove_volumes_update) = self.driver.update_group(
self.ctxt, volumegroup, add_vols, [])
self.assertTrue(_update_volumegroup.called)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
model_update = {'status': 'available'}
fake_update_volumegroup_info = [model_update, None,
remove_vols]
_update_volumegroup.return_value = (
fake_update_volumegroup_info)
(model_update, add_volumes_update,
remove_volumes_update) = self.driver.update_group(
self.ctxt, volumegroup, [], remove_vols)
self.assertTrue(_update_volumegroup.called)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# Delete Volume Group
model_update = self.driver.delete_group(self.ctxt, volumegroup,
None)
self.assertTrue(delete_volumegroup.called)
self.assertEqual(fields.GroupStatus.DELETED,
model_update[0]['status'])
@mock.patch.object(storwize_svc_common.StorwizeHelpers, @mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_rccg') 'create_rccg')
def test_storwize_group_create(self, create_rccg): def test_storwize_group_create(self, create_rccg):
@ -6940,7 +7175,8 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
def test_storwize_delete_group(self, _del_rep_grp, is_grp_a_cg_rep_type, def test_storwize_delete_group(self, _del_rep_grp, is_grp_a_cg_rep_type,
is_grp_a_cg_snapshot_type): is_grp_a_cg_snapshot_type):
is_grp_a_cg_snapshot_type.side_effect = [True, True, False, True] is_grp_a_cg_snapshot_type.side_effect = [True, True, False, True]
is_grp_a_cg_rep_type.side_effect = [False, False, False, False] is_grp_a_cg_rep_type.side_effect = [False, False, False, False,
False, False]
type_ref = volume_types.create(self.ctxt, 'testtype', None) type_ref = volume_types.create(self.ctxt, 'testtype', None)
group = testutils.create_group(self.ctxt, group = testutils.create_group(self.ctxt,
group_type_id=fake.GROUP_TYPE_ID, group_type_id=fake.GROUP_TYPE_ID,
@ -6969,7 +7205,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
is_grp_a_cg_snapshot_type): is_grp_a_cg_snapshot_type):
"""Test group update.""" """Test group update."""
is_grp_a_cg_snapshot_type.side_effect = [False, True, True, False] is_grp_a_cg_snapshot_type.side_effect = [False, True, True, False]
is_grp_a_cg_rep_type.side_effect = [False, False, False, is_grp_a_cg_rep_type.side_effect = [False, False, False, False,
False, True, True] False, True, True]
group = mock.MagicMock() group = mock.MagicMock()
self.assertRaises(NotImplementedError, self.driver.update_group, self.assertRaises(NotImplementedError, self.driver.update_group,

View File

@ -49,6 +49,8 @@ REPLICA_CHG_VOL_PREFIX = 'chg_'
RCCG_PREFIX = 'rccg-' RCCG_PREFIX = 'rccg-'
HYPERCG_PREFIX = 'hycg-' HYPERCG_PREFIX = 'hycg-'
VG_PREFIX = 'vg-'
# remote mirror copy status # remote mirror copy status
REP_CONSIS_SYNC = 'consistent_synchronized' REP_CONSIS_SYNC = 'consistent_synchronized'
REP_CONSIS_COPYING = 'consistent_copying' REP_CONSIS_COPYING = 'consistent_copying'

View File

@ -540,6 +540,52 @@ class StorwizeSSH(object):
ssh_cmd = ['svctask', 'rmhost', '"%s"' % host] ssh_cmd = ['svctask', 'rmhost', '"%s"' % host]
self.run_ssh_assert_no_output(ssh_cmd) self.run_ssh_assert_no_output(ssh_cmd)
def mkvolumegroup(self, volumegroup_name):
"""Create a volume group(VG)."""
ssh_cmd = ['svctask', 'mkvolumegroup', '-name', '"%s"'
% volumegroup_name]
try:
return self.run_ssh_check_created(ssh_cmd)
except Exception as ex:
if hasattr(ex, 'msg') and 'CMMVC6035E' in ex.msg:
msg = (_('CMMVC6372W Action failed because volume group '
'with the name provided already exists.'))
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
with excutils.save_and_reraise_exception():
LOG.exception('Failed to create volumegroup.')
def lsvolumegroup(self, volumegroup_id_or_name):
"""Return volume group attributes or None if it doesn't exist."""
ssh_cmd = ['svcinfo', 'lsvolumegroup', '-bytes', '-delim', '!',
'"%s"' % volumegroup_id_or_name]
out, err = self._ssh(ssh_cmd, check_exit_code=False)
if not err:
return CLIResponse((out, err), ssh_cmd=ssh_cmd, delim='!',
with_header=False)[0]
if 'CMMVC5804E' in err:
return None
msg = (_('CLI Exception output:\n command: %(cmd)s\n '
'stdout: %(out)s\n stderr: %(err)s.') %
{'cmd': ssh_cmd,
'out': out,
'err': err})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
def rmvolumegroup(self, volumegroup_name_or_id):
"""Delete a volume group"""
ssh_cmd = ['svctask', 'rmvolumegroup', '"%s"' % volumegroup_name_or_id]
try:
self.run_ssh_assert_no_output(ssh_cmd)
except Exception as ex:
if hasattr(ex, 'msg') and 'CMMVC8749E' in ex.msg:
msg = _('rmvolumegroup: specified volume group is not empty.')
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
with excutils.save_and_reraise_exception():
LOG.exception('Failed to delete volumegroup.')
def mkvdisk(self, name, size, units, pool, opts, params): def mkvdisk(self, name, size, units, pool, opts, params):
ssh_cmd = ['svctask', 'mkvdisk', '-name', '"%s"' % name, '-mdiskgrp', ssh_cmd = ['svctask', 'mkvdisk', '-name', '"%s"' % name, '-mdiskgrp',
'"%s"' % pool, '-iogrp', six.text_type(opts['iogrp']), '"%s"' % pool, '-iogrp', six.text_type(opts['iogrp']),
@ -2769,6 +2815,34 @@ class StorwizeHelpers(object):
else: else:
return None return None
def create_volumegroup(self, volumegroup_name):
self.ssh.mkvolumegroup(volumegroup_name)
def get_volumegroup(self, volumegroup_id_or_name):
vg = self.ssh.lsvolumegroup(volumegroup_id_or_name)
return vg if len(vg) > 0 else None
def delete_volumegroup(self, volumegroup_id_or_name):
if self.ssh.lsvolumegroup(volumegroup_id_or_name):
self.ssh.rmvolumegroup(volumegroup_id_or_name)
def add_vdisk_to_volumegroup(self, vol_name, volumegroup_id):
self.ssh.chvdisk(vol_name, ['-volumegroup', volumegroup_id])
def remove_vdisk_from_volumegroup(self, vol_name):
self.ssh.chvdisk(vol_name, ['-novolumegroup'])
def check_codelevel_for_volumegroup(self, code_level):
if not (code_level >= (8, 5, 1, 0)):
msg = (_('The configured group type spec is '
'"volume_group_enabled". '
'The supported code level for this group type spec '
'is 8.5.1.0 '
'The current storage code level is %(code_level)s.')
% {'code_level': code_level})
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
def get_partnership_info(self, system_name): def get_partnership_info(self, system_name):
partnership = self.ssh.lspartnership(system_name) partnership = self.ssh.lspartnership(system_name)
return partnership[0] if len(partnership) > 0 else None return partnership[0] if len(partnership) > 0 else None
@ -3711,6 +3785,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
volume.metadata['Consistency Group Name'] = rccg_name volume.metadata['Consistency Group Name'] = rccg_name
volume.save() volume.save()
def _update_volumegroup_properties(self, ctxt, volume, group=None):
volumegroup_name = self._get_volumegroup_name(group) if group else ""
if not volume.metadata:
volume.metadata = dict()
volume.metadata['Volume Group Name'] = volumegroup_name
volume.save()
def create_volume(self, volume): def create_volume(self, volume):
LOG.debug('enter: create_volume: volume %s', volume['name']) LOG.debug('enter: create_volume: volume %s', volume['name'])
# Create a replication or hyperswap volume with group_id is not # Create a replication or hyperswap volume with group_id is not
@ -5990,6 +6071,12 @@ class StorwizeSVCCommonDriver(san.SanDriver,
if hyper_grp else storwize_const.RCCG_PREFIX) if hyper_grp else storwize_const.RCCG_PREFIX)
return rccg + group_id[0:4] + '-' + group_id[-5:] return rccg + group_id[0:4] + '-' + group_id[-5:]
@staticmethod
def _get_volumegroup_name(group, grp_id=None):
group_id = group.id if group else grp_id
vg = storwize_const.VG_PREFIX
return vg + 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):
"""Creates a group. """Creates a group.
@ -6011,7 +6098,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'] 'hyperswap_group_enabled',
'volume_group_enabled']
supported_grp = False supported_grp = False
for grp_spec in support_grps: for grp_spec in support_grps:
if volume_utils.is_group_a_type(group, grp_spec): if volume_utils.is_group_a_type(group, grp_spec):
@ -6102,6 +6190,19 @@ class StorwizeSVCCommonDriver(san.SanDriver,
model_update = {'status': fields.GroupStatus.ERROR} model_update = {'status': fields.GroupStatus.ERROR}
return model_update return model_update
if volume_utils.is_group_a_type(group, "volume_group_enabled"):
try:
self._helpers.check_codelevel_for_volumegroup(
self._state['code_level'])
volumegroup_name = self._get_volumegroup_name(group)
self._helpers.create_volumegroup(volumegroup_name)
except exception.VolumeBackendAPIException as err:
LOG.error("Failed to create volume group %(volumegroup)s. "
"Exception: %(exception)s.",
{'volumegroup': volumegroup_name, 'exception': err})
model_update = {'status': fields.GroupStatus.ERROR}
return model_update
return model_update return model_update
def delete_group(self, context, group, volumes): def delete_group(self, context, group, volumes):
@ -6123,7 +6224,10 @@ class StorwizeSVCCommonDriver(san.SanDriver,
"consistent_group_replication_enabled") "consistent_group_replication_enabled")
and not volume_utils.is_group_a_type( and not volume_utils.is_group_a_type(
group, group,
"hyperswap_group_enabled")): "hyperswap_group_enabled")
and not volume_utils.is_group_a_type(
group,
"volume_group_enabled")):
raise NotImplementedError() raise NotImplementedError()
model_update = {'status': fields.GroupStatus.DELETED} model_update = {'status': fields.GroupStatus.DELETED}
@ -6133,10 +6237,15 @@ class StorwizeSVCCommonDriver(san.SanDriver,
model_update, volumes_model_update = self._delete_replication_grp( model_update, volumes_model_update = self._delete_replication_grp(
group, volumes) group, volumes)
if volume_utils.is_group_a_type(group, "hyperswap_group_enabled"): elif volume_utils.is_group_a_type(group, "hyperswap_group_enabled"):
model_update, volumes_model_update = self._delete_hyperswap_grp( model_update, volumes_model_update = self._delete_hyperswap_grp(
group, volumes) group, volumes)
elif volume_utils.is_group_a_type(group, "volume_group_enabled"):
self._helpers.check_codelevel_for_volumegroup(
self._state['code_level'])
model_update = self._delete_volumegroup(group)
else: else:
for volume in volumes: for volume in volumes:
try: try:
@ -6180,7 +6289,10 @@ class StorwizeSVCCommonDriver(san.SanDriver,
"consistent_group_replication_enabled") "consistent_group_replication_enabled")
and not volume_utils.is_group_a_type( and not volume_utils.is_group_a_type(
group, group,
"hyperswap_group_enabled")): "hyperswap_group_enabled")
and not volume_utils.is_group_a_type(
group,
"volume_group_enabled")):
raise NotImplementedError() raise NotImplementedError()
if volume_utils.is_group_a_type( if volume_utils.is_group_a_type(
@ -6195,6 +6307,12 @@ class StorwizeSVCCommonDriver(san.SanDriver,
if volume_utils.is_group_a_cg_snapshot_type(group): if volume_utils.is_group_a_cg_snapshot_type(group):
return None, None, None return None, None, None
if volume_utils.is_group_a_type(group, "volume_group_enabled"):
self._helpers.check_codelevel_for_volumegroup(
self._state['code_level'])
return self._update_volumegroup(context, group, add_volumes,
remove_volumes)
def create_group_from_src(self, context, group, volumes, def create_group_from_src(self, context, group, volumes,
group_snapshot=None, snapshots=None, group_snapshot=None, snapshots=None,
source_group=None, source_vols=None): source_group=None, source_vols=None):
@ -6778,6 +6896,75 @@ class StorwizeSVCCommonDriver(san.SanDriver,
{'vol': volume.name, 'exception': err}) {'vol': volume.name, 'exception': err})
return model_update, added_vols, removed_vols return model_update, added_vols, removed_vols
def _delete_volumegroup(self, group):
model_update = {'status': fields.GroupStatus.DELETED}
volumegroup_name = self._get_volumegroup_name(group)
try:
self._helpers.delete_volumegroup(volumegroup_name)
except exception.VolumeBackendAPIException as err:
LOG.error("Failed to delete volume group %(volumegroup)s. "
"Exception: %(exception)s.",
{'volumegroup': volumegroup_name, 'exception': err})
model_update = {'status': fields.GroupStatus.ERROR_DELETING}
return model_update
def _update_volumegroup(self, context, group, add_volumes,
remove_volumes):
model_update = {'status': fields.GroupStatus.AVAILABLE}
LOG.info("Update volume group: %(volumegroup_id)s. ",
{'volumegroup_id': group.id})
volumegroup_name = self._get_volumegroup_name(group)
# This code block fails during remove of volumes from group
try:
volumegroup = self._helpers.get_volumegroup(volumegroup_name)
volumegroup_id = volumegroup["id"]
except Exception as ex:
if len(add_volumes) > 0:
LOG.exception("Unable to retrieve volume group "
"information. Failed with exception "
"%(ex)s", ex)
if not volumegroup and len(add_volumes) > 0:
LOG.error("Failed to update group: %(volumegroup)s does not "
"exist in backend.",
{'volumegroup': volumegroup_name})
model_update['status'] = fields.GroupStatus.ERROR
return model_update, None, None
# Add volume(s) to the volume group
added_vols = []
for volume in add_volumes:
vol_name = volume.name
try:
self._helpers.add_vdisk_to_volumegroup(vol_name,
volumegroup_id)
added_vols.append({'id': volume.id,
'group_id': group.id})
self._update_volumegroup_properties(context, volume, group)
except exception.VolumeBackendAPIException as err:
model_update['status'] = fields.GroupStatus.ERROR
LOG.error("Failed to add the volume %(vol)s to "
"group. Exception: %(exception)s.",
{'vol': volume.name, 'exception': err})
# Remove volume(s) from the volume group
removed_vols = []
for volume in remove_volumes:
vol_name = volume.name
try:
self._helpers.remove_vdisk_from_volumegroup(vol_name)
removed_vols.append({'id': volume.id,
'group_id': None})
self._update_volumegroup_properties(context, volume)
except exception.VolumeBackendAPIException as err:
model_update['status'] = fields.GroupStatus.ERROR
LOG.error("Failed to remove the volume %(vol)s from "
"group. Exception: %(exception)s.",
{'vol': volume.name, 'exception': err})
return model_update, added_vols, removed_vols
def _delete_hyperswap_grp(self, group, volumes): def _delete_hyperswap_grp(self, group, volumes):
model_update = {'status': fields.GroupStatus.DELETED} model_update = {'status': fields.GroupStatus.DELETED}
volumes_model_update = [] volumes_model_update = []

View File

@ -0,0 +1,6 @@
---
features:
- |
IBM Spectrum Virtualize Family driver: Added support for volumegroup
for SVC Code Level 8.5.1.0 and above. User can now create, modify
and delete volumegroup using the exising cinder CLI for group operations.