VMAX driver - detach volume shouldn't remove from volume groups
Detaching a volume in VMAX removes the volume from all its current storage groups and returns it to its default storage group. This is not desired behaviour when the volume is a member of a generic volume group. The volume should only be removed from the storage group associated with the masking view related to the detach operation (or all masking views in the case of a force detach). This patch rectifies the issue. Change-Id: I065ffba9615af54998ae94a8d2d2fd3853f462cb Closes-Bug: 1721207
This commit is contained in:
parent
cbce7226bb
commit
27fd333df9
@ -2877,6 +2877,16 @@ class VMAXProvisionTest(test.TestCase):
|
|||||||
self.data.group_snapshot_name, self.data.extra_specs,
|
self.data.group_snapshot_name, self.data.extra_specs,
|
||||||
unlink=True)
|
unlink=True)
|
||||||
|
|
||||||
|
@mock.patch.object(rest.VMAXRest, 'get_storage_group',
|
||||||
|
side_effect=[None, VMAXCommonData.sg_details[1]])
|
||||||
|
@mock.patch.object(provision.VMAXProvision, 'create_volume_group')
|
||||||
|
def test_get_or_create_volume_group(self, mock_create, mock_sg):
|
||||||
|
for x in range(0, 2):
|
||||||
|
self.provision.get_or_create_volume_group(
|
||||||
|
self.data.array, self.data.test_group, self.data.extra_specs)
|
||||||
|
self.assertEqual(2, mock_sg.call_count)
|
||||||
|
self.assertEqual(1, mock_create.call_count)
|
||||||
|
|
||||||
|
|
||||||
class VMAXCommonTest(test.TestCase):
|
class VMAXCommonTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -2999,7 +3009,7 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
self.common._remove_members(array, volume, device_id,
|
self.common._remove_members(array, volume, device_id,
|
||||||
extra_specs, self.data.connector)
|
extra_specs, self.data.connector)
|
||||||
mock_rm.assert_called_once_with(
|
mock_rm.assert_called_once_with(
|
||||||
array, device_id, volume_name,
|
array, volume, device_id, volume_name,
|
||||||
extra_specs, True, self.data.connector)
|
extra_specs, True, self.data.connector)
|
||||||
|
|
||||||
def test_unmap_lun(self):
|
def test_unmap_lun(self):
|
||||||
@ -3567,9 +3577,9 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.common, 'cleanup_lun_replication') as mock_clean:
|
self.common, 'cleanup_lun_replication') as mock_clean:
|
||||||
self.common._remove_vol_and_cleanup_replication(
|
self.common._remove_vol_and_cleanup_replication(
|
||||||
array, device_id, volume_name, extra_specs)
|
array, device_id, volume_name, extra_specs, volume)
|
||||||
mock_rm.assert_called_once_with(
|
mock_rm.assert_called_once_with(
|
||||||
array, device_id, volume_name, extra_specs, False)
|
array, volume, device_id, volume_name, extra_specs, False)
|
||||||
mock_clean.assert_not_called()
|
mock_clean.assert_not_called()
|
||||||
self.common._remove_vol_and_cleanup_replication(
|
self.common._remove_vol_and_cleanup_replication(
|
||||||
array, device_id, volume_name, extra_specs, volume)
|
array, device_id, volume_name, extra_specs, volume)
|
||||||
@ -3929,7 +3939,7 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
self.common._slo_workload_migration(
|
self.common._slo_workload_migration(
|
||||||
device_id, volume, host, volume_name, new_type, extra_specs)
|
device_id, volume, host, volume_name, new_type, extra_specs)
|
||||||
self.common._migrate_volume.assert_called_once_with(
|
self.common._migrate_volume.assert_called_once_with(
|
||||||
extra_specs[utils.ARRAY], device_id,
|
extra_specs[utils.ARRAY], volume, device_id,
|
||||||
extra_specs[utils.SRP], 'Silver',
|
extra_specs[utils.SRP], 'Silver',
|
||||||
'OLTP', volume_name, new_type, extra_specs)
|
'OLTP', volume_name, new_type, extra_specs)
|
||||||
|
|
||||||
@ -3974,7 +3984,7 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
extra_specs)
|
extra_specs)
|
||||||
self.assertTrue(bool(migrate_status))
|
self.assertTrue(bool(migrate_status))
|
||||||
self.common._migrate_volume.assert_called_once_with(
|
self.common._migrate_volume.assert_called_once_with(
|
||||||
extra_specs[utils.ARRAY], device_id,
|
extra_specs[utils.ARRAY], volume, device_id,
|
||||||
extra_specs[utils.SRP], self.data.slo,
|
extra_specs[utils.SRP], self.data.slo,
|
||||||
self.data.workload, volume_name, new_type, extra_specs)
|
self.data.workload, volume_name, new_type, extra_specs)
|
||||||
|
|
||||||
@ -3985,25 +3995,28 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
device_id = self.data.device_id
|
device_id = self.data.device_id
|
||||||
volume_name = self.data.test_volume.name
|
volume_name = self.data.test_volume.name
|
||||||
extra_specs = self.data.extra_specs
|
extra_specs = self.data.extra_specs
|
||||||
|
volume = self.data.test_volume
|
||||||
new_type = {'extra_specs': {}}
|
new_type = {'extra_specs': {}}
|
||||||
migrate_status = self.common._migrate_volume(
|
migrate_status = self.common._migrate_volume(
|
||||||
self.data.array, device_id, self.data.srp, self.data.slo,
|
self.data.array, volume, device_id, self.data.srp,
|
||||||
self.data.workload, volume_name, new_type, extra_specs)
|
self.data.slo, self.data.workload, volume_name,
|
||||||
|
new_type, extra_specs)
|
||||||
self.assertTrue(migrate_status)
|
self.assertTrue(migrate_status)
|
||||||
target_extra_specs = {
|
target_extra_specs = {
|
||||||
'array': self.data.array, 'interval': 3,
|
'array': self.data.array, 'interval': 3,
|
||||||
'retries': 120, 'slo': self.data.slo,
|
'retries': 120, 'slo': self.data.slo,
|
||||||
'srp': self.data.srp, 'workload': self.data.workload}
|
'srp': self.data.srp, 'workload': self.data.workload}
|
||||||
mock_remove.assert_called_once_with(
|
mock_remove.assert_called_once_with(
|
||||||
self.data.array, device_id, volume_name,
|
self.data.array, volume, device_id, volume_name,
|
||||||
target_extra_specs, reset=True)
|
target_extra_specs, reset=True)
|
||||||
mock_remove.reset_mock()
|
mock_remove.reset_mock()
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.rest, 'get_storage_groups_from_volume',
|
self.rest, 'get_storage_groups_from_volume',
|
||||||
return_value=[]):
|
return_value=[]):
|
||||||
migrate_status = self.common._migrate_volume(
|
migrate_status = self.common._migrate_volume(
|
||||||
self.data.array, device_id, self.data.srp, self.data.slo,
|
self.data.array, volume, device_id, self.data.srp,
|
||||||
self.data.workload, volume_name, new_type, extra_specs)
|
self.data.slo, self.data.workload, volume_name,
|
||||||
|
new_type, extra_specs)
|
||||||
self.assertTrue(migrate_status)
|
self.assertTrue(migrate_status)
|
||||||
mock_remove.assert_not_called()
|
mock_remove.assert_not_called()
|
||||||
|
|
||||||
@ -4017,7 +4030,8 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
self.masking, 'get_or_create_default_storage_group',
|
self.masking, 'get_or_create_default_storage_group',
|
||||||
side_effect=exception.VolumeBackendAPIException):
|
side_effect=exception.VolumeBackendAPIException):
|
||||||
migrate_status = self.common._migrate_volume(
|
migrate_status = self.common._migrate_volume(
|
||||||
self.data.array, device_id, self.data.srp, self.data.slo,
|
self.data.array, self.data.test_volume, device_id,
|
||||||
|
self.data.srp, self.data.slo,
|
||||||
self.data.workload, volume_name, new_type, extra_specs)
|
self.data.workload, volume_name, new_type, extra_specs)
|
||||||
self.assertFalse(migrate_status)
|
self.assertFalse(migrate_status)
|
||||||
|
|
||||||
@ -4030,7 +4044,8 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
self.rest, 'is_volume_in_storagegroup',
|
self.rest, 'is_volume_in_storagegroup',
|
||||||
return_value=False):
|
return_value=False):
|
||||||
migrate_status = self.common._migrate_volume(
|
migrate_status = self.common._migrate_volume(
|
||||||
self.data.array, device_id, self.data.srp, self.data.slo,
|
self.data.array, self.data.test_volume, device_id,
|
||||||
|
self.data.srp, self.data.slo,
|
||||||
self.data.workload, volume_name, new_type, extra_specs)
|
self.data.workload, volume_name, new_type, extra_specs)
|
||||||
self.assertFalse(migrate_status)
|
self.assertFalse(migrate_status)
|
||||||
|
|
||||||
@ -4080,26 +4095,6 @@ class VMAXCommonTest(test.TestCase):
|
|||||||
self.data.srp, volume_name, False)
|
self.data.srp, volume_name, False)
|
||||||
self.assertEqual(ref_return, return_val)
|
self.assertEqual(ref_return, return_val)
|
||||||
|
|
||||||
def test_find_volume_group_name_from_id(self):
|
|
||||||
array = self.data.array
|
|
||||||
group_id = 'GrpId'
|
|
||||||
group_name = None
|
|
||||||
ref_group_name = self.data.storagegroup_name_with_id
|
|
||||||
with mock.patch.object(
|
|
||||||
self.rest, 'get_storage_group_list',
|
|
||||||
return_value=self.data.sg_list_rep):
|
|
||||||
group_name = self.common._find_volume_group_name_from_id(
|
|
||||||
array, group_id)
|
|
||||||
self.assertEqual(ref_group_name, group_name)
|
|
||||||
|
|
||||||
def test_find_volume_group_name_from_id_not_found(self):
|
|
||||||
array = self.data.array
|
|
||||||
group_id = 'GrpId'
|
|
||||||
group_name = None
|
|
||||||
group_name = self.common._find_volume_group_name_from_id(
|
|
||||||
array, group_id)
|
|
||||||
self.assertIsNone(group_name)
|
|
||||||
|
|
||||||
def test_find_volume_group(self):
|
def test_find_volume_group(self):
|
||||||
group = self.data.test_group_1
|
group = self.data.test_group_1
|
||||||
array = self.data.array
|
array = self.data.array
|
||||||
@ -4845,7 +4840,8 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
'get_or_create_masking_view_and_map_lun')
|
'get_or_create_masking_view_and_map_lun')
|
||||||
def test_setup_masking_view(self, mock_get_or_create_mv):
|
def test_setup_masking_view(self, mock_get_or_create_mv):
|
||||||
self.driver.masking.setup_masking_view(
|
self.driver.masking.setup_masking_view(
|
||||||
self.data.array, self.maskingviewdict, self.extra_specs)
|
self.data.array, self.data.test_volume,
|
||||||
|
self.maskingviewdict, self.extra_specs)
|
||||||
mock_get_or_create_mv.assert_called_once()
|
mock_get_or_create_mv.assert_called_once()
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@ -4869,19 +4865,22 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
mock_add_volume):
|
mock_add_volume):
|
||||||
rollback_dict = (
|
rollback_dict = (
|
||||||
self.driver.masking.get_or_create_masking_view_and_map_lun(
|
self.driver.masking.get_or_create_masking_view_and_map_lun(
|
||||||
self.data.array, self.maskingviewdict['maskingview_name'],
|
self.data.array, self.data.test_volume,
|
||||||
|
self.maskingviewdict['maskingview_name'],
|
||||||
self.maskingviewdict, self.extra_specs))
|
self.maskingviewdict, self.extra_specs))
|
||||||
self.assertEqual(self.maskingviewdict, rollback_dict)
|
self.assertEqual(self.maskingviewdict, rollback_dict)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VolumeBackendAPIException,
|
exception.VolumeBackendAPIException,
|
||||||
self.driver.masking.get_or_create_masking_view_and_map_lun,
|
self.driver.masking.get_or_create_masking_view_and_map_lun,
|
||||||
self.data.array, self.maskingviewdict['maskingview_name'],
|
self.data.array, self.data.test_volume,
|
||||||
|
self.maskingviewdict['maskingview_name'],
|
||||||
self.maskingviewdict, self.extra_specs)
|
self.maskingviewdict, self.extra_specs)
|
||||||
self.maskingviewdict['slo'] = None
|
self.maskingviewdict['slo'] = None
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VolumeBackendAPIException,
|
exception.VolumeBackendAPIException,
|
||||||
self.driver.masking.get_or_create_masking_view_and_map_lun,
|
self.driver.masking.get_or_create_masking_view_and_map_lun,
|
||||||
self.data.array, self.maskingviewdict['maskingview_name'],
|
self.data.array, self.data.test_volume,
|
||||||
|
self.maskingviewdict['maskingview_name'],
|
||||||
self.maskingviewdict, self.extra_specs)
|
self.maskingviewdict, self.extra_specs)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@ -5246,14 +5245,16 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VolumeBackendAPIException,
|
exception.VolumeBackendAPIException,
|
||||||
self.mask.check_if_rollback_action_for_masking_required,
|
self.mask.check_if_rollback_action_for_masking_required,
|
||||||
self.data.array, self.device_id, self.maskingviewdict)
|
self.data.array, self.data.test_volume,
|
||||||
|
self.device_id, self.maskingviewdict)
|
||||||
with mock.patch.object(masking.VMAXMasking,
|
with mock.patch.object(masking.VMAXMasking,
|
||||||
'remove_and_reset_members'):
|
'remove_and_reset_members'):
|
||||||
self.maskingviewdict[
|
self.maskingviewdict[
|
||||||
'default_sg_name'] = self.data.defaultstoragegroup_name
|
'default_sg_name'] = self.data.defaultstoragegroup_name
|
||||||
error_message = (
|
error_message = (
|
||||||
self.mask.check_if_rollback_action_for_masking_required(
|
self.mask.check_if_rollback_action_for_masking_required(
|
||||||
self.data.array, self.device_id, self.maskingviewdict))
|
self.data.array, self.data.test_volume,
|
||||||
|
self.device_id, self.maskingviewdict))
|
||||||
self.assertIsNone(error_message)
|
self.assertIsNone(error_message)
|
||||||
|
|
||||||
@mock.patch.object(rest.VMAXRest, 'delete_masking_view')
|
@mock.patch.object(rest.VMAXRest, 'delete_masking_view')
|
||||||
@ -5328,13 +5329,14 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(masking.VMAXMasking, '_cleanup_deletion')
|
@mock.patch.object(masking.VMAXMasking, '_cleanup_deletion')
|
||||||
def test_remove_and_reset_members(self, mock_cleanup):
|
def test_remove_and_reset_members(self, mock_cleanup):
|
||||||
self.mask.remove_and_reset_members(self.data.array, self.device_id,
|
self.mask.remove_and_reset_members(
|
||||||
self.volume_name, self.extra_specs,
|
self.data.array, self.device_id, self.data.test_volume,
|
||||||
reset=False)
|
self.volume_name, self.extra_specs, reset=False)
|
||||||
mock_cleanup.assert_called_once()
|
mock_cleanup.assert_called_once()
|
||||||
|
|
||||||
@mock.patch.object(rest.VMAXRest, 'get_storage_groups_from_volume',
|
@mock.patch.object(rest.VMAXRest, 'get_storage_groups_from_volume',
|
||||||
side_effect=[[VMAXCommonData.storagegroup_name_i],
|
side_effect=[[VMAXCommonData.storagegroup_name_i],
|
||||||
|
[VMAXCommonData.storagegroup_name_i],
|
||||||
[VMAXCommonData.storagegroup_name_i,
|
[VMAXCommonData.storagegroup_name_i,
|
||||||
VMAXCommonData.storagegroup_name_f]])
|
VMAXCommonData.storagegroup_name_f]])
|
||||||
@mock.patch.object(masking.VMAXMasking, 'remove_volume_from_sg')
|
@mock.patch.object(masking.VMAXMasking, 'remove_volume_from_sg')
|
||||||
@ -5342,14 +5344,19 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
'add_volume_to_default_storage_group')
|
'add_volume_to_default_storage_group')
|
||||||
def test_cleanup_deletion(self, mock_add, mock_remove_vol, mock_get_sg):
|
def test_cleanup_deletion(self, mock_add, mock_remove_vol, mock_get_sg):
|
||||||
self.mask._cleanup_deletion(
|
self.mask._cleanup_deletion(
|
||||||
self.data.array, self.device_id, self.volume_name,
|
self.data.array, self.data.test_volume, self.device_id,
|
||||||
self.extra_specs, None, True)
|
self.volume_name, self.extra_specs, None, True)
|
||||||
mock_add.assert_not_called()
|
mock_add.assert_not_called()
|
||||||
self.mask._cleanup_deletion(
|
self.mask._cleanup_deletion(
|
||||||
self.data.array, self.device_id, self.volume_name,
|
self.data.array, self.data.test_volume, self.device_id,
|
||||||
self.extra_specs, None, True)
|
self.volume_name, self.extra_specs, self.data.connector, True)
|
||||||
mock_add.assert_called_once_with(self.data.array, self.device_id,
|
mock_add.assert_not_called()
|
||||||
self.volume_name, self.extra_specs)
|
self.mask._cleanup_deletion(
|
||||||
|
self.data.array, self.data.test_volume, self.device_id,
|
||||||
|
self.volume_name, self.extra_specs, None, True)
|
||||||
|
mock_add.assert_called_once_with(
|
||||||
|
self.data.array, self.device_id,
|
||||||
|
self.volume_name, self.extra_specs, volume=self.data.test_volume)
|
||||||
|
|
||||||
@mock.patch.object(masking.VMAXMasking, '_last_vol_in_sg')
|
@mock.patch.object(masking.VMAXMasking, '_last_vol_in_sg')
|
||||||
@mock.patch.object(masking.VMAXMasking, '_multiple_vols_in_sg')
|
@mock.patch.object(masking.VMAXMasking, '_multiple_vols_in_sg')
|
||||||
@ -5491,6 +5498,14 @@ class VMAXMaskingTest(test.TestCase):
|
|||||||
self.data.array, self.device_id, self.volume_name,
|
self.data.array, self.device_id, self.volume_name,
|
||||||
self.extra_specs, src_sg=self.data.storagegroup_name_i)
|
self.extra_specs, src_sg=self.data.storagegroup_name_i)
|
||||||
mock_move.assert_called_once()
|
mock_move.assert_called_once()
|
||||||
|
mock_add_sg.reset_mock()
|
||||||
|
vol_grp_member = deepcopy(self.data.test_volume)
|
||||||
|
vol_grp_member.group_id = self.data.test_vol_grp_name_id_only
|
||||||
|
vol_grp_member.group = self.data.test_group
|
||||||
|
self.mask.add_volume_to_default_storage_group(
|
||||||
|
self.data.array, self.device_id, self.volume_name,
|
||||||
|
self.extra_specs, volume=vol_grp_member)
|
||||||
|
self.assertEqual(2, mock_add_sg.call_count)
|
||||||
|
|
||||||
@mock.patch.object(provision.VMAXProvision, 'create_storage_group')
|
@mock.patch.object(provision.VMAXProvision, 'create_storage_group')
|
||||||
def test_get_or_create_default_storage_group(self, mock_create_sg):
|
def test_get_or_create_default_storage_group(self, mock_create_sg):
|
||||||
@ -5917,8 +5932,8 @@ class VMAXCommonReplicationTest(test.TestCase):
|
|||||||
self.data.device_id2, self.data.rdf_group_no, "1",
|
self.data.device_id2, self.data.rdf_group_no, "1",
|
||||||
rep_extra_specs)
|
rep_extra_specs)
|
||||||
mock_rm.assert_called_once_with(
|
mock_rm.assert_called_once_with(
|
||||||
self.data.remote_array, self.data.device_id2, "1",
|
self.data.remote_array, self.data.test_volume,
|
||||||
rep_extra_specs, False)
|
self.data.device_id2, "1", rep_extra_specs, False)
|
||||||
# Cleanup legacy replication
|
# Cleanup legacy replication
|
||||||
self.common.cleanup_lun_replication(
|
self.common.cleanup_lun_replication(
|
||||||
self.data.test_legacy_vol, "1", self.data.device_id,
|
self.data.test_legacy_vol, "1", self.data.device_id,
|
||||||
@ -6122,9 +6137,9 @@ class VMAXCommonReplicationTest(test.TestCase):
|
|||||||
rep_config = self.utils.get_replication_config(
|
rep_config = self.utils.get_replication_config(
|
||||||
[self.replication_device])
|
[self.replication_device])
|
||||||
self.common.enable_rdf(
|
self.common.enable_rdf(
|
||||||
self.data.array, self.data.device_id, self.data.rdf_group_no,
|
self.data.array, self.data.test_volume, self.data.device_id,
|
||||||
rep_config, 'OS-1', self.data.remote_array, self.data.device_id2,
|
self.data.rdf_group_no, rep_config, 'OS-1',
|
||||||
self.extra_specs)
|
self.data.remote_array, self.data.device_id2, self.extra_specs)
|
||||||
self.assertEqual(2, mock_remove.call_count)
|
self.assertEqual(2, mock_remove.call_count)
|
||||||
self.assertEqual(2, mock_add.call_count)
|
self.assertEqual(2, mock_add.call_count)
|
||||||
|
|
||||||
@ -6135,7 +6150,7 @@ class VMAXCommonReplicationTest(test.TestCase):
|
|||||||
[self.replication_device])
|
[self.replication_device])
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VolumeBackendAPIException, self.common.enable_rdf,
|
exception.VolumeBackendAPIException, self.common.enable_rdf,
|
||||||
self.data.array, self.data.device_id,
|
self.data.array, self.data.test_volume, self.data.device_id,
|
||||||
self.data.failed_resource, rep_config, 'OS-1',
|
self.data.failed_resource, rep_config, 'OS-1',
|
||||||
self.data.remote_array, self.data.device_id2, self.extra_specs)
|
self.data.remote_array, self.data.device_id2, self.extra_specs)
|
||||||
self.assertEqual(1, mock_cleanup.call_count)
|
self.assertEqual(1, mock_cleanup.call_count)
|
||||||
|
@ -254,19 +254,17 @@ class VMAXCommon(object):
|
|||||||
volume_name, volume_size, extra_specs))
|
volume_name, volume_size, extra_specs))
|
||||||
|
|
||||||
if volume.group_id is not None:
|
if volume.group_id is not None:
|
||||||
group_name = self._find_volume_group_name_from_id(
|
group_name = self.provision.get_or_create_volume_group(
|
||||||
extra_specs[utils.ARRAY], volume.group_id)
|
extra_specs[utils.ARRAY], volume.group, extra_specs)
|
||||||
if group_name is not None:
|
self.masking.add_volume_to_storage_group(
|
||||||
self.masking.add_volume_to_storage_group(
|
extra_specs[utils.ARRAY], volume_dict['device_id'],
|
||||||
extra_specs[utils.ARRAY], volume_dict['device_id'],
|
group_name, volume_name, extra_specs)
|
||||||
group_name, volume_name, extra_specs)
|
|
||||||
|
|
||||||
# Set-up volume replication, if enabled
|
# Set-up volume replication, if enabled
|
||||||
if self.utils.is_replication_enabled(extra_specs):
|
if self.utils.is_replication_enabled(extra_specs):
|
||||||
rep_update = self._replicate_volume(volume, volume_name,
|
rep_update = self._replicate_volume(volume, volume_name,
|
||||||
volume_dict, extra_specs)
|
volume_dict, extra_specs)
|
||||||
model_update.update(rep_update)
|
model_update.update(rep_update)
|
||||||
|
|
||||||
LOG.info("Leaving create_volume: %(name)s. Volume dict: %(dict)s.",
|
LOG.info("Leaving create_volume: %(name)s. Volume dict: %(dict)s.",
|
||||||
{'name': volume_name, 'dict': volume_dict})
|
{'name': volume_name, 'dict': volume_dict})
|
||||||
model_update.update(
|
model_update.update(
|
||||||
@ -424,7 +422,8 @@ class VMAXCommon(object):
|
|||||||
volume_name = volume.name
|
volume_name = volume.name
|
||||||
LOG.debug("Detaching volume %s.", volume_name)
|
LOG.debug("Detaching volume %s.", volume_name)
|
||||||
return self.masking.remove_and_reset_members(
|
return self.masking.remove_and_reset_members(
|
||||||
array, device_id, volume_name, extra_specs, True, connector)
|
array, volume, device_id, volume_name,
|
||||||
|
extra_specs, True, connector)
|
||||||
|
|
||||||
def _unmap_lun(self, volume, connector):
|
def _unmap_lun(self, volume, connector):
|
||||||
"""Unmaps a volume from the host.
|
"""Unmaps a volume from the host.
|
||||||
@ -587,7 +586,7 @@ class VMAXCommon(object):
|
|||||||
else:
|
else:
|
||||||
masking_view_dict['isLiveMigration'] = False
|
masking_view_dict['isLiveMigration'] = False
|
||||||
rollback_dict = self.masking.setup_masking_view(
|
rollback_dict = self.masking.setup_masking_view(
|
||||||
masking_view_dict[utils.ARRAY],
|
masking_view_dict[utils.ARRAY], volume,
|
||||||
masking_view_dict, extra_specs)
|
masking_view_dict, extra_specs)
|
||||||
|
|
||||||
# Find host lun id again after the volume is exported to the host.
|
# Find host lun id again after the volume is exported to the host.
|
||||||
@ -1495,7 +1494,7 @@ class VMAXCommon(object):
|
|||||||
raise exception.VolumeBackendAPIException(data=error_message)
|
raise exception.VolumeBackendAPIException(data=error_message)
|
||||||
|
|
||||||
def _remove_vol_and_cleanup_replication(
|
def _remove_vol_and_cleanup_replication(
|
||||||
self, array, device_id, volume_name, extra_specs, volume=None):
|
self, array, device_id, volume_name, extra_specs, volume):
|
||||||
"""Remove a volume from its storage groups and cleanup replication.
|
"""Remove a volume from its storage groups and cleanup replication.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
@ -1506,7 +1505,7 @@ class VMAXCommon(object):
|
|||||||
"""
|
"""
|
||||||
# Remove from any storage groups
|
# Remove from any storage groups
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
array, device_id, volume_name, extra_specs, False)
|
array, volume, device_id, volume_name, extra_specs, False)
|
||||||
# Cleanup remote replication
|
# Cleanup remote replication
|
||||||
if self.utils.is_replication_enabled(extra_specs):
|
if self.utils.is_replication_enabled(extra_specs):
|
||||||
self.cleanup_lun_replication(volume, volume_name,
|
self.cleanup_lun_replication(volume, volume_name,
|
||||||
@ -1953,20 +1952,21 @@ class VMAXCommon(object):
|
|||||||
'targetHost': host['host'],
|
'targetHost': host['host'],
|
||||||
'cc': do_change_compression})
|
'cc': do_change_compression})
|
||||||
return self._migrate_volume(
|
return self._migrate_volume(
|
||||||
extra_specs[utils.ARRAY], device_id,
|
extra_specs[utils.ARRAY], volume, device_id,
|
||||||
extra_specs[utils.SRP], target_slo,
|
extra_specs[utils.SRP], target_slo,
|
||||||
target_workload, volume_name, new_type, extra_specs)
|
target_workload, volume_name, new_type, extra_specs)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _migrate_volume(
|
def _migrate_volume(
|
||||||
self, array, device_id, srp, target_slo,
|
self, array, volume, device_id, srp, target_slo,
|
||||||
target_workload, volume_name, new_type, extra_specs):
|
target_workload, volume_name, new_type, extra_specs):
|
||||||
"""Migrate from one slo/workload combination to another.
|
"""Migrate from one slo/workload combination to another.
|
||||||
|
|
||||||
This requires moving the volume from its current SG to a
|
This requires moving the volume from its current SG to a
|
||||||
new or existing SG that has the target attributes.
|
new or existing SG that has the target attributes.
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param device_id: the device number
|
:param device_id: the device number
|
||||||
:param srp: the storage resource pool
|
:param srp: the storage resource pool
|
||||||
:param target_slo: the target service level
|
:param target_slo: the target service level
|
||||||
@ -2005,7 +2005,7 @@ class VMAXCommon(object):
|
|||||||
array, device_id, target_sg_name, volume_name, extra_specs)
|
array, device_id, target_sg_name, volume_name, extra_specs)
|
||||||
else:
|
else:
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
array, device_id, volume_name, target_extra_specs,
|
array, volume, device_id, volume_name, target_extra_specs,
|
||||||
reset=True)
|
reset=True)
|
||||||
|
|
||||||
# Check that it has been added.
|
# Check that it has been added.
|
||||||
@ -2145,7 +2145,7 @@ class VMAXCommon(object):
|
|||||||
|
|
||||||
# Enable rdf replication and establish the link
|
# Enable rdf replication and establish the link
|
||||||
rdf_dict = self.enable_rdf(
|
rdf_dict = self.enable_rdf(
|
||||||
array, device_id, rdf_group_no, self.rep_config,
|
array, volume, device_id, rdf_group_no, self.rep_config,
|
||||||
target_name, remote_array, target_device_id, extra_specs)
|
target_name, remote_array, target_device_id, extra_specs)
|
||||||
|
|
||||||
LOG.info('Successfully setup replication for %s.',
|
LOG.info('Successfully setup replication for %s.',
|
||||||
@ -2189,7 +2189,7 @@ class VMAXCommon(object):
|
|||||||
if target_device is not None:
|
if target_device is not None:
|
||||||
# Clean-up target
|
# Clean-up target
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
remote_array, target_device, volume_name,
|
remote_array, volume, target_device, volume_name,
|
||||||
rep_extra_specs, False)
|
rep_extra_specs, False)
|
||||||
self._cleanup_remote_target(
|
self._cleanup_remote_target(
|
||||||
array, remote_array, device_id, target_device,
|
array, remote_array, device_id, target_device,
|
||||||
@ -2494,13 +2494,13 @@ class VMAXCommon(object):
|
|||||||
# have a mix of replicated and non-replicated volumes as
|
# have a mix of replicated and non-replicated volumes as
|
||||||
# the SRDF groups become unmanageable).
|
# the SRDF groups become unmanageable).
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
array, device_id, volume_name, extra_specs, False)
|
array, volume, device_id, volume_name, extra_specs, False)
|
||||||
|
|
||||||
# Repeat on target side
|
# Repeat on target side
|
||||||
rep_extra_specs = self._get_replication_extra_specs(
|
rep_extra_specs = self._get_replication_extra_specs(
|
||||||
extra_specs, self.rep_config)
|
extra_specs, self.rep_config)
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
remote_array, target_device, volume_name,
|
remote_array, volume, target_device, volume_name,
|
||||||
rep_extra_specs, False)
|
rep_extra_specs, False)
|
||||||
|
|
||||||
LOG.info("Breaking replication relationship...")
|
LOG.info("Breaking replication relationship...")
|
||||||
@ -2539,11 +2539,12 @@ class VMAXCommon(object):
|
|||||||
LOG.error(exception_message)
|
LOG.error(exception_message)
|
||||||
raise exception.VolumeBackendAPIException(data=exception_message)
|
raise exception.VolumeBackendAPIException(data=exception_message)
|
||||||
|
|
||||||
def enable_rdf(self, array, device_id, rdf_group_no, rep_config,
|
def enable_rdf(self, array, volume, device_id, rdf_group_no, rep_config,
|
||||||
target_name, remote_array, target_device, extra_specs):
|
target_name, remote_array, target_device, extra_specs):
|
||||||
"""Create a replication relationship with a target volume.
|
"""Create a replication relationship with a target volume.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param device_id: the device id
|
:param device_id: the device id
|
||||||
:param rdf_group_no: the rdf group number
|
:param rdf_group_no: the rdf group number
|
||||||
:param rep_config: the replication config
|
:param rep_config: the replication config
|
||||||
@ -2559,10 +2560,10 @@ class VMAXCommon(object):
|
|||||||
# Remove source and target instances from their
|
# Remove source and target instances from their
|
||||||
# default storage groups
|
# default storage groups
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
array, device_id, target_name, extra_specs, False)
|
array, volume, device_id, target_name, extra_specs, False)
|
||||||
|
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
remote_array, target_device, target_name,
|
remote_array, volume, target_device, target_name,
|
||||||
rep_extra_specs, False)
|
rep_extra_specs, False)
|
||||||
|
|
||||||
# Establish replication relationship
|
# Establish replication relationship
|
||||||
@ -2585,7 +2586,7 @@ class VMAXCommon(object):
|
|||||||
"group. Volume name: %(name)s "),
|
"group. Volume name: %(name)s "),
|
||||||
{'name': target_name})
|
{'name': target_name})
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
remote_array, target_device, target_name,
|
remote_array, volume, target_device, target_name,
|
||||||
rep_extra_specs, False)
|
rep_extra_specs, False)
|
||||||
self._cleanup_remote_target(
|
self._cleanup_remote_target(
|
||||||
array, remote_array, device_id, target_device,
|
array, remote_array, device_id, target_device,
|
||||||
@ -3009,21 +3010,6 @@ class VMAXCommon(object):
|
|||||||
|
|
||||||
return model_update, snapshots_model_update
|
return model_update, snapshots_model_update
|
||||||
|
|
||||||
def _find_volume_group_name_from_id(self, array, group_id):
|
|
||||||
"""Finds the volume group name given its id
|
|
||||||
|
|
||||||
:param array: the array serial number
|
|
||||||
:param group_id: the group id
|
|
||||||
:returns: group_name: Name of the group
|
|
||||||
"""
|
|
||||||
group_name = None
|
|
||||||
sg_list = self.rest.get_storage_group_list(array)
|
|
||||||
for sg in sg_list:
|
|
||||||
if group_id in sg:
|
|
||||||
group_name = sg
|
|
||||||
return group_name
|
|
||||||
return group_name
|
|
||||||
|
|
||||||
def _find_volume_group(self, array, group):
|
def _find_volume_group(self, array, group):
|
||||||
"""Finds a volume group given the group.
|
"""Finds a volume group given the group.
|
||||||
|
|
||||||
|
@ -40,24 +40,25 @@ class VMAXMasking(object):
|
|||||||
self.provision = provision.VMAXProvision(self.rest)
|
self.provision = provision.VMAXProvision(self.rest)
|
||||||
|
|
||||||
def setup_masking_view(
|
def setup_masking_view(
|
||||||
self, serial_number, masking_view_dict, extra_specs):
|
self, serial_number, volume, masking_view_dict, extra_specs):
|
||||||
|
|
||||||
@coordination.synchronized("emc-mv-{maskingview_name}")
|
@coordination.synchronized("emc-mv-{maskingview_name}")
|
||||||
def do_get_or_create_masking_view_and_map_lun(maskingview_name):
|
def do_get_or_create_masking_view_and_map_lun(maskingview_name):
|
||||||
return self.get_or_create_masking_view_and_map_lun(
|
return self.get_or_create_masking_view_and_map_lun(
|
||||||
serial_number, maskingview_name, masking_view_dict,
|
serial_number, volume, maskingview_name, masking_view_dict,
|
||||||
extra_specs)
|
extra_specs)
|
||||||
return do_get_or_create_masking_view_and_map_lun(
|
return do_get_or_create_masking_view_and_map_lun(
|
||||||
masking_view_dict[utils.MV_NAME])
|
masking_view_dict[utils.MV_NAME])
|
||||||
|
|
||||||
def get_or_create_masking_view_and_map_lun(
|
def get_or_create_masking_view_and_map_lun(
|
||||||
self, serial_number, maskingview_name, masking_view_dict,
|
self, serial_number, volume, maskingview_name, masking_view_dict,
|
||||||
extra_specs):
|
extra_specs):
|
||||||
"""Get or Create a masking view and add a volume to the storage group.
|
"""Get or Create a masking view and add a volume to the storage group.
|
||||||
|
|
||||||
Given a masking view dict either get or create a masking view and add
|
Given a masking view dict either get or create a masking view and add
|
||||||
the volume to the associated storage group.
|
the volume to the associated storage group.
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param maskingview_name: the masking view name
|
:param maskingview_name: the masking view name
|
||||||
:param masking_view_dict: the masking view dict
|
:param masking_view_dict: the masking view dict
|
||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
@ -112,7 +113,7 @@ class VMAXMasking(object):
|
|||||||
|
|
||||||
if rollback_dict['slo'] is not None:
|
if rollback_dict['slo'] is not None:
|
||||||
self.check_if_rollback_action_for_masking_required(
|
self.check_if_rollback_action_for_masking_required(
|
||||||
serial_number, device_id, masking_view_dict)
|
serial_number, volume, device_id, masking_view_dict)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._check_adding_volume_to_storage_group(
|
self._check_adding_volume_to_storage_group(
|
||||||
@ -832,7 +833,7 @@ class VMAXMasking(object):
|
|||||||
return error_message
|
return error_message
|
||||||
|
|
||||||
def check_if_rollback_action_for_masking_required(
|
def check_if_rollback_action_for_masking_required(
|
||||||
self, serial_number, device_id, rollback_dict):
|
self, serial_number, volume, device_id, rollback_dict):
|
||||||
"""Rollback action for volumes with an associated service level.
|
"""Rollback action for volumes with an associated service level.
|
||||||
|
|
||||||
We need to be able to return the volume to the default storage group
|
We need to be able to return the volume to the default storage group
|
||||||
@ -841,6 +842,7 @@ class VMAXMasking(object):
|
|||||||
the exception occurred. We also may need to clean up any unused
|
the exception occurred. We also may need to clean up any unused
|
||||||
initiator groups.
|
initiator groups.
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param device_id: the device id
|
:param device_id: the device id
|
||||||
:param rollback_dict: the rollback dict
|
:param rollback_dict: the rollback dict
|
||||||
:returns: error message -- string, or None
|
:returns: error message -- string, or None
|
||||||
@ -883,9 +885,10 @@ class VMAXMasking(object):
|
|||||||
# Remove it from its current storage group and return it
|
# Remove it from its current storage group and return it
|
||||||
# to its default masking view if slo is defined.
|
# to its default masking view if slo is defined.
|
||||||
self.remove_and_reset_members(
|
self.remove_and_reset_members(
|
||||||
serial_number, device_id,
|
serial_number, volume, device_id,
|
||||||
rollback_dict['volume_name'],
|
rollback_dict['volume_name'],
|
||||||
rollback_dict['extra_specs'])
|
rollback_dict['extra_specs'], True,
|
||||||
|
rollback_dict['connector'])
|
||||||
message = (_("Rollback - Volume in another storage "
|
message = (_("Rollback - Volume in another storage "
|
||||||
"group besides default storage group."))
|
"group besides default storage group."))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -1019,11 +1022,12 @@ class VMAXMasking(object):
|
|||||||
|
|
||||||
@coordination.synchronized("emc-vol-{device_id}")
|
@coordination.synchronized("emc-vol-{device_id}")
|
||||||
def remove_and_reset_members(
|
def remove_and_reset_members(
|
||||||
self, serial_number, device_id, volume_name, extra_specs,
|
self, serial_number, volume, device_id, volume_name,
|
||||||
reset=True, connector=None):
|
extra_specs, reset=True, connector=None):
|
||||||
"""This is called on a delete, unmap device or rollback.
|
"""This is called on a delete, unmap device or rollback.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param device_id: the volume device id
|
:param device_id: the volume device id
|
||||||
:param volume_name: the volume name
|
:param volume_name: the volume name
|
||||||
:param extra_specs: additional info
|
:param extra_specs: additional info
|
||||||
@ -1031,33 +1035,48 @@ class VMAXMasking(object):
|
|||||||
:param connector: the connector object (optional)
|
:param connector: the connector object (optional)
|
||||||
"""
|
"""
|
||||||
self._cleanup_deletion(
|
self._cleanup_deletion(
|
||||||
serial_number, device_id, volume_name,
|
serial_number, volume, device_id, volume_name,
|
||||||
extra_specs, connector, reset)
|
extra_specs, connector, reset)
|
||||||
|
|
||||||
def _cleanup_deletion(
|
def _cleanup_deletion(
|
||||||
self, serial_number, device_id, volume_name,
|
self, serial_number, volume, device_id, volume_name,
|
||||||
extra_specs, connector, reset):
|
extra_specs, connector, reset):
|
||||||
"""Prepare a volume for a delete operation.
|
"""Prepare a volume for a delete operation.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
|
:param volume: the volume object
|
||||||
:param device_id: the volume device id
|
:param device_id: the volume device id
|
||||||
:param volume_name: the volume name
|
:param volume_name: the volume name
|
||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
:param connector: the connector object
|
:param connector: the connector object
|
||||||
"""
|
"""
|
||||||
move = False
|
move = False
|
||||||
|
short_host_name = None
|
||||||
storagegroup_names = (self.rest.get_storage_groups_from_volume(
|
storagegroup_names = (self.rest.get_storage_groups_from_volume(
|
||||||
serial_number, device_id))
|
serial_number, device_id))
|
||||||
if storagegroup_names:
|
if storagegroup_names:
|
||||||
if len(storagegroup_names) == 1 and reset is True:
|
if len(storagegroup_names) == 1 and reset is True:
|
||||||
move = True
|
move = True
|
||||||
for sg_name in storagegroup_names:
|
elif connector is not None and reset is True:
|
||||||
self.remove_volume_from_sg(
|
short_host_name = self.utils.get_host_short_name(
|
||||||
serial_number, device_id, volume_name, sg_name,
|
connector['host'])
|
||||||
extra_specs, connector, move)
|
move = True
|
||||||
|
if short_host_name:
|
||||||
|
for sg_name in storagegroup_names:
|
||||||
|
if short_host_name in sg_name:
|
||||||
|
self.remove_volume_from_sg(
|
||||||
|
serial_number, device_id, volume_name, sg_name,
|
||||||
|
extra_specs, connector, move)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for sg_name in storagegroup_names:
|
||||||
|
self.remove_volume_from_sg(
|
||||||
|
serial_number, device_id, volume_name, sg_name,
|
||||||
|
extra_specs, connector, move)
|
||||||
if reset is True and move is False:
|
if reset is True and move is False:
|
||||||
self.add_volume_to_default_storage_group(
|
self.add_volume_to_default_storage_group(
|
||||||
serial_number, device_id, volume_name, extra_specs)
|
serial_number, device_id, volume_name,
|
||||||
|
extra_specs, volume=volume)
|
||||||
|
|
||||||
def remove_volume_from_sg(
|
def remove_volume_from_sg(
|
||||||
self, serial_number, device_id, vol_name, storagegroup_name,
|
self, serial_number, device_id, vol_name, storagegroup_name,
|
||||||
@ -1386,7 +1405,7 @@ class VMAXMasking(object):
|
|||||||
|
|
||||||
def add_volume_to_default_storage_group(
|
def add_volume_to_default_storage_group(
|
||||||
self, serial_number, device_id, volume_name,
|
self, serial_number, device_id, volume_name,
|
||||||
extra_specs, src_sg=None):
|
extra_specs, src_sg=None, volume=None):
|
||||||
"""Return volume to its default storage group.
|
"""Return volume to its default storage group.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
@ -1394,6 +1413,7 @@ class VMAXMasking(object):
|
|||||||
:param volume_name: the volume name
|
:param volume_name: the volume name
|
||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
:param src_sg: the source storage group, if any
|
:param src_sg: the source storage group, if any
|
||||||
|
:param volume: the volume object
|
||||||
"""
|
"""
|
||||||
do_disable_compression = self.utils.is_compression_disabled(
|
do_disable_compression = self.utils.is_compression_disabled(
|
||||||
extra_specs)
|
extra_specs)
|
||||||
@ -1414,6 +1434,16 @@ class VMAXMasking(object):
|
|||||||
self._check_adding_volume_to_storage_group(
|
self._check_adding_volume_to_storage_group(
|
||||||
serial_number, device_id, storagegroup_name, volume_name,
|
serial_number, device_id, storagegroup_name, volume_name,
|
||||||
extra_specs)
|
extra_specs)
|
||||||
|
if volume:
|
||||||
|
# Need to check if the volume needs to be returned to a
|
||||||
|
# generic volume group. This may be necessary in a force-detach
|
||||||
|
# situation.
|
||||||
|
if volume.group_id is not None:
|
||||||
|
vol_grp_name = self.provision.get_or_create_volume_group(
|
||||||
|
serial_number, volume.group, extra_specs)
|
||||||
|
self._check_adding_volume_to_storage_group(
|
||||||
|
serial_number, device_id,
|
||||||
|
vol_grp_name, volume_name, extra_specs)
|
||||||
|
|
||||||
def get_or_create_default_storage_group(
|
def get_or_create_default_storage_group(
|
||||||
self, serial_number, srp, slo, workload, extra_specs,
|
self, serial_number, srp, slo, workload, extra_specs,
|
||||||
|
@ -448,6 +448,33 @@ class VMAXProvision(object):
|
|||||||
self.rest.modify_rdf_device_pair(
|
self.rest.modify_rdf_device_pair(
|
||||||
array, device_id, rdf_group, extra_specs, split=False)
|
array, device_id, rdf_group, extra_specs, split=False)
|
||||||
|
|
||||||
|
def get_or_create_volume_group(self, array, group, extra_specs):
|
||||||
|
"""Get or create a volume group.
|
||||||
|
|
||||||
|
Sometimes it may be necessary to recreate a volume group on the
|
||||||
|
backend - for example, when the last member volume has been removed
|
||||||
|
from the group, but the cinder group object has not been deleted.
|
||||||
|
:param array: the array serial number
|
||||||
|
:param group: the group object
|
||||||
|
:param extra_specs: the extra specifications
|
||||||
|
:return: group name
|
||||||
|
"""
|
||||||
|
vol_grp_name = self.utils.update_volume_group_name(group)
|
||||||
|
return self.get_or_create_group(array, vol_grp_name, extra_specs)
|
||||||
|
|
||||||
|
def get_or_create_group(self, array, group_name, extra_specs):
|
||||||
|
"""Get or create a generic volume group.
|
||||||
|
|
||||||
|
:param array: the array serial number
|
||||||
|
:param group_name: the group name
|
||||||
|
:param extra_specs: the extra specifications
|
||||||
|
:return: group name
|
||||||
|
"""
|
||||||
|
storage_group = self.rest.get_storage_group(array, group_name)
|
||||||
|
if not storage_group:
|
||||||
|
self.create_volume_group(array, group_name, extra_specs)
|
||||||
|
return group_name
|
||||||
|
|
||||||
def create_volume_group(self, array, group_name, extra_specs):
|
def create_volume_group(self, array, group_name, extra_specs):
|
||||||
"""Create a generic volume group.
|
"""Create a generic volume group.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user