PowerMax Driver - RDF status validation

Adding validation calls for the state of RDF
enabled storage groups before create/delete/retype
of volumes.

Change-Id: I2fa916aaa432b5d5a28d5315444227bf2a2da631
This commit is contained in:
odonos12 2020-04-17 14:37:03 +01:00
parent 99c09bddce
commit 5eaa924349
5 changed files with 472 additions and 4 deletions

View File

@ -706,6 +706,7 @@ class PowerMaxReplicationTest(test.TestCase):
self.data.test_clone_volume, self.data.test_volume)
self.assertEqual(ref_model_update, model_update)
@mock.patch.object(common.PowerMaxCommon, '_validate_rdfg_status')
@mock.patch.object(
common.PowerMaxCommon, '_add_volume_to_rdf_management_group')
@mock.patch.object(
@ -727,12 +728,13 @@ class PowerMaxReplicationTest(test.TestCase):
return_value=(True, True))
def test_create_volume_rep_enabled(
self, mck_slo, mck_prep, mck_get, mck_create, mck_protect, mck_set,
mck_add):
mck_add, mck_valid):
volume = self.data.test_volume
volume_name = self.data.volume_id
volume_size = 1
extra_specs = deepcopy(self.data.rep_extra_specs)
extra_specs['mode'] = utils.REP_ASYNC
extra_specs[utils.REP_CONFIG] = self.data.rep_config_async
volume_dict, rep_update, rep_info_dict = self.common._create_volume(
volume, volume_name, volume_size, extra_specs)
self.assertEqual(self.data.provider_location, volume_dict)
@ -789,6 +791,7 @@ class PowerMaxReplicationTest(test.TestCase):
mock_rm.assert_called_once_with(
array, volume, device_id, volume_name, ref_extra_specs, False)
@mock.patch.object(common.PowerMaxCommon, '_validate_rdfg_status')
@mock.patch.object(
common.PowerMaxCommon, 'get_volume_metadata', return_value='')
@mock.patch.object(rest.PowerMaxRest, 'srdf_resume_replication')
@ -800,7 +803,7 @@ class PowerMaxReplicationTest(test.TestCase):
return_value=({'mgmt_sg_name': tpd.PowerMaxData.rdf_managed_async_grp,
'rdf_group_no': tpd.PowerMaxData.rdf_group_no_1}, True))
def test_migrate_volume_success_rep_to_no_rep(
self, mck_break, mck_retype, mck_resume, mck_get):
self, mck_break, mck_retype, mck_resume, mck_get, mck_valid):
array_id = self.data.array
volume = self.data.test_volume
device_id = self.data.device_id
@ -828,6 +831,7 @@ class PowerMaxReplicationTest(test.TestCase):
target_slo, target_workload, target_extra_specs)
self.assertTrue(success)
@mock.patch.object(common.PowerMaxCommon, '_validate_rdfg_status')
@mock.patch.object(common.PowerMaxCommon, '_sync_check')
@mock.patch.object(
common.PowerMaxCommon, 'get_volume_metadata', return_value='')
@ -845,7 +849,8 @@ class PowerMaxReplicationTest(test.TestCase):
'remote_array': tpd.PowerMaxData.remote_array},
tpd.PowerMaxData.rep_extra_specs, False))
def test_migrate_volume_success_no_rep_to_rep(
self, mck_configure, mck_retype, mck_protect, mck_get, mck_check):
self, mck_configure, mck_retype, mck_protect, mck_get, mck_check,
mck_valid):
self.common.rep_config = {'mode': utils.REP_SYNC,
'array': self.data.array}
array_id = self.data.array
@ -885,6 +890,7 @@ class PowerMaxReplicationTest(test.TestCase):
target_extra_specs)
self.assertTrue(success)
@mock.patch.object(common.PowerMaxCommon, '_validate_rdfg_status')
@mock.patch.object(
common.PowerMaxCommon, 'get_volume_metadata', return_value='')
@mock.patch.object(
@ -893,7 +899,7 @@ class PowerMaxReplicationTest(test.TestCase):
common.PowerMaxCommon, '_retype_volume',
return_value=(True, tpd.PowerMaxData.defaultstoragegroup_name))
def test_migrate_volume_success_rep_to_rep(self, mck_retype, mck_remote,
mck_get):
mck_get, mck_valid):
self.common.rep_config = {'mode': utils.REP_SYNC,
'array': self.data.array}
array_id = self.data.array
@ -1304,3 +1310,244 @@ class PowerMaxReplicationTest(test.TestCase):
self.assertEqual(extra_specs[utils.REP_CONFIG], rep_extra_specs)
self.assertTrue(resume_rdf)
@mock.patch.object(
provision.PowerMaxProvision, 'verify_slo_workload',
return_value=(True, True))
@mock.patch.object(utils.PowerMaxUtils, 'get_rdf_management_group_name',
return_value=tpd.PowerMaxData.rdf_managed_async_grp)
@mock.patch.object(common.PowerMaxCommon,
'_validate_management_group_volume_consistency',
return_value=True)
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_rdf_states',
side_effect=[True, True])
@mock.patch.object(common.PowerMaxCommon,
'_validate_rdf_group_storage_group_exclusivity',
side_effect=[True, True])
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_is_replication_enabled',
side_effect=[True, True])
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
return_value=tpd.PowerMaxData.sg_details[0])
def test_validate_rdfg_status_success(
self, mck_get, mck_is_rep, mck_is_excl, mck_states, mck_cons,
mck_mgrp_name, mck_slo):
array = self.data.array
extra_specs = deepcopy(self.data.rep_extra_specs6)
extra_specs[utils.REP_MODE] = utils.REP_ASYNC
extra_specs[utils.REP_CONFIG] = self.data.rep_config_async
management_sg_name = self.data.rdf_managed_async_grp
rdfg = self.data.rdf_group_no_2
mode = utils.REP_ASYNC
self.common._validate_rdfg_status(array, extra_specs)
self.assertEqual(2, mck_get.call_count)
self.assertEqual(2, mck_is_rep.call_count)
self.assertEqual(2, mck_is_excl.call_count)
self.assertEqual(2, mck_states.call_count)
self.assertEqual(1, mck_cons.call_count)
self.assertEqual(1, mck_mgrp_name.call_count)
mck_is_rep.assert_called_with(array, management_sg_name)
mck_is_excl.assert_called_with(array, management_sg_name)
mck_states.assert_called_with(array, management_sg_name, rdfg, mode)
mck_cons.assert_called_with(array, management_sg_name, rdfg)
@mock.patch.object(
provision.PowerMaxProvision, 'verify_slo_workload',
return_value=(True, True))
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_rdf_states',
return_value=False)
@mock.patch.object(common.PowerMaxCommon,
'_validate_rdf_group_storage_group_exclusivity',
return_value=True)
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_is_replication_enabled',
return_value=True)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
return_value=tpd.PowerMaxData.sg_details[0])
def test_validate_rdfg_status_failure_default_sg(
self, mck_get, mck_is_rep, mck_is_excl, mck_states, mck_slo):
array = self.data.array
extra_specs = deepcopy(self.data.rep_extra_specs6)
extra_specs[utils.REP_MODE] = utils.REP_ASYNC
extra_specs[utils.REP_CONFIG] = self.data.rep_config_async
rdfg = self.data.rdf_group_no_2
mode = utils.REP_ASYNC
disable_compression = self.utils.is_compression_disabled(extra_specs)
storage_group = self.utils.get_default_storage_group_name(
extra_specs['srp'], extra_specs['slo'], extra_specs['workload'],
disable_compression, True, extra_specs['rep_mode'])
self.assertRaises(exception.VolumeBackendAPIException,
self.common._validate_rdfg_status,
array, extra_specs)
self.assertEqual(1, mck_get.call_count)
self.assertEqual(1, mck_is_rep.call_count)
self.assertEqual(1, mck_is_excl.call_count)
self.assertEqual(1, mck_states.call_count)
mck_is_rep.assert_called_with(array, storage_group)
mck_is_excl.assert_called_with(array, storage_group)
mck_states.assert_called_with(array, storage_group, rdfg, mode)
@mock.patch.object(
provision.PowerMaxProvision, 'verify_slo_workload',
return_value=(True, True))
@mock.patch.object(utils.PowerMaxUtils, 'get_rdf_management_group_name',
return_value=tpd.PowerMaxData.rdf_managed_async_grp)
@mock.patch.object(common.PowerMaxCommon,
'_validate_management_group_volume_consistency',
return_value=False)
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_rdf_states',
side_effect=[True, True])
@mock.patch.object(common.PowerMaxCommon,
'_validate_rdf_group_storage_group_exclusivity',
side_effect=[True, True])
@mock.patch.object(common.PowerMaxCommon,
'_validate_storage_group_is_replication_enabled',
side_effect=[True, True])
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
return_value=tpd.PowerMaxData.sg_details[0])
def test_validate_rdfg_status_failure_management_sg(
self, mck_get, mck_is_rep, mck_is_excl, mck_states, mck_cons,
mck_mgrp_name, mck_slo):
array = self.data.array
extra_specs = deepcopy(self.data.rep_extra_specs6)
extra_specs[utils.REP_MODE] = utils.REP_ASYNC
extra_specs[utils.REP_CONFIG] = self.data.rep_config_async
management_sg_name = self.data.rdf_managed_async_grp
rdfg = self.data.rdf_group_no_2
mode = utils.REP_ASYNC
self.assertRaises(exception.VolumeBackendAPIException,
self.common._validate_rdfg_status,
array, extra_specs)
self.assertEqual(2, mck_get.call_count)
self.assertEqual(2, mck_is_rep.call_count)
self.assertEqual(2, mck_is_excl.call_count)
self.assertEqual(2, mck_states.call_count)
self.assertEqual(1, mck_cons.call_count)
self.assertEqual(1, mck_mgrp_name.call_count)
mck_is_rep.assert_called_with(array, management_sg_name)
mck_is_excl.assert_called_with(array, management_sg_name)
mck_states.assert_called_with(array, management_sg_name, rdfg, mode)
mck_cons.assert_called_with(array, management_sg_name, rdfg)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rep',
return_value={'rdf': True})
def test_validate_storage_group_is_replication_enabled_success(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
is_valid = self.common._validate_storage_group_is_replication_enabled(
array, storage_group)
self.assertTrue(is_valid)
mck_get.assert_called_once_with(array, storage_group)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rep',
return_value={'rdf': False})
def test_validate_storage_group_is_replication_enabled_failure(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
is_valid = self.common._validate_storage_group_is_replication_enabled(
array, storage_group)
self.assertFalse(is_valid)
mck_get.assert_called_once_with(array, storage_group)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rdf_group_state',
return_value=[utils.RDF_SYNC_STATE])
def test_validate_storage_group_rdf_states_success(self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
rdf_group_no = self.data.rdf_group_no_1
rep_mode = utils.REP_SYNC
is_valid = self.common._validate_storage_group_rdf_states(
array, storage_group, rdf_group_no, rep_mode)
self.assertTrue(is_valid)
mck_get.assert_called_once_with(array, storage_group, rdf_group_no)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rdf_group_state',
return_value=[utils.RDF_SYNC_STATE, utils.RDF_ACTIVE])
def test_validate_storage_group_rdf_states_multi_async_state_failure(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
rdf_group_no = self.data.rdf_group_no_1
rep_mode = utils.REP_ASYNC
is_valid = self.common._validate_storage_group_rdf_states(
array, storage_group, rdf_group_no, rep_mode)
self.assertFalse(is_valid)
mck_get.assert_called_once_with(array, storage_group, rdf_group_no)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rdf_group_state',
return_value=['invalid_state'])
def test_validate_storage_group_rdf_states_invalid_state_failure(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
rdf_group_no = self.data.rdf_group_no_1
rep_mode = utils.REP_ASYNC
is_valid = self.common._validate_storage_group_rdf_states(
array, storage_group, rdf_group_no, rep_mode)
self.assertFalse(is_valid)
mck_get.assert_called_once_with(array, storage_group, rdf_group_no)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rdf_groups',
return_value=[tpd.PowerMaxData.rdf_group_no_1])
def test_validate_rdf_group_storage_group_exclusivity_success(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
is_valid = self.common._validate_rdf_group_storage_group_exclusivity(
array, storage_group)
self.assertTrue(is_valid)
mck_get.assert_called_once_with(array, storage_group)
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group_rdf_groups',
return_value=[tpd.PowerMaxData.rdf_group_no_1,
tpd.PowerMaxData.rdf_group_no_2])
def test_validate_rdf_group_storage_group_exclusivity_failure(
self, mck_get):
array = self.data.array
storage_group = self.data.storagegroup_name_f
is_valid = self.common._validate_rdf_group_storage_group_exclusivity(
array, storage_group)
self.assertFalse(is_valid)
mck_get.assert_called_once_with(array, storage_group)
@mock.patch.object(rest.PowerMaxRest, 'get_volumes_in_storage_group',
return_value=[tpd.PowerMaxData.device_id])
@mock.patch.object(rest.PowerMaxRest, 'get_rdf_group_volume_list',
return_value=[tpd.PowerMaxData.device_id])
def test_validate_management_group_volume_consistency_success(
self, mck_rdf, mck_sg):
array = self.data.array
storage_group = self.data.rdf_managed_async_grp
rdf_group = self.data.rdf_group_no_1
is_valid = self.common._validate_management_group_volume_consistency(
array, storage_group, rdf_group)
self.assertTrue(is_valid)
mck_rdf.assert_called_once_with(array, rdf_group)
mck_sg.assert_called_once_with(array, storage_group)
@mock.patch.object(rest.PowerMaxRest, 'get_volumes_in_storage_group',
return_value=[tpd.PowerMaxData.device_id])
@mock.patch.object(rest.PowerMaxRest, 'get_rdf_group_volume_list',
return_value=[tpd.PowerMaxData.device_id,
tpd.PowerMaxData.device_id2])
def test_validate_management_group_volume_consistency_failure(
self, mck_rdf, mck_sg):
array = self.data.array
storage_group = self.data.rdf_managed_async_grp
rdf_group = self.data.rdf_group_no_1
is_valid = self.common._validate_management_group_volume_consistency(
array, storage_group, rdf_group)
self.assertFalse(is_valid)
mck_rdf.assert_called_once_with(array, rdf_group)
mck_sg.assert_called_once_with(array, storage_group)

View File

@ -1816,6 +1816,20 @@ class PowerMaxRestTest(test.TestCase):
self.data.array, 'replication', ref_get_resource)
self.assertEqual(states, [utils.RDF_SUSPENDED_STATE])
@mock.patch.object(rest.PowerMaxRest, 'get_resource',
return_value={'rdfgs': [100, 200]})
def test_get_storage_group_rdf_groups(self, mck_get):
rdf_groups = self.rest.get_storage_group_rdf_groups(
self.data.array, self.data.storagegroup_name_f)
self.assertEqual([100, 200], rdf_groups)
@mock.patch.object(rest.PowerMaxRest, 'get_resource',
return_value={"name": ["00038", "00039"]})
def test_get_rdf_group_volume_list(self, mck_get):
volumes_list = self.rest.get_rdf_group_volume_list(
self.data.array, self.data.rdf_group_no_1)
self.assertEqual(["00038", "00039"], volumes_list)
@mock.patch.object(rest.PowerMaxRest, 'get_resource')
def test_get_rdf_pair_volume(self, mck_get):
rdf_grp_no = self.data.rdf_group_no_1

View File

@ -1958,6 +1958,9 @@ class PowerMaxCommon(object):
return volume_name
array = extra_specs[utils.ARRAY]
if self.utils.is_replication_enabled(extra_specs):
self._validate_rdfg_status(array, extra_specs)
# Check if the volume being deleted is a
# source or target for copy session
self._sync_check(array, device_id, extra_specs,
@ -2021,6 +2024,9 @@ class PowerMaxCommon(object):
if self.utils.is_replication_enabled(extra_specs):
is_re, rep_mode = True, extra_specs['rep_mode']
if is_re:
self._validate_rdfg_status(array, extra_specs)
storagegroup_name = self.masking.get_or_create_default_storage_group(
array, extra_specs[utils.SRP], extra_specs[utils.SLO],
extra_specs[utils.WORKLOAD], extra_specs,
@ -3697,6 +3703,11 @@ class PowerMaxCommon(object):
utils.BACKEND_ID_LEGACY_REP)
backend_ids_differ = curr_backend_id != tgt_backend_id
if was_rep_enabled:
self._validate_rdfg_status(array, extra_specs)
if is_rep_enabled:
self._validate_rdfg_status(array, target_extra_specs)
# Scenario 1: Rep -> Non-Rep
# Scenario 2: Cleanup for Rep -> Diff Rep type
if (was_rep_enabled and not is_rep_enabled) or backend_ids_differ:
@ -6074,3 +6085,170 @@ class PowerMaxCommon(object):
array, volume, device_id, volume_name, extra_specs, False)
self._delete_from_srp(
array, device_id, volume_name, extra_specs)
def _validate_rdfg_status(self, array, extra_specs):
"""Validate RDF group states before and after various operations
:param array: array serial number -- str
:param extra_specs: volume extra specs -- dict
"""
rep_extra_specs = self._get_replication_extra_specs(
extra_specs, extra_specs[utils.REP_CONFIG])
rep_mode = extra_specs['rep_mode']
rdf_group_no = rep_extra_specs['rdf_group_no']
# Get default storage group for volume
disable_compression = self.utils.is_compression_disabled(extra_specs)
storage_group_name = self.utils.get_default_storage_group_name(
extra_specs['srp'], extra_specs['slo'], extra_specs['workload'],
disable_compression, True, extra_specs['rep_mode'])
# Check for storage group. Will be unavailable for first vol create
storage_group_details = self.rest.get_storage_group(
array, storage_group_name)
storage_group_available = storage_group_details is not None
if storage_group_available:
is_rep = self._validate_storage_group_is_replication_enabled(
array, storage_group_name)
is_exclusive = self._validate_rdf_group_storage_group_exclusivity(
array, storage_group_name)
is_valid_states = self._validate_storage_group_rdf_states(
array, storage_group_name, rdf_group_no, rep_mode)
if not (is_rep and is_exclusive and is_valid_states):
msg = (_('RDF validation for storage group %s failed. Please '
'see logged error messages for specific details.'
) % storage_group_name)
raise exception.VolumeBackendAPIException(msg)
# Perform checks against Async or Metro management storage groups
if rep_mode is not utils.REP_SYNC:
management_sg_name = self.utils.get_rdf_management_group_name(
extra_specs['rep_config'])
management_sg_details = self.rest.get_storage_group(
array, management_sg_name)
management_sg_available = management_sg_details is not None
if management_sg_available:
is_rep = self._validate_storage_group_is_replication_enabled(
array, management_sg_name)
is_excl = self._validate_rdf_group_storage_group_exclusivity(
array, management_sg_name)
is_valid_states = self._validate_storage_group_rdf_states(
array, management_sg_name, rdf_group_no, rep_mode)
is_cons = self._validate_management_group_volume_consistency(
array, management_sg_name, rdf_group_no)
if not (is_rep and is_excl and is_valid_states and is_cons):
msg = (_(
'RDF validation for storage group %s failed. Please '
'see logged error messages for specific details.')
% management_sg_name)
raise exception.VolumeBackendAPIException(msg)
def _validate_storage_group_is_replication_enabled(
self, array, storage_group_name):
"""Validate that a storage groups is marked as RDF enabled
:param array: array serial number -- str
:param storage_group_name: name of the storage group -- str
:returns: consistency validation checks passed -- boolean
"""
is_valid = True
sg_details = self.rest.get_storage_group_rep(array, storage_group_name)
sg_rdf_enabled = sg_details.get('rdf', False)
if not sg_rdf_enabled:
LOG.error('Storage group %s is expected to be RDF enabled but '
'is not. Please check that all volumes in this storage '
'group are RDF enabled and part of the same RDFG.',
storage_group_name)
is_valid = False
return is_valid
def _validate_storage_group_rdf_states(
self, array, storage_group_name, rdf_group_no, rep_mode):
"""Validate that the RDF states found for storage groups are valid.
:param array: array serial number -- str
:param storage_group_name: name of the storage group -- str
:param rep_mode: replication mode being used -- str
:returns: consistency validation checks passed -- boolean
"""
is_valid = True
sg_rdf_states = self.rest.get_storage_group_rdf_group_state(
array, storage_group_name, rdf_group_no)
# Verify Async & Metro modes only have a single state
if rep_mode is not utils.REP_SYNC:
if len(sg_rdf_states) > 1:
sg_states_str = (', '.join(sg_rdf_states))
LOG.error('More than one RDFG state found for storage group '
'%s. We expect a single state for all volumes when '
'using %s replication mode. Found %s states.',
storage_group_name, rep_mode, sg_states_str)
is_valid = False
# Determine which list of valid states to use
if rep_mode is utils.REP_SYNC:
valid_states = utils.RDF_VALID_STATES_SYNC
elif rep_mode is utils.REP_ASYNC:
valid_states = utils.RDF_VALID_STATES_ASYNC
else:
valid_states = utils.RDF_VALID_STATES_METRO
# Validate storage group states
for state in sg_rdf_states:
if state.lower() not in valid_states:
valid_states_str = (', '.join(valid_states))
LOG.error('Invalid RDF state found for storage group %s. '
'Found state %s. Valid states are %s.',
storage_group_name, state, valid_states_str)
is_valid = False
return is_valid
def _validate_rdf_group_storage_group_exclusivity(
self, array, storage_group_name):
"""Validate that a storage group only has one RDF group.
:param array: array serial number -- str
:param storage_group_name: name of storage group -- str
:returns: consistency validation checks passed -- boolean
"""
is_valid = True
sg_rdf_groups = self.rest.get_storage_group_rdf_groups(
array, storage_group_name)
if len(sg_rdf_groups) > 1:
rdf_groups_str = ', '.join(sg_rdf_groups)
LOG.error('Detected more than one RDF group associated with '
'storage group %s. Only one RDFG should be associated '
'with a storage group. Found RDF groups %s',
storage_group_name, rdf_groups_str)
is_valid = False
return is_valid
def _validate_management_group_volume_consistency(
self, array, management_sg_name, rdf_group_number):
"""Validate volume consistency between management SG and RDF group
:param array: array serial number -- str
:param management_sg_name: name of storage group -- str
:param rdf_group_number: rdf group number to check -- str
:returns: consistency validation checks passed -- boolean
"""
is_valid = True
rdfg_volumes = self.rest.get_rdf_group_volume_list(
array, rdf_group_number)
sg_volumes = self.rest.get_volumes_in_storage_group(
array, management_sg_name)
missing_volumes = list()
for rdfg_volume in rdfg_volumes:
if rdfg_volume not in sg_volumes:
missing_volumes.append(rdfg_volume)
if missing_volumes:
missing_volumes_str = ', '.join(missing_volumes)
LOG.error(
'Inconsistency found between management group %s and RDF '
'group %s. The following volumes are not in the management '
'storage group %s. All Asynchronous and Metro volumes must '
'be managed together.',
management_sg_name, rdf_group_number, missing_volumes_str)
is_valid = False
return is_valid

View File

@ -2273,6 +2273,18 @@ class PowerMaxRest(object):
return rdf_group.get('states', list()) if rdf_group else dict()
def get_storage_group_rdf_groups(self, array, storage_group):
"""Get a list of rdf group numbers used by a storage group.
:param array: the array serial number -- str
:param storage_group: the storage group name to check -- str
:return: RDFGs associated with the storage group -- dict
"""
resource = ('storagegroup/%(storage_group)s/rdf_group' % {
'storage_group': storage_group})
storage_group_details = self.get_resource(array, REPLICATION, resource)
return storage_group_details['rdfgs']
def get_rdf_group_list(self, array):
"""Get rdf group list from array.
@ -2280,6 +2292,18 @@ class PowerMaxRest(object):
"""
return self.get_resource(array, REPLICATION, 'rdf_group')
def get_rdf_group_volume_list(self, array, rdf_group_no):
"""Get a list of all volumes in an RDFG.
:param array: the array serial number -- str
:param rdf_group_no: the RDF group number -- str
:return: RDFG volume list -- list
"""
resource = ('rdf_group/%(rdf_group)s/volume' % {
'rdf_group': rdf_group_no})
rdf_group_volumes = self.get_resource(array, REPLICATION, resource)
return rdf_group_volumes['name']
def get_rdf_group_volume(self, array, src_device_id):
"""Get the RDF details for a volume.

View File

@ -79,6 +79,11 @@ RDF_FAILEDOVER_STATE = 'failed over'
RDF_ACTIVE = 'active'
RDF_ACTIVEACTIVE = 'activeactive'
RDF_ACTIVEBIAS = 'activebias'
RDF_VALID_STATES_SYNC = [RDF_SYNC_STATE, RDF_SYNCINPROG_STATE]
RDF_VALID_STATES_ASYNC = [RDF_CONSISTENT_STATE, RDF_SUSPENDED_STATE,
RDF_SYNCINPROG_STATE]
RDF_VALID_STATES_METRO = [RDF_ACTIVEBIAS, RDF_ACTIVEACTIVE,
RDF_SUSPENDED_STATE, RDF_SYNCINPROG_STATE]
RDF_CONS_EXEMPT = 'exempt'
RDF_ALLOW_METRO_DELETE = 'allow_delete_metro'
RDF_GROUP_NO = 'rdf_group_number'