Merge "PowerMax Driver - Short host name and port group name override"

changes/06/711006/1
Zuul 2020-03-02 21:38:39 +00:00 committed by Gerrit Code Review
commit e1ace50684
14 changed files with 1130 additions and 185 deletions

View File

@ -44,6 +44,7 @@ class PowerMaxData(object):
port_group_name_f = 'OS-fibre-PG'
port_group_name_i = 'OS-iscsi-PG'
masking_view_name_f = 'OS-HostX-F-OS-fibre-PG-MV'
masking_view_name_Y_f = 'OS-HostY-F-OS-fibre-PG-MV'
masking_view_name_i = 'OS-HostX-SRP_1-I-OS-iscsi-PG-MV'
initiatorgroup_name_f = 'OS-HostX-F-IG'
initiatorgroup_name_i = 'OS-HostX-I-IG'
@ -277,7 +278,8 @@ class PowerMaxData(object):
'storagetype:storagegrouptags': u'good, comma, separated,list'}
vol_type_extra_specs_tags_bad = {
'storagetype:storagegrouptags': u'B&d, [list]'}
extra_specs_port_group_template = deepcopy(extra_specs)
extra_specs_port_group_template['port_group_template'] = 'portGroupName'
extra_specs_migrate = deepcopy(extra_specs)
extra_specs_migrate[utils.PORTGROUPNAME] = port_group_name_f
@ -401,7 +403,9 @@ class PowerMaxData(object):
'storagegroup_name': storagegroup_name_f,
'volume_name': test_volume.name,
'workload': workload,
'replication_enabled': False}
'replication_enabled': False,
'used_host_name': 'HostX',
'port_group_label': port_group_name_f}
masking_view_dict_no_slo = deepcopy(masking_view_dict)
masking_view_dict_no_slo.update(

View File

@ -323,6 +323,10 @@ class FakeConfiguration(object):
self.u4p_failover_timeout = value
elif key == 'u4p_primary':
self.u4p_primary = value
elif key == 'powermax_short_host_name_template':
self.powermax_short_host_name_template = value
elif key == 'powermax_port_group_name_template':
self.powermax_port_group_name_template = value
def safe_get(self, key):
try:

View File

@ -45,9 +45,11 @@ class PowerMaxCommonTest(test.TestCase):
self.mock_object(volume_utils, 'get_max_over_subscription_ratio',
return_value=1.0)
configuration = tpfo.FakeConfiguration(
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
emc_file=None, volume_backend_name='CommonTests', interval=1,
retries=1, san_ip='1.1.1.1', san_login='smc',
vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc',
san_api_port=8443, vmax_port_groups=[self.data.port_group_name_f])
san_api_port=8443, vmax_port_groups=[self.data.port_group_name_f],
powermax_port_group_name_template='portGroupName')
rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession())
driver = fc.PowerMaxFCDriver(configuration=configuration)
@ -72,7 +74,6 @@ class PowerMaxCommonTest(test.TestCase):
side_effect=[[], tpd.PowerMaxData.array_info_wl])
def test_gather_info_tests(self, mck_parse, mck_combo, mck_rest,
mck_nextgen, mck_ucode):
# Use-Case 1: Gather info no-opts
configuration = tpfo.FakeConfiguration(
None, 'config_group', None, None)
@ -83,6 +84,54 @@ class PowerMaxCommonTest(test.TestCase):
self.assertTrue(self.common.next_gen)
self.assertEqual(self.common.ucode_level, self.data.next_gen_ucode)
@mock.patch.object(common.PowerMaxCommon,
'_gather_info')
def test_get_attributes_from_config_short_host_template(
self, mock_gather):
configuration = tpfo.FakeConfiguration(
emc_file=None, volume_backend_name='config_group', interval='10',
retries='10', replication_device=None,
powermax_short_host_name_template='shortHostName')
driver = fc.PowerMaxFCDriver(configuration=configuration)
driver.common._get_attributes_from_config()
self.assertEqual(
'shortHostName', driver.common.powermax_short_host_name_template)
@mock.patch.object(common.PowerMaxCommon,
'_gather_info')
def test_get_attributes_from_config_no_short_host_template(
self, mock_gather):
configuration = tpfo.FakeConfiguration(
emc_file=None, volume_backend_name='config_group', interval='10',
retries='10', replication_device=None)
driver = fc.PowerMaxFCDriver(configuration=configuration)
driver.common._get_attributes_from_config()
self.assertIsNone(driver.common.powermax_short_host_name_template)
@mock.patch.object(common.PowerMaxCommon,
'_gather_info')
def test_get_attributes_from_config_port_group_template(
self, mock_gather):
configuration = tpfo.FakeConfiguration(
emc_file=None, volume_backend_name='config_group', interval='10',
retries='10', replication_device=None,
powermax_port_group_name_template='portGroupName')
driver = fc.PowerMaxFCDriver(configuration=configuration)
driver.common._get_attributes_from_config()
self.assertEqual(
'portGroupName', driver.common.powermax_port_group_name_template)
@mock.patch.object(common.PowerMaxCommon,
'_gather_info')
def test_get_attributes_from_config_no_port_group_template(
self, mock_gather):
configuration = tpfo.FakeConfiguration(
emc_file=None, volume_backend_name='config_group', interval='10',
retries='10', replication_device=None)
driver = fc.PowerMaxFCDriver(configuration=configuration)
driver.common._get_attributes_from_config()
self.assertIsNone(driver.common.powermax_port_group_name_template)
def test_get_slo_workload_combinations_powermax(self):
array_info = self.common.get_attributes_from_cinder_config()
finalarrayinfolist = self.common._get_slo_workload_combinations(
@ -267,7 +316,8 @@ class PowerMaxCommonTest(test.TestCase):
array, volume, device_id, extra_specs, self.data.connector, False)
mock_rm.assert_called_once_with(
array, volume, device_id, volume_name,
extra_specs, True, self.data.connector, async_grp=None)
extra_specs, True, self.data.connector, async_grp=None,
host_template=None)
@mock.patch.object(masking.PowerMaxMasking,
'return_volume_to_fast_managed_group')
@ -282,7 +332,8 @@ class PowerMaxCommonTest(test.TestCase):
array, volume, device_id, extra_specs, self.data.connector, True)
mock_rm.assert_called_once_with(
array, volume, device_id, volume_name,
extra_specs, False, self.data.connector, async_grp=None)
extra_specs, False, self.data.connector, async_grp=None,
host_template=None)
mock_return.assert_called_once()
def test_unmap_lun(self):
@ -296,7 +347,18 @@ class PowerMaxCommonTest(test.TestCase):
self.common._unmap_lun(volume, connector)
mock_remove.assert_called_once_with(
array, volume, device_id, extra_specs,
connector, False, async_grp=None)
connector, False, async_grp=None, host_template=None)
def test_unmap_lun_force(self):
volume = self.data.test_volume
extra_specs = deepcopy(self.data.extra_specs_intervals_set)
extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
connector = deepcopy(self.data.connector)
del connector['host']
with mock.patch.object(
self.common.utils, 'get_host_short_name') as mock_host:
self.common._unmap_lun(volume, connector)
mock_host.assert_not_called()
@mock.patch.object(common.PowerMaxCommon, '_remove_members')
def test_unmap_lun_attachments(self, mock_rm):
@ -327,7 +389,7 @@ class PowerMaxCommonTest(test.TestCase):
self.common._unmap_lun(volume, connector)
mock_remove.assert_called_once_with(
array, volume, device_id, extra_specs,
connector, False, async_grp=None)
connector, False, async_grp=None, host_template=None)
def test_unmap_lun_not_mapped(self):
volume = self.data.test_volume
@ -350,7 +412,7 @@ class PowerMaxCommonTest(test.TestCase):
self.common._unmap_lun(volume, None)
mock_remove.assert_called_once_with(
array, volume, device_id, extra_specs, None,
False, async_grp=None)
False, async_grp=None, host_template=None)
def test_initialize_connection_already_mapped(self):
volume = self.data.test_volume
@ -613,7 +675,9 @@ class PowerMaxCommonTest(test.TestCase):
@mock.patch.object(
common.PowerMaxCommon, '_get_masking_views_from_volume',
return_value=([], [tpd.PowerMaxData.masking_view_name_f]))
return_value=([tpd.PowerMaxData.masking_view_name_f],
[tpd.PowerMaxData.masking_view_name_f,
tpd.PowerMaxData.masking_view_name_Y_f]))
def test_find_host_lun_id_multiattach(self, mock_mask):
volume = self.data.test_volume
extra_specs = self.data.extra_specs
@ -631,6 +695,27 @@ class PowerMaxCommonTest(test.TestCase):
self.data.extra_specs, self.data.rep_extra_specs)
mock_tgt.assert_called_once()
@mock.patch.object(rest.PowerMaxRest, 'find_mv_connections_for_vol',
return_value='1')
@mock.patch.object(common.PowerMaxCommon, '_get_masking_views_from_volume',
side_effect=[([], ['OS-HostX-I-PG-MV']),
(['OS-HostX-I-PG-MV'],
['OS-HostX-I-PG-MV'])])
@mock.patch.object(rest.PowerMaxRest, 'get_volume',
return_value=tpd.PowerMaxData.volume_details[0])
def test_find_host_lun_id_backward_compatible(
self, mock_vol, mock_mvs, mock_mv_conns):
expected_dict = {'hostlunid': '1', 'maskingview': 'OS-HostX-I-PG-MV',
'array': '000197800123', 'device_id': '00001'}
self.common.powermax_short_host_name_template = (
'shortHostName[:7]finance')
masked_vols, is_multiattach = self.common.find_host_lun_id(
self.data.test_volume, 'HostX',
self.data.extra_specs)
self.assertEqual(expected_dict, masked_vols)
self.assertFalse(is_multiattach)
mock_mv_conns.assert_called_once()
def test_get_masking_views_from_volume(self):
array = self.data.array
device_id = self.data.device_id
@ -692,6 +777,7 @@ class PowerMaxCommonTest(test.TestCase):
extra_specs[utils.WORKLOAD] = self.data.workload
ref_mv_dict = self.data.masking_view_dict
self.common.next_gen = False
self.common.powermax_port_group_name_template = 'portGroupName'
masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs)
self.assertEqual(ref_mv_dict, masking_view_dict)
@ -1142,6 +1228,31 @@ class PowerMaxCommonTest(test.TestCase):
self.data.test_volume, self.data.connector)
self.assertEqual([self.data.wwnn1], metro_wwns)
@mock.patch.object(common.PowerMaxCommon,
'_get_target_wwns_from_masking_view')
@mock.patch.object(utils.PowerMaxUtils, 'get_host_name_label',
return_value = 'my_short_h94485')
@mock.patch.object(utils.PowerMaxUtils, 'is_replication_enabled',
return_value=False)
def test_get_target_wwns_host_override(
self, mock_rep_check, mock_label, mock_mv):
host_record = {'host': 'my_short_host_name'}
connector = deepcopy(self.data.connector)
connector.update(host_record)
extra_specs = {'pool_name': 'Diamond+DSS+SRP_1+000197800123',
'srp': 'SRP_1', 'array': '000197800123',
'storagetype:portgroupname': 'OS-fibre-PG',
'interval': 1, 'retries': 1, 'slo': 'Diamond',
'workload': 'DSS'}
host_template = 'shortHostName[:10]uuid[:5]'
self.common.powermax_short_host_name_template = host_template
self.common.get_target_wwns_from_masking_view(
self.data.test_volume, connector)
mock_label.assert_called_once_with(
connector['host'], host_template)
mock_mv.assert_called_once_with(
self.data.device_id, 'my_short_h94485', extra_specs)
def test_get_port_group_from_masking_view(self):
array = self.data.array
maskingview_name = self.data.masking_view_name_f
@ -1464,7 +1575,7 @@ class PowerMaxCommonTest(test.TestCase):
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
return_value=tpd.PowerMaxData.sg_details[1])
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
return_value=('OS-Test-SG', '', '', ''))
return_value=('OS-Test-SG', '', ''))
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
return_value=True)
@mock.patch.object(masking.PowerMaxMasking,
@ -1500,7 +1611,7 @@ class PowerMaxCommonTest(test.TestCase):
rest.PowerMaxRest, 'get_volume',
return_value=tpd.PowerMaxData.volume_details_attached)
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
return_value=('OS-Test-SG', '', '', ''))
return_value=('OS-Test-SG', '', ''))
@mock.patch.object(provision.PowerMaxProvision, 'create_storage_group')
@mock.patch.object(masking.PowerMaxMasking, 'add_child_sg_to_parent_sg')
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
@ -1544,7 +1655,7 @@ class PowerMaxCommonTest(test.TestCase):
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
side_effect=[tpd.PowerMaxData.sg_details[1], None])
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
return_value=('OS-Test-SG', '', '', ''))
return_value=('OS-Test-SG', '', ''))
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
return_value=False)
@mock.patch.object(masking.PowerMaxMasking,

View File

@ -148,6 +148,19 @@ class PowerMaxFCTest(test.TestCase):
self.data.test_volume, self.data.connector)
self.assertEqual({}, zoning_mappings)
@mock.patch.object(
common.PowerMaxCommon, 'get_masking_views_from_volume',
side_effect = ([(None, False),
([tpd.PowerMaxData.masking_view_name_f], False)]))
def test_get_zoning_mappings_retry_backward_compatibility(
self, mock_views):
with mock.patch.object(self.common.utils, 'get_host_name_label',
return_value=None) as mock_label:
self.driver._get_zoning_mappings(
self.data.test_volume, self.data.connector)
self.assertEqual(2, mock_label.call_count)
self.assertEqual(2, mock_views.call_count)
@mock.patch.object(
common.PowerMaxCommon, 'get_masking_views_from_volume',
return_value=([tpd.PowerMaxData.masking_view_name_f], True))

View File

@ -833,7 +833,7 @@ class PowerMaxMaskingTest(test.TestCase):
self.data.masking_view_name_f]
with mock.patch.object(
rest.PowerMaxRest, 'get_masking_views_by_initiator_group',
side_effect=[mv_list, []]):
side_effect=[mv_list, mv_list, [], []]):
self.mask._last_volume_delete_initiator_group(
self.data.array, self.data.initiatorgroup_name_i,
self.data.connector['host'])
@ -974,7 +974,8 @@ class PowerMaxMaskingTest(test.TestCase):
def test_pre_multiattach(self, mock_return):
mv_dict = self.mask.pre_multiattach(
self.data.array, self.data.device_id,
self.data.masking_view_dict_multiattach, self.data.extra_specs)
self.data.masking_view_dict_multiattach,
self.data.extra_specs)
mock_return.assert_not_called()
self.assertEqual(self.data.storagegroup_name_f,
mv_dict[utils.FAST_SG])
@ -993,7 +994,8 @@ class PowerMaxMaskingTest(test.TestCase):
return_value='DiamondDSS'):
self.mask.pre_multiattach(
self.data.array, self.data.device_id,
self.data.masking_view_dict_multiattach, self.data.extra_specs)
self.data.masking_view_dict_multiattach,
self.data.extra_specs)
utils.PowerMaxUtils.truncate_string.assert_called_once_with(
'DiamondDSS', 10)
@ -1007,7 +1009,8 @@ class PowerMaxMaskingTest(test.TestCase):
self, mock_return, mock_sg):
for x in range(0, 2):
self.mask.return_volume_to_fast_managed_group(
self.data.array, self.data.device_id, self.data.extra_specs)
self.data.array, self.data.device_id,
self.data.extra_specs)
no_slo_specs = deepcopy(self.data.extra_specs)
no_slo_specs[utils.SLO] = None
self.mask.return_volume_to_fast_managed_group(
@ -1093,3 +1096,44 @@ class PowerMaxMaskingTest(test.TestCase):
self.data.array, self.data.add_volume_sg_info_dict,
self.data.extra_specs_tags)
mock_except.assert_called()
@mock.patch.object(rest.PowerMaxRest,
'get_masking_views_from_storage_group',
return_value=[tpd.PowerMaxData.masking_view_name_f])
def test_get_host_and_port_group_labels(self, mock_mv):
host_label, port_group_label = (
self.mask._get_host_and_port_group_labels(
self.data.array, self.data.parent_sg_f))
self.assertEqual('HostX', host_label)
self.assertEqual('OS-fibre-PG', port_group_label)
@mock.patch.object(rest.PowerMaxRest,
'get_masking_views_from_storage_group',
return_value=['OS-HostX699ea-I-p-name3b02c-MV'])
def test_get_host_and_port_group_labels_complex(self, mock_mv):
host_label, port_group_label = (
self.mask._get_host_and_port_group_labels(
self.data.array, self.data.parent_sg_f))
self.assertEqual('HostX699ea', host_label)
self.assertEqual('p-name3b02c', port_group_label)
@mock.patch.object(rest.PowerMaxRest,
'get_masking_views_from_storage_group',
return_value=['OS-myhost-I-myportgroup-MV'])
def test_get_host_and_port_group_labels_plain(self, mock_mv):
host_label, port_group_label = (
self.mask._get_host_and_port_group_labels(
self.data.array, self.data.parent_sg_f))
self.assertEqual('myhost', host_label)
self.assertEqual('myportgroup', port_group_label)
@mock.patch.object(rest.PowerMaxRest,
'get_masking_views_from_storage_group',
return_value=[
'OS-host-with-dash-I-portgroup-with-dashes-MV'])
def test_get_host_and_port_group_labels_dashes(self, mock_mv):
host_label, port_group_label = (
self.mask._get_host_and_port_group_labels(
self.data.array, self.data.parent_sg_f))
self.assertEqual('host-with-dash', host_label)
self.assertEqual('portgroup-with-dashes', port_group_label)

View File

@ -51,18 +51,18 @@ class PowerMaxReplicationTest(test.TestCase):
'allow_extend': 'True'}
volume_utils.get_max_over_subscription_ratio = mock.Mock()
configuration = tpfo.FakeConfiguration(
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
san_password='smc', san_api_port=8443,
None, 'CommonReplicationTests', interval=1, retries=1,
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
vmax_port_groups=[self.data.port_group_name_f],
replication_device=self.replication_device)
rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession())
driver = fc.PowerMaxFCDriver(configuration=configuration)
iscsi_config = tpfo.FakeConfiguration(
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
san_password='smc', san_api_port=8443,
None, 'CommonReplicationTests', interval=1, retries=1,
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
vmax_port_groups=[self.data.port_group_name_i],
replication_device=self.replication_device)
iscsi_driver = iscsi.PowerMaxISCSIDriver(configuration=iscsi_config)
@ -87,9 +87,9 @@ class PowerMaxReplicationTest(test.TestCase):
'rdf_group_label': self.data.rdf_group_name,
'allow_extend': 'True', 'mode': 'async'}
async_configuration = tpfo.FakeConfiguration(
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
san_password='smc', san_api_port=8443,
None, 'CommonReplicationTests', interval=1, retries=1,
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
vmax_port_groups=[self.data.port_group_name_f],
replication_device=self.async_rep_device)
self.async_driver = fc.PowerMaxFCDriver(
@ -101,9 +101,9 @@ class PowerMaxReplicationTest(test.TestCase):
'rdf_group_label': self.data.rdf_group_name,
'allow_extend': 'True', 'mode': 'metro'}
metro_configuration = tpfo.FakeConfiguration(
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
san_password='smc', san_api_port=8443,
None, 'CommonReplicationTests', interval=1, retries=1,
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
vmax_port_groups=[self.data.port_group_name_f],
replication_device=self.metro_rep_device)
self.metro_driver = fc.PowerMaxFCDriver(

View File

@ -450,6 +450,7 @@ class PowerMaxUtilsTest(test.TestCase):
def test_get_child_sg_name(self):
host_name = 'HostX'
port_group_label = self.data.port_group_name_f
# Slo and rep enabled
extra_specs1 = {
'pool_name': u'Diamond+DSS+SRP_1+000197800123',
@ -463,23 +464,24 @@ class PowerMaxUtilsTest(test.TestCase):
'rep_mode': 'Synchronous',
utils.PORTGROUPNAME: self.data.port_group_name_f}
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
self.utils.get_child_sg_name(host_name, extra_specs1))
child_sg_name, do_disable_compression, rep_enabled = (
self.utils.get_child_sg_name(
host_name, extra_specs1, port_group_label))
re_name = self.data.storagegroup_name_f + '-RE'
self.assertEqual(re_name, child_sg_name)
# Disable compression
extra_specs2 = deepcopy(self.data.extra_specs_disable_compression)
extra_specs2[utils.PORTGROUPNAME] = self.data.port_group_name_f
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
self.utils.get_child_sg_name(host_name, extra_specs2))
child_sg_name, do_disable_compression, rep_enabled = (
self.utils.get_child_sg_name(
host_name, extra_specs2, port_group_label))
cd_name = self.data.storagegroup_name_f + '-CD'
self.assertEqual(cd_name, child_sg_name)
# No slo
extra_specs3 = deepcopy(self.data.extra_specs)
extra_specs3[utils.SLO] = None
extra_specs3[utils.PORTGROUPNAME] = self.data.port_group_name_f
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
self.utils.get_child_sg_name(host_name, extra_specs3))
child_sg_name, do_disable_compression, rep_enabled = (
self.utils.get_child_sg_name(
host_name, extra_specs3, port_group_label))
self.assertEqual(self.data.no_slo_sg_name, child_sg_name)
def test_change_multiattach(self):
@ -732,3 +734,285 @@ class PowerMaxUtilsTest(test.TestCase):
input_list = 'one,two,three'
output_string = self.utils.convert_list_to_string(input_list)
self.assertEqual('one,two,three', output_string)
def test_regex_check_case_2(self):
test_template = 'shortHostName[:10]uuid[:5]'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertTrue(is_ok)
self.assertEqual('2', case)
def test_regex_check_case_3(self):
test_template = 'shortHostName[-10:]uuid[:5]'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertTrue(is_ok)
self.assertEqual('3', case)
def test_regex_check_case_4(self):
test_template = 'shortHostName[:7]finance'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertTrue(is_ok)
self.assertEqual('4', case)
def test_regex_check_case_5(self):
test_template = 'shortHostName[-6:]production'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertTrue(is_ok)
self.assertEqual('5', case)
def test_regex_check_case_2_misspelt(self):
test_template = 'shortHstName[:10]uuid[:5]'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_3_misspelt(self):
test_template = 'shortHostName[-10:]uud[:5]'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_4_misspelt(self):
test_template = 'shortHotName[:7]finance'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_5_misspelt(self):
test_template = 'shortHstName[-6:]production'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_4_invalid_chars(self):
test_template = 'shortHostName[:7]f*n&nce'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_5_invalid_chars(self):
test_template = 'shortHostName[-6:]pr*ducti*n'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_2_missing_square_bracket(self):
test_template = 'shortHostName[:10uuid[:5]'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_regex_check_case_4_missing_square_bracket(self):
test_template = 'shortHostName[:10finance'
is_ok, case = self.utils.regex_check(test_template, True)
self.assertFalse(is_ok)
self.assertEqual('0', case)
def test_prepare_string_entity_case_2(self):
test_template = 'shortHostName[:10]uuid[:5]'
altered_string = self.utils.prepare_string_entity(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[:10]uuid[:5]',
altered_string)
def test_prepare_string_entity_case_3(self):
test_template = 'shortHostName[-10:]uuid[:5]'
altered_string = self.utils.prepare_string_entity(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[-10:]uuid[:5]',
altered_string)
def test_prepare_string_entity_case_4(self):
test_template = 'shortHostName[:7]finance'
altered_string = self.utils.prepare_string_entity(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[:7]finance',
altered_string)
def test_prepare_string_entity_case_5(self):
test_template = 'shortHostName[-6:]production'
altered_string = self.utils.prepare_string_entity(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[-6:]production',
altered_string)
def test_prepare_string_with_uuid_case_2(self):
test_template = 'shortHostName[:10]uuid[:5]'
pass_two, uuid = self.utils.prepare_string_with_uuid(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[:10]944854dce45898b544a1cb9071d3cc35[:5]',
pass_two)
self.assertEqual('944854dce45898b544a1cb9071d3cc35', uuid)
def test_prepare_string_with_uuid_case_3(self):
test_template = 'shortHostName[-10:]uuid[:5]'
pass_two, uuid = self.utils.prepare_string_with_uuid(
test_template, 'my_short_host_name', True)
self.assertEqual(
'my_short_host_name[-10:]944854dce45898b544a1cb9071d3cc35[:5]',
pass_two)
self.assertEqual('944854dce45898b544a1cb9071d3cc35', uuid)
def test_check_upper_limit_short_host(self):
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.check_upper_limit,
12, 12, True)
def test_check_upper_limit_short_host_case_4(self):
user_define_name = 'Little_too_long'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.check_upper_limit,
12, len(user_define_name), True)
def test_validate_short_host_name_from_template_case_1(self):
test_template = 'shortHostName'
short_host_name = 'my_short_host'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('my_short_host', result_string)
def test_validate_short_host_name_from_template_case_1_exceeds_16char(
self):
test_template = 'shortHostName'
short_host_name = 'my_short_host_greater_than_16chars'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('6chars0bc43f914e', result_string)
def test_validate_short_host_name_from_template_case_1_template_misspelt(
self):
test_template = 'shortHstName'
short_host_name = 'my_short_host'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.validate_short_host_name_from_template,
test_template, short_host_name)
def test_validate_short_host_name_from_template_case_2(self):
test_template = 'shortHostName[:10]uuid[:5]'
short_host_name = 'my_short_host_name'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('my_short_h94485', result_string)
def test_validate_short_host_name_from_template_case_2_shorter_than(self):
test_template = 'shortHostName[:10]uuid[:5]'
short_host_name = 'HostX'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('HostX699ea', result_string)
def test_validate_short_host_name_from_template_case_3(self):
test_template = 'shortHostName[-10:]uuid[:5]'
short_host_name = 'my_short_host_name'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('_host_name94485', result_string)
def test_validate_short_host_name_from_template_case_3_shorter_than(self):
test_template = 'shortHostName[-10:]uuid[:5]'
short_host_name = 'HostX'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('HostX699ea', result_string)
def test_validate_short_host_name_from_template_case_4(self):
test_template = 'shortHostName[:7]finance'
short_host_name = 'my_short_host_name'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('my_shorfinance', result_string)
def test_validate_short_host_name_from_template_case_5(self):
test_template = 'shortHostName[-6:]production'
short_host_name = 'my_short_host_name'
result_string = self.utils.validate_short_host_name_from_template(
test_template, short_host_name)
self.assertEqual('t_nameproduction', result_string)
def test_validate_short_host_name_exception_missing_minus(self):
test_template = 'shortHostName[6:]production'
short_host_name = 'my_short_host_name'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.validate_short_host_name_from_template,
test_template, short_host_name)
def test_validate_port_group_from_template_case_1(self):
test_template = 'portGroupName'
port_group_name = 'my_pg'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('my_pg', result_string)
def test_validate_port_group_from_template_case_1_long(self):
test_template = 'portGroupName'
port_group_name = 'my_port_group_name'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('p_name5ba163', result_string)
def test_validate_port_group_from_template_case_1_misspelt(self):
test_template = 'portGr*upName'
port_group_name = 'my_port_group_name'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.validate_port_group_name_from_template,
test_template, port_group_name)
def test_validate_port_group_from_template_case_2(self):
test_template = 'portGroupName[:6]uuid[:5]'
port_group_name = 'my_port_group_name'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('my_por3b02c', result_string)
def test_validate_port_group_from_template_case_3(self):
test_template = 'portGroupName[-6:]uuid[:5]'
port_group_name = 'my_port_group_name'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('p_name3b02c', result_string)
def test_validate_port_group_from_template_case_4(self):
test_template = 'portGroupName[:6]test'
port_group_name = 'my_port_group_name'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('my_portest', result_string)
def test_validate_port_group_from_template_case_5(self):
test_template = 'portGroupName[-7:]test'
port_group_name = 'my_port_group_name'
result_string = self.utils.validate_port_group_name_from_template(
test_template, port_group_name)
self.assertEqual('up_nametest', result_string)
def test_validate_port_group_name_exception_missing_minus(self):
test_template = 'portGroupName[6:]test'
port_group_name = 'my_port_group_name'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.validate_port_group_name_from_template,
test_template, port_group_name)
def test_validate_port_group_name_exception_chars_exceeded(self):
test_template = 'portGroupName[:10]test'
port_group_name = 'my_port_group_name'
self.assertRaises(exception.VolumeBackendAPIException,
self.utils.validate_port_group_name_from_template,
test_template, port_group_name)
def test_get_port_name_label_default(self):
port_name_in = 'my_port_group_name'
port_group_template = 'portGroupName'
port_name_out = self.utils.get_port_name_label(
port_name_in, port_group_template)
self.assertEqual('p_name5ba163', port_name_out)
def test_get_port_name_label_template(self):
port_name_in = 'my_port_group_name'
port_group_template = 'portGroupName[-6:]uuid[:5]'
port_name_out = self.utils.get_port_name_label(
port_name_in, port_group_template)
self.assertEqual('p_name3b02c', port_name_out)

View File

@ -140,7 +140,13 @@ powermax_opts = [
'configured prior for server connection.'),
cfg.ListOpt(utils.POWERMAX_ARRAY_TAG_LIST,
bounds=True,
help='List of user assigned name for storage array.')]
help='List of user assigned name for storage array.'),
cfg.StrOpt(utils.POWERMAX_SHORT_HOST_NAME_TEMPLATE,
default='shortHostName',
help='User defined override for short host name.'),
cfg.StrOpt(utils.POWERMAX_PORT_GROUP_NAME_TEMPLATE,
default='portGroupName',
help='User defined override for port group name.')]
CONF.register_opts(powermax_opts, group=configuration.SHARED_CONF_GROUP)
@ -185,6 +191,8 @@ class PowerMaxCommon(object):
self.rep_devices = []
self.failover = False
self.powermax_array_tag_list = None
self.powermax_short_host_name_template = None
self.powermax_port_group_name_template = None
# Gather environment info
self._get_replication_info()
@ -220,6 +228,10 @@ class PowerMaxCommon(object):
utils.POWERMAX_SNAPVX_UNLINK_LIMIT)
self.powermax_array_tag_list = self.configuration.safe_get(
utils.POWERMAX_ARRAY_TAG_LIST)
self.powermax_short_host_name_template = self.configuration.safe_get(
utils.POWERMAX_SHORT_HOST_NAME_TEMPLATE)
self.powermax_port_group_name_template = self.configuration.safe_get(
utils.POWERMAX_PORT_GROUP_NAME_TEMPLATE)
self.pool_info['backend_name'] = (
self.configuration.safe_get('volume_backend_name'))
mosr = volume_utils.get_max_over_subscription_ratio(
@ -673,7 +685,7 @@ class PowerMaxCommon(object):
def _remove_members(self, array, volume, device_id,
extra_specs, connector, is_multiattach,
async_grp=None):
async_grp=None, host_template=None):
"""This method unmaps a volume from a host.
Removes volume from the storage group that belongs to a masking view.
@ -690,7 +702,8 @@ class PowerMaxCommon(object):
reset = False if is_multiattach else True
self.masking.remove_and_reset_members(
array, volume, device_id, volume_name,
extra_specs, reset, connector, async_grp=async_grp)
extra_specs, reset, connector, async_grp=async_grp,
host_template=host_template)
if is_multiattach:
self.masking.return_volume_to_fast_managed_group(
array, device_id, extra_specs)
@ -713,7 +726,7 @@ class PowerMaxCommon(object):
async_grp = None
LOG.info("Unmap volume: %(volume)s.", {'volume': volume})
if connector is not None:
host = self.utils.get_host_short_name(connector['host'])
host_name = connector.get('host')
attachment_list = volume.volume_attachment
LOG.debug("Volume attachment list: %(atl)s. "
"Attachment type: %(at)s",
@ -725,7 +738,7 @@ class PowerMaxCommon(object):
if att_list is not None:
host_list = [att.connector['host'] for att in att_list if
att is not None and att.connector is not None]
current_host_occurances = host_list.count(host)
current_host_occurances = host_list.count(host_name)
if current_host_occurances > 1:
LOG.info("Volume is attached to multiple instances on "
"this host. Not removing the volume from the "
@ -734,10 +747,10 @@ class PowerMaxCommon(object):
else:
LOG.warning("Cannot get host name from connector object - "
"assuming force-detach.")
host = None
host_name = None
device_info, is_multiattach = (
self.find_host_lun_id(volume, host, extra_specs))
self.find_host_lun_id(volume, host_name, extra_specs))
if 'hostlunid' not in device_info:
LOG.info("Volume %s is not mapped. No volume to unmap.",
volume_name)
@ -746,23 +759,25 @@ class PowerMaxCommon(object):
if self.utils.does_vol_need_rdf_management_group(extra_specs):
async_grp = self.utils.get_async_rdf_managed_grp_name(
self.rep_config)
self._remove_members(array, volume, device_info['device_id'],
extra_specs, connector, is_multiattach,
async_grp=async_grp)
self._remove_members(
array, volume, device_info['device_id'], extra_specs, connector,
is_multiattach, async_grp=async_grp,
host_template=self.powermax_short_host_name_template)
if self.utils.is_metro_device(self.rep_config, extra_specs):
# Need to remove from remote masking view
device_info, __ = (self.find_host_lun_id(
volume, host, extra_specs, rep_extra_specs))
volume, host_name, extra_specs, rep_extra_specs))
if 'hostlunid' in device_info:
self._remove_members(
rep_extra_specs[utils.ARRAY], volume,
device_info['device_id'], rep_extra_specs, connector,
is_multiattach, async_grp=async_grp)
is_multiattach, async_grp=async_grp,
host_template=self.powermax_short_host_name_template)
else:
# Make an attempt to clean up initiator group
self.masking.attempt_ig_cleanup(
connector, self.protocol, rep_extra_specs[utils.ARRAY],
True)
True, host_template=self.powermax_short_host_name_template)
if is_multiattach and LOG.isEnabledFor(logging.DEBUG):
mv_list, sg_list = (
self._get_mvs_and_sgs_from_volume(
@ -817,7 +832,7 @@ class PowerMaxCommon(object):
if self.utils.is_volume_failed_over(volume):
extra_specs = rep_extra_specs
device_info_dict, is_multiattach = (
self.find_host_lun_id(volume, connector['host'], extra_specs))
self.find_host_lun_id(volume, connector.get('host'), extra_specs))
masking_view_dict = self._populate_masking_dict(
volume, connector, extra_specs)
masking_view_dict[utils.IS_MULTIATTACH] = is_multiattach
@ -843,7 +858,7 @@ class PowerMaxCommon(object):
device_info_dict['maskingview']))
if self.utils.is_metro_device(self.rep_config, extra_specs):
remote_info_dict, is_multiattach = (
self.find_host_lun_id(volume, connector['host'],
self.find_host_lun_id(volume, connector.get('host'),
extra_specs, rep_extra_specs))
if remote_info_dict.get('hostlunid') is None:
# Need to attach on remote side
@ -987,7 +1002,7 @@ class PowerMaxCommon(object):
# Find host lun id again after the volume is exported to the host.
device_info_dict, __ = self.find_host_lun_id(
volume, connector['host'], extra_specs, rep_extra_specs)
volume, connector.get('host'), extra_specs, rep_extra_specs)
if 'hostlunid' not in device_info_dict:
# Did not successfully attach to host, so a rollback is required.
error_message = (_("Error Attaching volume %(vol)s. Cannot "
@ -1499,13 +1514,23 @@ class PowerMaxCommon(object):
device_id = self.get_remote_target_device(
extra_specs[utils.ARRAY], volume, device_id)[0]
extra_specs = rep_extra_specs
host_name = self.utils.get_host_short_name(host) if host else None
host_name = self.utils.get_host_name_label(
host, self.powermax_short_host_name_template) if host else None
if device_id:
array = extra_specs[utils.ARRAY]
# Return only masking views for this host
host_maskingviews, all_masking_view_list = (
self._get_masking_views_from_volume(
array, device_id, host_name))
if not host_maskingviews:
# Backward compatibility if a new template was added to
# an existing backend.
host_name = self.utils.get_host_short_name(
host) if host else None
host_maskingviews, all_masking_view_list = (
self._get_masking_views_from_volume_for_host(
all_masking_view_list, host_name))
for maskingview in host_maskingviews:
host_lun_id = self.rest.find_mv_connections_for_vol(
@ -1516,6 +1541,7 @@ class PowerMaxCommon(object):
'array': array,
'device_id': device_id}
maskedvols = devicedict
if not maskedvols:
LOG.debug(
"Host lun id not found for volume: %(volume_name)s "
@ -1573,17 +1599,28 @@ class PowerMaxCommon(object):
:returns: masking view list, all masking view list
"""
LOG.debug("Getting masking views from volume")
host_maskingview_list, all_masking_view_list = [], []
host_compare = True if host else False
mvs, __ = self._get_mvs_and_sgs_from_volume(array, device_id)
for mv in mvs:
all_masking_view_list.append(mv)
if host_compare:
if host.lower() in mv.lower():
host_maskingview_list.append(mv)
maskingview_list = (host_maskingview_list if host_compare else
all_masking_view_list)
return maskingview_list, all_masking_view_list
return self._get_masking_views_from_volume_for_host(mvs, host)
def _get_masking_views_from_volume_for_host(
self, masking_views, host_name):
"""Check all masking views for host_name
:param masking_views: list of masking view
:param host_name: the host name for comparision
:returns: masking view list, all masking view list
"""
LOG.debug("Getting masking views from volume for host %(host)s ",
{'host': host_name})
host_masking_view_list, all_masking_view_list = [], []
for masking_view in masking_views:
all_masking_view_list.append(masking_view)
if host_name:
if host_name.lower() in masking_view.lower():
host_masking_view_list.append(masking_view)
host_masking_view_list = (host_masking_view_list if host_name else
all_masking_view_list)
return host_masking_view_list, all_masking_view_list
def _get_mvs_and_sgs_from_volume(self, array, device_id):
"""Helper function to retrieve masking views and storage groups.
@ -1664,7 +1701,10 @@ class PowerMaxCommon(object):
raise exception.VolumeBackendAPIException(exception_message)
protocol = self.utils.get_short_protocol_type(self.protocol)
short_host_name = self.utils.get_host_short_name(connector['host'])
short_host_name = self.utils.get_host_name_label(
connector['host'], self.powermax_short_host_name_template)
masking_view_dict[utils.USED_HOST_NAME] = short_host_name
masking_view_dict[utils.SLO] = extra_specs[utils.SLO]
masking_view_dict[utils.WORKLOAD] = 'NONE' if self.next_gen else (
extra_specs[utils.WORKLOAD])
@ -1675,19 +1715,27 @@ class PowerMaxCommon(object):
"in cinder.conf or as an extra spec. Port group "
"cannot be left empty as creating a new masking "
"view will fail.")
masking_view_dict[utils.PORT_GROUP_LABEL] = (
self.utils.get_port_name_label(
extra_specs[utils.PORTGROUPNAME],
self.powermax_port_group_name_template))
masking_view_dict[utils.PORTGROUPNAME] = (
extra_specs[utils.PORTGROUPNAME])
masking_view_dict[utils.INITIATOR_CHECK] = (
self._get_initiator_check_flag())
child_sg_name, do_disable_compression, rep_enabled, short_pg_name = (
self.utils.get_child_sg_name(short_host_name, extra_specs))
child_sg_name, do_disable_compression, rep_enabled = (
self.utils.get_child_sg_name(
short_host_name, extra_specs,
masking_view_dict[utils.PORT_GROUP_LABEL]))
masking_view_dict[utils.DISABLECOMPRESSION] = do_disable_compression
masking_view_dict[utils.IS_RE] = rep_enabled
mv_prefix = (
"OS-%(shortHostName)s-%(protocol)s-%(pg)s"
% {'shortHostName': short_host_name,
'protocol': protocol, 'pg': short_pg_name})
'protocol': protocol,
'pg': masking_view_dict[utils.PORT_GROUP_LABEL]})
masking_view_dict[utils.SG_NAME] = child_sg_name
@ -2159,7 +2207,8 @@ class PowerMaxCommon(object):
"""
metro_wwns = []
host = connector['host']
short_host_name = self.utils.get_host_short_name(host)
short_host_name = self.utils.get_host_name_label(
host, self.powermax_short_host_name_template) if host else None
extra_specs = self._initial_setup(volume)
rep_extra_specs = self._get_replication_extra_specs(
extra_specs, self.rep_config)
@ -3443,8 +3492,12 @@ class PowerMaxCommon(object):
{'volume_name': device_id})
return False, None
target_sg_name, __, __, __ = self.utils.get_child_sg_name(
attached_host, target_extra_specs)
port_group_label = self.utils.get_port_name_label(
target_extra_specs[utils.PORTGROUPNAME],
self.powermax_port_group_name_template)
target_sg_name, __, __ = self.utils.get_child_sg_name(
attached_host, target_extra_specs, port_group_label)
target_sg = self.rest.get_storage_group(array, target_sg_name)
if not target_sg:

View File

@ -117,6 +117,8 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
- Volume/Snapshot backed metadata inclusion
- Debug metadata compression and service level info fix
4.2.0 - Support of Unisphere storage group and array tags
- User defined override for short host name and port group name
(bp powermax-user-defined-hostname-portgroup)
"""
VERSION = "4.2.0"
@ -332,7 +334,8 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
"""
loc = volume.provider_location
name = ast.literal_eval(loc)
host = self.common.utils.get_host_short_name(connector['host'])
host_label = self.common.utils.get_host_name_label(
connector['host'], self.common.powermax_short_host_name_template)
zoning_mappings = {}
try:
array = name['array']
@ -345,7 +348,14 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
masking_views, is_metro = (
self.common.get_masking_views_from_volume(
array, volume, device_id, host))
array, volume, device_id, host_label))
if not masking_views:
# Backward compatibility with pre Ussuri short host name.
host_label = self.common.utils.get_host_short_name(
connector['host'])
masking_views, is_metro = (
self.common.get_masking_views_from_volume(
array, volume, device_id, host_label))
if masking_views:
portgroup = (
self.common.get_port_group_from_masking_view(
@ -379,7 +389,7 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
else:
masking_views, __ = (
self.common.get_masking_views_from_volume(
metro_array, volume, metro_device_id, host))
metro_array, volume, metro_device_id, host_label))
if masking_views:
metro_portgroup = (
self.common.get_port_group_from_masking_view(

View File

@ -122,6 +122,8 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
- Volume/Snapshot backed metadata inclusion
- Debug metadata compression and service level info fix
4.2.0 - Support of Unisphere storage group and array tags
- User defined override for short host name and port group name
(bp powermax-user-defined-hostname-portgroup)
"""
VERSION = "4.2.0"

View File

@ -240,8 +240,10 @@ class PowerMaxMasking(object):
storagegroup_name = masking_view_dict[utils.SG_NAME]
connector = masking_view_dict[utils.CONNECTOR]
port_group_name = masking_view_dict[utils.PORTGROUPNAME]
LOG.info("Port Group in masking view operation: %(port_group_name)s.",
{'port_group_name': port_group_name})
LOG.info("Port Group in masking view operation: %(pg_name)s. "
"The port group labels is %(pg_label)s.",
{'pg_name': masking_view_dict[utils.PORTGROUPNAME],
'pg_label': masking_view_dict[utils.PORT_GROUP_LABEL]})
init_group_name, error_message = (self._get_or_create_initiator_group(
serial_number, init_group_name, connector, extra_specs))
@ -1082,7 +1084,8 @@ class PowerMaxMasking(object):
@coordination.synchronized("emc-vol-{device_id}")
def remove_and_reset_members(
self, serial_number, volume, device_id, volume_name,
extra_specs, reset=True, connector=None, async_grp=None):
extra_specs, reset=True, connector=None, async_grp=None,
host_template=None):
"""This is called on a delete, unmap device or rollback.
:param serial_number: the array serial number
@ -1093,14 +1096,16 @@ class PowerMaxMasking(object):
:param reset: reset, return to original SG (optional)
:param connector: the connector object (optional)
:param async_grp: the async rep group (optional)
:param host_template: the host template (optional)
"""
self._cleanup_deletion(
serial_number, volume, device_id, volume_name,
extra_specs, connector, reset, async_grp)
extra_specs, connector, reset, async_grp,
host_template=host_template)
def _cleanup_deletion(
self, serial_number, volume, device_id, volume_name,
extra_specs, connector, reset, async_grp):
extra_specs, connector, reset, async_grp, host_template=None):
"""Prepare a volume for a delete operation.
:param serial_number: the array serial number
@ -1111,6 +1116,7 @@ class PowerMaxMasking(object):
:param connector: the connector object
:param reset: flag to indicate if reset is required -- bool
:param async_grp: the async rep group
:param host_template: the host template (if it exists)
"""
move = False
short_host_name = None
@ -1124,21 +1130,24 @@ class PowerMaxMasking(object):
if len(storagegroup_names) == 1 and reset is True:
move = True
elif connector is not None:
short_host_name = self.utils.get_host_short_name(
connector['host'])
short_host_name = self.utils.get_host_name_label(
connector.get('host'),
host_template) if connector.get('host') else None
move = reset
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)
extra_specs, connector, move,
host_template=host_template)
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)
extra_specs, connector, move,
host_template=host_template)
if reset is True and move is False:
self.add_volume_to_default_storage_group(
serial_number, device_id, volume_name,
@ -1146,7 +1155,7 @@ class PowerMaxMasking(object):
def remove_volume_from_sg(
self, serial_number, device_id, vol_name, storagegroup_name,
extra_specs, connector=None, move=False):
extra_specs, connector=None, move=False, host_template=None):
"""Remove a volume from a storage group.
:param serial_number: the array serial number
@ -1156,6 +1165,7 @@ class PowerMaxMasking(object):
:param extra_specs: the extra specifications
:param connector: the connector object
:param move: flag to indicate if move should be used instead of remove
:param host_template: the host template (if it exists)
"""
masking_list = self.rest.get_masking_views_from_storage_group(
serial_number, storagegroup_name)
@ -1179,7 +1189,7 @@ class PowerMaxMasking(object):
# Last volume in the storage group - delete sg.
self._last_vol_in_sg(
serial_number, device_id, vol_name, sg_name,
extra_specs, move)
extra_specs, move, host_template=host_template)
else:
# Not the last volume so remove it from storage group
self._multiple_vols_in_sg(
@ -1221,7 +1231,8 @@ class PowerMaxMasking(object):