PowerMax Driver - Short host name and port group name override

This feature allows the user to override the short host name and port group
name seen in PowerMax masking view and storage view terminology. This
facilitates the user to give more meaningful names, especially when the
short host name exceeds 16 characters and the port group name exceeds
12 characters, which is the condition where the driver truncates these value.

Change-Id: I38153c731170f736f83f3bda36ee6c8969a79d50
Implements: blueprint powermax-user-defined-hostname-portgroup
changes/29/702229/11
Helen Walsh 2020-01-13 14:38:16 +00:00
parent cff7fffc36
commit e276986e4b
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

@ -44,9 +44,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)
@ -71,7 +73,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)
@ -82,6 +83,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(
@ -266,7 +315,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')
@ -281,7 +331,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):
@ -295,7 +346,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):
@ -326,7 +388,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
@ -349,7 +411,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
@ -612,7 +674,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
@ -630,6 +694,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
@ -691,6 +776,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)
@ -1140,6 +1226,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
@ -1462,7 +1573,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,
@ -1498,7 +1609,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',
@ -1542,7 +1653,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):