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-portgroupchanges/29/702229/11
parent
cff7fffc36
commit
e276986e4b
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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):
|
|||