Merge "PowerMax Driver - Short host name and port group name override"
This commit is contained in:
commit
e1ace50684
@ -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:
|
||||
|
@ -45,9 +45,11 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.mock_object(volume_utils, 'get_max_over_subscription_ratio',
|
||||
return_value=1.0)
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||
emc_file=None, volume_backend_name='CommonTests', interval=1,
|
||||
retries=1, san_ip='1.1.1.1', san_login='smc',
|
||||
vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc',
|
||||
san_api_port=8443, vmax_port_groups=[self.data.port_group_name_f])
|
||||
san_api_port=8443, vmax_port_groups=[self.data.port_group_name_f],
|
||||
powermax_port_group_name_template='portGroupName')
|
||||
rest.PowerMaxRest._establish_rest_session = mock.Mock(
|
||||
return_value=tpfo.FakeRequestsSession())
|
||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||
@ -72,7 +74,6 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
side_effect=[[], tpd.PowerMaxData.array_info_wl])
|
||||
def test_gather_info_tests(self, mck_parse, mck_combo, mck_rest,
|
||||
mck_nextgen, mck_ucode):
|
||||
|
||||
# Use-Case 1: Gather info no-opts
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
None, 'config_group', None, None)
|
||||
@ -83,6 +84,54 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.assertTrue(self.common.next_gen)
|
||||
self.assertEqual(self.common.ucode_level, self.data.next_gen_ucode)
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon,
|
||||
'_gather_info')
|
||||
def test_get_attributes_from_config_short_host_template(
|
||||
self, mock_gather):
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
emc_file=None, volume_backend_name='config_group', interval='10',
|
||||
retries='10', replication_device=None,
|
||||
powermax_short_host_name_template='shortHostName')
|
||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||
driver.common._get_attributes_from_config()
|
||||
self.assertEqual(
|
||||
'shortHostName', driver.common.powermax_short_host_name_template)
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon,
|
||||
'_gather_info')
|
||||
def test_get_attributes_from_config_no_short_host_template(
|
||||
self, mock_gather):
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
emc_file=None, volume_backend_name='config_group', interval='10',
|
||||
retries='10', replication_device=None)
|
||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||
driver.common._get_attributes_from_config()
|
||||
self.assertIsNone(driver.common.powermax_short_host_name_template)
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon,
|
||||
'_gather_info')
|
||||
def test_get_attributes_from_config_port_group_template(
|
||||
self, mock_gather):
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
emc_file=None, volume_backend_name='config_group', interval='10',
|
||||
retries='10', replication_device=None,
|
||||
powermax_port_group_name_template='portGroupName')
|
||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||
driver.common._get_attributes_from_config()
|
||||
self.assertEqual(
|
||||
'portGroupName', driver.common.powermax_port_group_name_template)
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon,
|
||||
'_gather_info')
|
||||
def test_get_attributes_from_config_no_port_group_template(
|
||||
self, mock_gather):
|
||||
configuration = tpfo.FakeConfiguration(
|
||||
emc_file=None, volume_backend_name='config_group', interval='10',
|
||||
retries='10', replication_device=None)
|
||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||
driver.common._get_attributes_from_config()
|
||||
self.assertIsNone(driver.common.powermax_port_group_name_template)
|
||||
|
||||
def test_get_slo_workload_combinations_powermax(self):
|
||||
array_info = self.common.get_attributes_from_cinder_config()
|
||||
finalarrayinfolist = self.common._get_slo_workload_combinations(
|
||||
@ -267,7 +316,8 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
array, volume, device_id, extra_specs, self.data.connector, False)
|
||||
mock_rm.assert_called_once_with(
|
||||
array, volume, device_id, volume_name,
|
||||
extra_specs, True, self.data.connector, async_grp=None)
|
||||
extra_specs, True, self.data.connector, async_grp=None,
|
||||
host_template=None)
|
||||
|
||||
@mock.patch.object(masking.PowerMaxMasking,
|
||||
'return_volume_to_fast_managed_group')
|
||||
@ -282,7 +332,8 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
array, volume, device_id, extra_specs, self.data.connector, True)
|
||||
mock_rm.assert_called_once_with(
|
||||
array, volume, device_id, volume_name,
|
||||
extra_specs, False, self.data.connector, async_grp=None)
|
||||
extra_specs, False, self.data.connector, async_grp=None,
|
||||
host_template=None)
|
||||
mock_return.assert_called_once()
|
||||
|
||||
def test_unmap_lun(self):
|
||||
@ -296,7 +347,18 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.common._unmap_lun(volume, connector)
|
||||
mock_remove.assert_called_once_with(
|
||||
array, volume, device_id, extra_specs,
|
||||
connector, False, async_grp=None)
|
||||
connector, False, async_grp=None, host_template=None)
|
||||
|
||||
def test_unmap_lun_force(self):
|
||||
volume = self.data.test_volume
|
||||
extra_specs = deepcopy(self.data.extra_specs_intervals_set)
|
||||
extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
|
||||
connector = deepcopy(self.data.connector)
|
||||
del connector['host']
|
||||
with mock.patch.object(
|
||||
self.common.utils, 'get_host_short_name') as mock_host:
|
||||
self.common._unmap_lun(volume, connector)
|
||||
mock_host.assert_not_called()
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon, '_remove_members')
|
||||
def test_unmap_lun_attachments(self, mock_rm):
|
||||
@ -327,7 +389,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.common._unmap_lun(volume, connector)
|
||||
mock_remove.assert_called_once_with(
|
||||
array, volume, device_id, extra_specs,
|
||||
connector, False, async_grp=None)
|
||||
connector, False, async_grp=None, host_template=None)
|
||||
|
||||
def test_unmap_lun_not_mapped(self):
|
||||
volume = self.data.test_volume
|
||||
@ -350,7 +412,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.common._unmap_lun(volume, None)
|
||||
mock_remove.assert_called_once_with(
|
||||
array, volume, device_id, extra_specs, None,
|
||||
False, async_grp=None)
|
||||
False, async_grp=None, host_template=None)
|
||||
|
||||
def test_initialize_connection_already_mapped(self):
|
||||
volume = self.data.test_volume
|
||||
@ -613,7 +675,9 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
|
||||
@mock.patch.object(
|
||||
common.PowerMaxCommon, '_get_masking_views_from_volume',
|
||||
return_value=([], [tpd.PowerMaxData.masking_view_name_f]))
|
||||
return_value=([tpd.PowerMaxData.masking_view_name_f],
|
||||
[tpd.PowerMaxData.masking_view_name_f,
|
||||
tpd.PowerMaxData.masking_view_name_Y_f]))
|
||||
def test_find_host_lun_id_multiattach(self, mock_mask):
|
||||
volume = self.data.test_volume
|
||||
extra_specs = self.data.extra_specs
|
||||
@ -631,6 +695,27 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.data.extra_specs, self.data.rep_extra_specs)
|
||||
mock_tgt.assert_called_once()
|
||||
|
||||
@mock.patch.object(rest.PowerMaxRest, 'find_mv_connections_for_vol',
|
||||
return_value='1')
|
||||
@mock.patch.object(common.PowerMaxCommon, '_get_masking_views_from_volume',
|
||||
side_effect=[([], ['OS-HostX-I-PG-MV']),
|
||||
(['OS-HostX-I-PG-MV'],
|
||||
['OS-HostX-I-PG-MV'])])
|
||||
@mock.patch.object(rest.PowerMaxRest, 'get_volume',
|
||||
return_value=tpd.PowerMaxData.volume_details[0])
|
||||
def test_find_host_lun_id_backward_compatible(
|
||||
self, mock_vol, mock_mvs, mock_mv_conns):
|
||||
expected_dict = {'hostlunid': '1', 'maskingview': 'OS-HostX-I-PG-MV',
|
||||
'array': '000197800123', 'device_id': '00001'}
|
||||
self.common.powermax_short_host_name_template = (
|
||||
'shortHostName[:7]finance')
|
||||
masked_vols, is_multiattach = self.common.find_host_lun_id(
|
||||
self.data.test_volume, 'HostX',
|
||||
self.data.extra_specs)
|
||||
self.assertEqual(expected_dict, masked_vols)
|
||||
self.assertFalse(is_multiattach)
|
||||
mock_mv_conns.assert_called_once()
|
||||
|
||||
def test_get_masking_views_from_volume(self):
|
||||
array = self.data.array
|
||||
device_id = self.data.device_id
|
||||
@ -692,6 +777,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
extra_specs[utils.WORKLOAD] = self.data.workload
|
||||
ref_mv_dict = self.data.masking_view_dict
|
||||
self.common.next_gen = False
|
||||
self.common.powermax_port_group_name_template = 'portGroupName'
|
||||
masking_view_dict = self.common._populate_masking_dict(
|
||||
volume, connector, extra_specs)
|
||||
self.assertEqual(ref_mv_dict, masking_view_dict)
|
||||
@ -1142,6 +1228,31 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
self.data.test_volume, self.data.connector)
|
||||
self.assertEqual([self.data.wwnn1], metro_wwns)
|
||||
|
||||
@mock.patch.object(common.PowerMaxCommon,
|
||||
'_get_target_wwns_from_masking_view')
|
||||
@mock.patch.object(utils.PowerMaxUtils, 'get_host_name_label',
|
||||
return_value = 'my_short_h94485')
|
||||
@mock.patch.object(utils.PowerMaxUtils, 'is_replication_enabled',
|
||||
return_value=False)
|
||||
def test_get_target_wwns_host_override(
|
||||
self, mock_rep_check, mock_label, mock_mv):
|
||||
host_record = {'host': 'my_short_host_name'}
|
||||
connector = deepcopy(self.data.connector)
|
||||
connector.update(host_record)
|
||||
extra_specs = {'pool_name': 'Diamond+DSS+SRP_1+000197800123',
|
||||
'srp': 'SRP_1', 'array': '000197800123',
|
||||
'storagetype:portgroupname': 'OS-fibre-PG',
|
||||
'interval': 1, 'retries': 1, 'slo': 'Diamond',
|
||||
'workload': 'DSS'}
|
||||
host_template = 'shortHostName[:10]uuid[:5]'
|
||||
self.common.powermax_short_host_name_template = host_template
|
||||
self.common.get_target_wwns_from_masking_view(
|
||||
self.data.test_volume, connector)
|
||||
mock_label.assert_called_once_with(
|
||||
connector['host'], host_template)
|
||||
mock_mv.assert_called_once_with(
|
||||
self.data.device_id, 'my_short_h94485', extra_specs)
|
||||
|
||||
def test_get_port_group_from_masking_view(self):
|
||||
array = self.data.array
|
||||
maskingview_name = self.data.masking_view_name_f
|
||||
@ -1464,7 +1575,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
|
||||
return_value=tpd.PowerMaxData.sg_details[1])
|
||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
||||
return_value=('OS-Test-SG', '', '', ''))
|
||||
return_value=('OS-Test-SG', '', ''))
|
||||
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
|
||||
return_value=True)
|
||||
@mock.patch.object(masking.PowerMaxMasking,
|
||||
@ -1500,7 +1611,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
rest.PowerMaxRest, 'get_volume',
|
||||
return_value=tpd.PowerMaxData.volume_details_attached)
|
||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
||||
return_value=('OS-Test-SG', '', '', ''))
|
||||
return_value=('OS-Test-SG', '', ''))
|
||||
@mock.patch.object(provision.PowerMaxProvision, 'create_storage_group')
|
||||
@mock.patch.object(masking.PowerMaxMasking, 'add_child_sg_to_parent_sg')
|
||||
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
|
||||
@ -1544,7 +1655,7 @@ class PowerMaxCommonTest(test.TestCase):
|
||||
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
|
||||
side_effect=[tpd.PowerMaxData.sg_details[1], None])
|
||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
||||
return_value=('OS-Test-SG', '', '', ''))
|
||||
return_value=('OS-Test-SG', '', ''))
|
||||
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
|
||||
return_value=False)
|
||||
@mock.patch.object(masking.PowerMaxMasking,
|
||||
|
@ -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):
|
||||
# Last volume in the storage group - delete sg.
|
||||
self._last_vol_in_sg(
|
||||
serial_number, device_id, vol_name, sg_name,
|
||||
extra_specs, move, connector)
|
||||
extra_specs, move, connector,
|
||||
host_template=host_template)
|
||||
else:
|
||||
# Not the last volume so remove it from storage group
|
||||
self._multiple_vols_in_sg(
|
||||
@ -1235,8 +1246,9 @@ class PowerMaxMasking(object):
|
||||
return do_remove_volume_from_sg(masking_name, storagegroup_name,
|
||||
parent_sg_name, serial_number)
|
||||
|
||||
def _last_vol_in_sg(self, serial_number, device_id, volume_name,
|
||||
storagegroup_name, extra_specs, move, connector=None):
|
||||
def _last_vol_in_sg(
|
||||
self, serial_number, device_id, volume_name, storagegroup_name,
|
||||
extra_specs, move, connector=None, host_template=None):
|
||||
"""Steps if the volume is the last in a storage group.
|
||||
|
||||
1. Check if the volume is in a masking view.
|
||||
@ -1255,6 +1267,7 @@ class PowerMaxMasking(object):
|
||||
:param extra_specs: extra specifications
|
||||
:param move: flag to indicate a move instead of remove
|
||||
:param connector: the connector object
|
||||
:param host_template: the host template (if it exists)
|
||||
:returns: status -- bool
|
||||
"""
|
||||
LOG.debug("Only one volume remains in storage group "
|
||||
@ -1269,7 +1282,8 @@ class PowerMaxMasking(object):
|
||||
else:
|
||||
status = self._last_vol_masking_views(
|
||||
serial_number, storagegroup_name, maskingview_list,
|
||||
device_id, volume_name, extra_specs, connector, move)
|
||||
device_id, volume_name, extra_specs, connector, move,
|
||||
host_template)
|
||||
return status
|
||||
|
||||
def _last_vol_no_masking_views(self, serial_number, storagegroup_name,
|
||||
@ -1314,7 +1328,8 @@ class PowerMaxMasking(object):
|
||||
|
||||
def _last_vol_masking_views(
|
||||
self, serial_number, storagegroup_name, maskingview_list,
|
||||
device_id, volume_name, extra_specs, connector, move):
|
||||
device_id, volume_name, extra_specs, connector, move,
|
||||
host_template=None):
|
||||
"""Remove the last vol from an sg associated with masking views.
|
||||
|
||||
Helper function for removing the last vol from a storage group
|
||||
@ -1326,6 +1341,7 @@ class PowerMaxMasking(object):
|
||||
:param volume_name: the volume name
|
||||
:param extra_specs: the extra specifications
|
||||
:param move: flag to indicate a move instead of remove
|
||||
:param host_template: the host template (if it exists)
|
||||
:returns: status -- bool
|
||||
"""
|
||||
status = False
|
||||
@ -1336,7 +1352,8 @@ class PowerMaxMasking(object):
|
||||
if num_vols_in_mv == 1:
|
||||
self._delete_mv_ig_and_sg(
|
||||
serial_number, device_id, mv, storagegroup_name,
|
||||
parent_sg_name, connector, move, extra_specs)
|
||||
parent_sg_name, connector, move, extra_specs,
|
||||
host_template=host_template)
|
||||
else:
|
||||
self._remove_last_vol_and_delete_sg(
|
||||
serial_number, device_id, volume_name,
|
||||
@ -1435,7 +1452,7 @@ class PowerMaxMasking(object):
|
||||
|
||||
def _delete_mv_ig_and_sg(
|
||||
self, serial_number, device_id, masking_view, storagegroup_name,
|
||||
parent_sg_name, connector, move, extra_specs):
|
||||
parent_sg_name, connector, move, extra_specs, host_template=None):
|
||||
"""Delete the masking view, storage groups and initiator group.
|
||||
|
||||
:param serial_number: array serial number
|
||||
@ -1446,15 +1463,15 @@ class PowerMaxMasking(object):
|
||||
:param connector: the connector object
|
||||
:param move: flag to indicate if the volume should be moved
|
||||
:param extra_specs: the extra specifications
|
||||
:param host_template: the host template (if it exists)
|
||||
"""
|
||||
host = (self.utils.get_host_short_name(connector['host'])
|
||||
if connector else None)
|
||||
|
||||
initiatorgroup = self.rest.get_element_from_masking_view(
|
||||
serial_number, masking_view, host=True)
|
||||
self._last_volume_delete_masking_view(serial_number, masking_view)
|
||||
self._last_volume_delete_initiator_group(
|
||||
serial_number, initiatorgroup, host)
|
||||
serial_number, initiatorgroup,
|
||||
connector.get('host') if connector else None,
|
||||
host_template)
|
||||
self._delete_cascaded_storage_groups(
|
||||
serial_number, storagegroup_name, parent_sg_name,
|
||||
extra_specs, device_id, move)
|
||||
@ -1643,7 +1660,8 @@ class PowerMaxMasking(object):
|
||||
self.rest.delete_storage_group(serial_number, storagegroup_name)
|
||||
|
||||
def _last_volume_delete_initiator_group(
|
||||
self, serial_number, initiatorgroup_name, host):
|
||||
self, serial_number, initiatorgroup_name, host,
|
||||
host_template=None):
|
||||
"""Delete the initiator group.
|
||||
|
||||
Delete the Initiator group if it has been created by the PowerMax
|
||||
@ -1651,40 +1669,58 @@ class PowerMaxMasking(object):
|
||||
:param serial_number: the array serial number
|
||||
:param initiatorgroup_name: initiator group name
|
||||
:param host: the short name of the host
|
||||
:param host_template: the host template (if it exists)
|
||||
"""
|
||||
def _do_delete_initiator_group(array, init_group_name):
|
||||
is_deleted = False
|
||||
maskingview_names = (
|
||||
self.rest.get_masking_views_by_initiator_group(
|
||||
array, init_group_name))
|
||||
if not maskingview_names:
|
||||
@coordination.synchronized(
|
||||
"emc-ig-{ig_name}-{array}")
|
||||
def _delete_ig(ig_name, array):
|
||||
# Check initiator group hasn't been recently deleted
|
||||
ig_details = self.rest.get_initiator_group(
|
||||
serial_number, ig_name)
|
||||
if ig_details:
|
||||
LOG.debug(
|
||||
"Last volume associated with the initiator "
|
||||
"group - deleting the associated initiator "
|
||||
"group %(initiatorgroup_name)s.",
|
||||
{'initiatorgroup_name': ig_name})
|
||||
self.rest.delete_initiator_group(
|
||||
array, ig_name)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
is_deleted = _delete_ig(init_group_name, array)
|
||||
else:
|
||||
LOG.warning("Initiator group %(ig_name)s is associated "
|
||||
"with masking views and can't be deleted. "
|
||||
"Number of associated masking view is: "
|
||||
"%(nmv)d.",
|
||||
{'ig_name': init_group_name,
|
||||
'nmv': len(maskingview_names)})
|
||||
return is_deleted
|
||||
|
||||
if host is not None:
|
||||
protocol = self.utils.get_short_protocol_type(self.protocol)
|
||||
default_ig_name = ("OS-%(shortHostName)s-%(protocol)s-IG"
|
||||
% {'shortHostName': host,
|
||||
'protocol': protocol})
|
||||
host_label = (self.utils.get_host_name_label(
|
||||
host, host_template) if host else None)
|
||||
default_ig_name = self.utils.get_possible_initiator_name(
|
||||
host_label, self.protocol)
|
||||
|
||||
if initiatorgroup_name == default_ig_name:
|
||||
maskingview_names = (
|
||||
self.rest.get_masking_views_by_initiator_group(
|
||||
serial_number, initiatorgroup_name))
|
||||
if not maskingview_names:
|
||||
@coordination.synchronized(
|
||||
"emc-ig-{ig_name}-{serial_number}")
|
||||
def _delete_ig(ig_name, serial_number):
|
||||
# Check initiator group hasn't been recently deleted
|
||||
ig_details = self.rest.get_initiator_group(
|
||||
serial_number, ig_name)
|
||||
if ig_details:
|
||||
LOG.debug(
|
||||
"Last volume associated with the initiator "
|
||||
"group - deleting the associated initiator "
|
||||
"group %(initiatorgroup_name)s.",
|
||||
{'initiatorgroup_name': initiatorgroup_name})
|
||||
self.rest.delete_initiator_group(
|
||||
serial_number, initiatorgroup_name)
|
||||
_delete_ig(initiatorgroup_name, serial_number)
|
||||
else:
|
||||
LOG.warning("Initiator group %(ig_name)s is associated "
|
||||
"with masking views and can't be deleted. "
|
||||
"Number of associated masking view is: "
|
||||
"%(nmv)d.",
|
||||
{'ig_name': initiatorgroup_name,
|
||||
'nmv': len(maskingview_names)})
|
||||
is_deleted = _do_delete_initiator_group(
|
||||
serial_number, initiatorgroup_name)
|
||||
if not is_deleted:
|
||||
host_label = (self.utils.get_host_short_name(
|
||||
host) if host else None)
|
||||
default_ig_name = self.utils.get_possible_initiator_name(
|
||||
host_label, self.protocol)
|
||||
if initiatorgroup_name == default_ig_name:
|
||||
_do_delete_initiator_group(
|
||||
serial_number, initiatorgroup_name)
|
||||
else:
|
||||
LOG.warning("Initiator group %(ig_name)s was "
|
||||
"not created by the PowerMax driver so will "
|
||||
@ -1715,19 +1751,13 @@ class PowerMaxMasking(object):
|
||||
for sg in sg_list.get('storageGroupId', []):
|
||||
if slo_wl_combo in sg:
|
||||
fast_source_sg_name = sg
|
||||
masking_view_name = (
|
||||
self.rest.get_masking_views_from_storage_group(
|
||||
serial_number, fast_source_sg_name))[0]
|
||||
port_group_name = self.rest.get_element_from_masking_view(
|
||||
serial_number, masking_view_name, portgroup=True)
|
||||
short_pg_name = self.utils.get_pg_short_name(port_group_name)
|
||||
short_host_name = masking_view_name.lstrip('OS-').rstrip(
|
||||
'-%s-MV' % short_pg_name)[:-2]
|
||||
extra_specs[utils.PORTGROUPNAME] = short_pg_name
|
||||
short_host_name, port_group_label = (
|
||||
self._get_host_and_port_group_labels(
|
||||
serial_number, fast_source_sg_name))
|
||||
no_slo_extra_specs = deepcopy(extra_specs)
|
||||
no_slo_extra_specs[utils.SLO] = None
|
||||
no_slo_sg_name, __, __, __ = self.utils.get_child_sg_name(
|
||||
short_host_name, no_slo_extra_specs)
|
||||
no_slo_sg_name, __, __ = self.utils.get_child_sg_name(
|
||||
short_host_name, no_slo_extra_specs, port_group_label)
|
||||
source_sg_details = self.rest.get_storage_group(
|
||||
serial_number, fast_source_sg_name)
|
||||
parent_sg_name = source_sg_details[
|
||||
@ -1763,6 +1793,34 @@ class PowerMaxMasking(object):
|
||||
message=exception_message)
|
||||
return mv_dict
|
||||
|
||||
def _get_host_and_port_group_labels(
|
||||
self, serial_number, storage_group):
|
||||
"""Get the host and port group labels
|
||||
|
||||
:param serial_number: the array serial number
|
||||
:param storage_group: the storage group
|
||||
:returns: short_host_name, port_group_label
|
||||
"""
|
||||
masking_view_name = (
|
||||
self.rest.get_masking_views_from_storage_group(
|
||||
serial_number, storage_group))[0]
|
||||
object_dict = self.get_components_from_masking_view_name(
|
||||
masking_view_name)
|
||||
return object_dict['host'], object_dict['portgroup']
|
||||
|
||||
def get_components_from_masking_view_name(self, masking_view_name):
|
||||
"""Get the host and port group labels
|
||||
|
||||
:param masking_view_name: the masking view name
|
||||
:returns: object dict
|
||||
"""
|
||||
regex_str = (r'^(?P<prefix>OS)-(?P<host>.+?)(?P<protocol>I|F)-'
|
||||
r'(?P<portgroup>(?!CD|RE|CD-RE).+)-(?P<postfix>MV)$')
|
||||
|
||||
object_dict = self.utils.get_object_components_and_correct_host(
|
||||
regex_str, masking_view_name)
|
||||
return object_dict
|
||||
|
||||
def return_volume_to_fast_managed_group(
|
||||
self, serial_number, device_id, extra_specs):
|
||||
"""Return a volume to a fast managed group if slo is set.
|
||||
@ -1782,18 +1840,11 @@ class PowerMaxMasking(object):
|
||||
for sg in sg_list.get('storageGroupId', []):
|
||||
if slo_wl_combo in sg:
|
||||
no_slo_sg_name = sg
|
||||
masking_view_name = (
|
||||
self.rest.get_masking_views_from_storage_group(
|
||||
serial_number, no_slo_sg_name))[0]
|
||||
port_group_name = self.rest.get_element_from_masking_view(
|
||||
serial_number, masking_view_name, portgroup=True)
|
||||
short_pg_name = self.utils.get_pg_short_name(
|
||||
port_group_name)
|
||||
short_host_name = masking_view_name.lstrip('OS-').rstrip(
|
||||
'-%s-MV' % short_pg_name)[:-2]
|
||||
extra_specs[utils.PORTGROUPNAME] = short_pg_name
|
||||
fast_sg_name, _, _, _ = self.utils.get_child_sg_name(
|
||||
short_host_name, extra_specs)
|
||||
short_host_name, port_group_label = (
|
||||
self._get_host_and_port_group_labels(
|
||||
serial_number, no_slo_sg_name))
|
||||
fast_sg_name, _, _ = self.utils.get_child_sg_name(
|
||||
short_host_name, extra_specs, port_group_label)
|
||||
source_sg_details = self.rest.get_storage_group(
|
||||
serial_number, no_slo_sg_name)
|
||||
parent_sg_name = source_sg_details[
|
||||
@ -1863,20 +1914,24 @@ class PowerMaxMasking(object):
|
||||
self.rest.delete_storage_group(
|
||||
serial_number, child_sg_name)
|
||||
|
||||
def attempt_ig_cleanup(self, connector, protocol, serial_number, force):
|
||||
def attempt_ig_cleanup(
|
||||
self, connector, protocol, serial_number, force,
|
||||
host_template=None):
|
||||
"""Attempt to cleanup an orphan initiator group
|
||||
|
||||
:param connector: connector object
|
||||
:param protocol: iscsi or fc
|
||||
:param serial_number: extra the array serial number
|
||||
:param force: flag to indicate if operation should be forced
|
||||
:param host_template: the host template (if it exists)
|
||||
"""
|
||||
protocol = self.utils.get_short_protocol_type(protocol)
|
||||
host_name = connector['host']
|
||||
short_host_name = self.utils.get_host_short_name(host_name)
|
||||
init_group = (
|
||||
("OS-%(shortHostName)s-%(protocol)s-IG"
|
||||
% {'shortHostName': short_host_name,
|
||||
'protocol': protocol}))
|
||||
host_name = connector.get('host')
|
||||
|
||||
host_label = self.utils.get_host_name_label(
|
||||
host_name, host_template=host_template)
|
||||
initiator_group_name = self.utils.get_possible_initiator_name(
|
||||
host_label, protocol)
|
||||
|
||||
self._check_ig_rollback(
|
||||
serial_number, init_group, connector, force)
|
||||
serial_number, initiator_group_name, connector, force)
|
||||
|
@ -319,7 +319,8 @@ class PowerMaxVolumeMetadata(object):
|
||||
parent_storage_group=parent_storage_group,
|
||||
initiator_group=initiator_group,
|
||||
port_group=port_group,
|
||||
host=host, is_multipath=is_multipath,
|
||||
host=host, used_host_name=masking_view_dict[utils.USED_HOST_NAME],
|
||||
is_multipath=is_multipath,
|
||||
identifier_name=self.utils.get_volume_element_name(volume.id),
|
||||
openstack_name=volume.display_name,
|
||||
mv_list=mv_list, sg_list=sg_list,
|
||||
|
@ -44,6 +44,8 @@ TRUNCATE_5 = 5
|
||||
TRUNCATE_27 = 27
|
||||
UCODE_5978_ELMSR = 221
|
||||
UCODE_5978 = 5978
|
||||
UPPER_HOST_CHARS = 16
|
||||
UPPER_PORT_GROUP_CHARS = 12
|
||||
|
||||
ARRAY = 'array'
|
||||
SLO = 'slo'
|
||||
@ -80,6 +82,7 @@ DEFAULT_PORT = 8443
|
||||
CLONE_SNAPSHOT_NAME = "snapshot_for_clone"
|
||||
STORAGE_GROUP_TAGS = 'storagetype:storagegrouptags'
|
||||
TAG_LIST = 'tag_list'
|
||||
USED_HOST_NAME = "used_host_name"
|
||||
|
||||
# Multiattach constants
|
||||
IS_MULTIATTACH = 'multiattach'
|
||||
@ -112,6 +115,9 @@ POWERMAX_SERVICE_LEVEL = 'powermax_service_level'
|
||||
POWERMAX_PORT_GROUPS = 'powermax_port_groups'
|
||||
POWERMAX_SNAPVX_UNLINK_LIMIT = 'powermax_snapvx_unlink_limit'
|
||||
POWERMAX_ARRAY_TAG_LIST = 'powermax_array_tag_list'
|
||||
POWERMAX_SHORT_HOST_NAME_TEMPLATE = 'powermax_short_host_name_template'
|
||||
POWERMAX_PORT_GROUP_NAME_TEMPLATE = 'powermax_port_group_name_template'
|
||||
PORT_GROUP_LABEL = 'port_group_label'
|
||||
|
||||
|
||||
class PowerMaxUtils(object):
|
||||
@ -127,6 +133,20 @@ class PowerMaxUtils(object):
|
||||
def get_host_short_name(self, host_name):
|
||||
"""Returns the short name for a given qualified host name.
|
||||
|
||||
Checks the host name to see if it is the fully qualified host name
|
||||
and returns part before the dot. If there is no dot in the host name
|
||||
the full host name is returned.
|
||||
:param host_name: the fully qualified host name
|
||||
:returns: string -- the short host_name
|
||||
"""
|
||||
short_host_name = self.get_host_short_name_from_fqn(host_name)
|
||||
|
||||
return self.generate_unique_trunc_host(short_host_name)
|
||||
|
||||
@staticmethod
|
||||
def get_host_short_name_from_fqn(host_name):
|
||||
"""Returns the short name for a given qualified host name.
|
||||
|
||||
Checks the host name to see if it is the fully qualified host name
|
||||
and returns part before the dot. If there is no dot in the host name
|
||||
the full host name is returned.
|
||||
@ -139,7 +159,7 @@ class PowerMaxUtils(object):
|
||||
else:
|
||||
short_host_name = host_name
|
||||
|
||||
return self.generate_unique_trunc_host(short_host_name)
|
||||
return short_host_name
|
||||
|
||||
@staticmethod
|
||||
def get_volumetype_extra_specs(volume, volume_type_id=None):
|
||||
@ -286,15 +306,12 @@ class PowerMaxUtils(object):
|
||||
:param host_name: long host name
|
||||
:returns: truncated host name
|
||||
"""
|
||||
if host_name and len(host_name) > 16:
|
||||
host_name = host_name.lower()
|
||||
m = hashlib.md5()
|
||||
m.update(host_name.encode('utf-8'))
|
||||
uuid = m.hexdigest()
|
||||
if host_name and len(host_name) > UPPER_HOST_CHARS:
|
||||
uuid = self.get_uuid_of_input(host_name)
|
||||
new_name = ("%(host)s%(uuid)s"
|
||||
% {'host': host_name[-6:],
|
||||
'uuid': uuid})
|
||||
host_name = self.truncate_string(new_name, 16)
|
||||
host_name = self.truncate_string(new_name, UPPER_HOST_CHARS)
|
||||
return host_name
|
||||
|
||||
def get_pg_short_name(self, portgroup_name):
|
||||
@ -303,17 +320,27 @@ class PowerMaxUtils(object):
|
||||
:param portgroup_name: long portgroup_name
|
||||
:returns: truncated portgroup_name
|
||||
"""
|
||||
if portgroup_name and len(portgroup_name) > 12:
|
||||
portgroup_name = portgroup_name.lower()
|
||||
m = hashlib.md5()
|
||||
m.update(portgroup_name.encode('utf-8'))
|
||||
uuid = m.hexdigest()
|
||||
if portgroup_name and len(portgroup_name) > UPPER_PORT_GROUP_CHARS:
|
||||
uuid = self.get_uuid_of_input(portgroup_name)
|
||||
new_name = ("%(pg)s%(uuid)s"
|
||||
% {'pg': portgroup_name[-6:],
|
||||
'uuid': uuid})
|
||||
portgroup_name = self.truncate_string(new_name, 12)
|
||||
portgroup_name = self.truncate_string(
|
||||
new_name, UPPER_PORT_GROUP_CHARS)
|
||||
return portgroup_name
|
||||
|
||||
@staticmethod
|
||||
def get_uuid_of_input(input_str):
|
||||
"""Get the uuid of the input string
|
||||
|
||||
:param input_str: input string
|
||||
:returns: uuid
|
||||
"""
|
||||
input_str = input_str.lower()
|
||||
m = hashlib.md5()
|
||||
m.update(input_str.encode('utf-8'))
|
||||
return m.hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def get_default_oversubscription_ratio(max_over_sub_ratio):
|
||||
"""Override ratio if necessary.
|
||||
@ -732,6 +759,7 @@ class PowerMaxUtils(object):
|
||||
"""Get the name of the default sg from the extra specs.
|
||||
|
||||
:param extra_specs: extra specs
|
||||
:param rep_mode: replication mode
|
||||
:returns: default sg - string
|
||||
"""
|
||||
do_disable_compression = self.is_compression_disabled(
|
||||
@ -763,7 +791,7 @@ class PowerMaxUtils(object):
|
||||
"""Get the temporary group name used for failover.
|
||||
|
||||
:param rep_config: the replication config
|
||||
:return: temp_grp_name
|
||||
:returns: temp_grp_name
|
||||
"""
|
||||
temp_grp_name = ("OS-%(rdf)s-temp-rdf-sg"
|
||||
% {'rdf': rep_config['rdf_group_label']})
|
||||
@ -771,15 +799,15 @@ class PowerMaxUtils(object):
|
||||
{'name': temp_grp_name})
|
||||
return temp_grp_name
|
||||
|
||||
def get_child_sg_name(self, host_name, extra_specs):
|
||||
def get_child_sg_name(self, host_name, extra_specs, port_group_label):
|
||||
"""Get the child storage group name for a masking view.
|
||||
|
||||
:param host_name: the short host name
|
||||
:param extra_specs: the extra specifications
|
||||
:return: child sg name, compression flag, rep flag, short pg name
|
||||
:param port_group_label: the port group label
|
||||
:returns: child sg name, compression flag, rep flag, short pg name
|
||||
"""
|
||||
do_disable_compression = False
|
||||
pg_name = self.get_pg_short_name(extra_specs[PORTGROUPNAME])
|
||||
rep_enabled = self.is_replication_enabled(extra_specs)
|
||||
if extra_specs[SLO]:
|
||||
slo_wl_combo = self.truncate_string(
|
||||
@ -790,7 +818,7 @@ class PowerMaxUtils(object):
|
||||
% {'shortHostName': host_name,
|
||||
'srpName': unique_name,
|
||||
'combo': slo_wl_combo,
|
||||
'pg': pg_name})
|
||||
'pg': port_group_label})
|
||||
do_disable_compression = self.is_compression_disabled(
|
||||
extra_specs)
|
||||
if do_disable_compression:
|
||||
@ -799,11 +827,11 @@ class PowerMaxUtils(object):
|
||||
else:
|
||||
child_sg_name = (
|
||||
"OS-%(shortHostName)s-No_SLO-%(pg)s"
|
||||
% {'shortHostName': host_name, 'pg': pg_name})
|
||||
% {'shortHostName': host_name, 'pg': port_group_label})
|
||||
if rep_enabled:
|
||||
rep_mode = extra_specs.get(REP_MODE, None)
|
||||
child_sg_name += self.get_replication_prefix(rep_mode)
|
||||
return child_sg_name, do_disable_compression, rep_enabled, pg_name
|
||||
return child_sg_name, do_disable_compression, rep_enabled
|
||||
|
||||
@staticmethod
|
||||
def change_multiattach(extra_specs, new_type_extra_specs):
|
||||
@ -811,7 +839,7 @@ class PowerMaxUtils(object):
|
||||
|
||||
:param extra_specs: the source type extra specs
|
||||
:param new_type_extra_specs: the target type extra specs
|
||||
:return: bool
|
||||
:returns: bool
|
||||
"""
|
||||
is_src_multiattach = volume_utils.is_boolean_str(
|
||||
extra_specs.get('multiattach'))
|
||||
@ -824,7 +852,7 @@ class PowerMaxUtils(object):
|
||||
"""Check if a volume with verbose description is valid for management.
|
||||
|
||||
:param source_vol: the verbose volume dict
|
||||
:return: bool True/False
|
||||
:returns: bool True/False
|
||||
"""
|
||||
vol_head = source_vol['volumeHeader']
|
||||
|
||||
@ -865,7 +893,7 @@ class PowerMaxUtils(object):
|
||||
"""Check if a volume with snapshot description is valid for management.
|
||||
|
||||
:param source_vol: the verbose volume dict
|
||||
:return: bool True/False
|
||||
:returns: bool True/False
|
||||
"""
|
||||
vol_head = source_vol['volumeHeader']
|
||||
|
||||
@ -902,7 +930,7 @@ class PowerMaxUtils(object):
|
||||
"""Parse a hostname from a storage group ID.
|
||||
|
||||
:param device_info: the device info dict
|
||||
:return: str -- the attached hostname
|
||||
:returns: str -- the attached hostname
|
||||
"""
|
||||
try:
|
||||
sg_id = device_info.get("storageGroupId")[0]
|
||||
@ -962,7 +990,7 @@ class PowerMaxUtils(object):
|
||||
"""Compare number of cylinders of source and target.
|
||||
|
||||
:param cylinders_source: number of cylinders on source
|
||||
:param cylinders_target: number of cylinders on target
|
||||
:param cylinder_target: number of cylinders on target
|
||||
"""
|
||||
if float(cylinders_source) > float(cylinder_target):
|
||||
exception_message = (
|
||||
@ -1054,3 +1082,330 @@ class PowerMaxUtils(object):
|
||||
"""
|
||||
return ','.join(map(str, list_input)) if isinstance(
|
||||
list_input, list) else list_input
|
||||
|
||||
def validate_short_host_name_from_template(
|
||||
self, short_host_template, short_host_name):
|
||||
"""Validate that the short host name is in a format we can use.
|
||||
|
||||
Can be one of
|
||||
shortHostName - where shortHostName is what the driver specifies
|
||||
it to be, default
|
||||
shortHostName[:x]uuid[:x] - where first x characters of the short
|
||||
host name and x uuid characters created from md5 hash of
|
||||
short host name
|
||||
shortHostName[:x]userdef - where first x characters of the short
|
||||
host name and a user defined name
|
||||
shortHostName[-x:]uuid[:x] - where last x characters of short host
|
||||
name and x uuid characters created from md5 hash of short host
|
||||
name
|
||||
shortHostName[-x:]suserdef - where last x characters of the short
|
||||
host name and a user defined name
|
||||
|
||||
:param short_host_template: short host name template
|
||||
:param short_host_name: short host name
|
||||
:raises: VolumeBackendAPIException
|
||||
:returns: new short host name -- string
|
||||
"""
|
||||
new_short_host_name = None
|
||||
is_ok, case = self.regex_check(short_host_template, True)
|
||||
if is_ok:
|
||||
new_short_host_name = (
|
||||
self.generate_entity_string(
|
||||
case, short_host_template, short_host_name, True))
|
||||
if not new_short_host_name:
|
||||
error_message = (_('Unable to generate string from short '
|
||||
'host template %(template)s. Please refer to '
|
||||
'the online documentation for correct '
|
||||
'template format(s) for short host name.') %
|
||||
{'template': short_host_template})
|
||||
LOG.error(error_message)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=error_message)
|
||||
|
||||
return new_short_host_name
|
||||
|
||||
def validate_port_group_name_from_template(
|
||||
self, port_group_template, port_group_name):
|
||||
"""Validate that the port group name is in a format we can use.
|
||||
|
||||
Can be one of
|
||||
portGroupName - where portGroupName is what the driver specifies
|
||||
it to be, default
|
||||
portGroupName[:x]uuid[:x] - where first x characters of the short
|
||||
host name and x uuid characters created from md5 hash of
|
||||
short host name
|
||||
portGroupName[:x]userdef - where first x characters of the short
|
||||
host name and a user defined name
|
||||
portGroupName[-x:]uuid[:x] - where last x characters of short host
|
||||
name and x uuid characters created from md5 hash of short host
|
||||
name
|
||||
portGroupName[-x:]userdef - where last x characters of the short
|
||||
host name and a user defined name
|
||||
|
||||
:param port_group_template: port group name template
|
||||
:param port_group_name: port group name
|
||||
:raises: VolumeBackendAPIException
|
||||
:returns: new port group name -- string
|
||||
"""
|
||||
new_port_group_name = None
|
||||
is_ok, case = self.regex_check(port_group_template, False)
|
||||
if is_ok:
|
||||
new_port_group_name = (
|
||||
self.generate_entity_string(
|
||||
case, port_group_template, port_group_name, False))
|
||||
|
||||
if not new_port_group_name:
|
||||
error_message = (_('Unable to generate string from port group '
|
||||
'template %(template)s. Please refer to '
|
||||
'the online documentation for correct '
|
||||
'template format(s) for port groups.') %
|
||||
{'template': port_group_template})
|
||||
LOG.error(error_message)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=error_message)
|
||||
|
||||
return new_port_group_name
|
||||
|
||||
def generate_entity_string(
|
||||
self, case, entity_template, entity_name, entity_flag):
|
||||
"""Generate the entity string if the template checks out
|
||||
|
||||
:param case: one of five cases
|
||||
:param entity_template: entity template
|
||||
:param entity_name: entity name
|
||||
:param entity_flag: storage group or port group flag
|
||||
:returns: new entity name -- string
|
||||
"""
|
||||
new_entity_name = None
|
||||
override_rule_warning = False
|
||||
try:
|
||||
if case == '1':
|
||||
new_entity_name = self.get_name_if_default_template(
|
||||
entity_name, entity_flag)
|
||||
elif case == '2':
|
||||
pass_two, uuid = self.prepare_string_with_uuid(
|
||||
entity_template, entity_name, entity_flag)
|
||||
m = re.match(r'^' + entity_name +
|
||||
r'\[:(\d+)\]' + uuid + r'\[:(\d+)\]$', pass_two)
|
||||
if m:
|
||||
num_1 = m.group(1)
|
||||
num_2 = m.group(2)
|
||||
self.check_upper_limit(
|
||||
int(num_1), int(num_2), entity_flag)
|
||||
new_entity_name = (
|
||||
entity_name[:int(num_1)] + uuid[:int(num_2)])
|
||||
override_rule_warning = True
|
||||
elif case == '3':
|
||||
pass_two, uuid = self.prepare_string_with_uuid(
|
||||
entity_template, entity_name, entity_flag)
|
||||
m = re.match(r'^' + entity_name +
|
||||
r'\[-(\d+):\]' + uuid + r'\[:(\d+)\]$', pass_two)
|
||||
if m:
|
||||
num_1 = m.group(1)
|
||||
num_2 = m.group(2)
|
||||
self.check_upper_limit(
|
||||
int(num_1), int(num_2), entity_flag)
|
||||
new_entity_name = (
|
||||
entity_name[-int(num_1):] + uuid[:int(num_2)])
|
||||
override_rule_warning = True
|
||||
elif case == '4':
|
||||
pass_two = self.prepare_string_entity(
|
||||
entity_template, entity_name, entity_flag)
|
||||
m = re.match(r'^' + entity_name +
|
||||
r'\[:(\d+)\]' + r'([a-zA-Z0-9_\\-]+)$', pass_two)
|
||||
if m:
|
||||
num_1 = m.group(1)
|
||||
user_defined = m.group(2)
|
||||
self.check_upper_limit(
|
||||
int(num_1), len(user_defined), entity_flag)
|
||||
new_entity_name = entity_name[:int(num_1)] + user_defined
|
||||
override_rule_warning = True
|
||||
elif case == '5':
|
||||
pass_two = self.prepare_string_entity(
|
||||
entity_template, entity_name, entity_flag)
|
||||
m = re.match(r'^' + entity_name +
|
||||
r'\[-(\d+):\]' + r'([a-zA-Z0-9_\\-]+)$', pass_two)
|
||||
if m:
|
||||
num_1 = m.group(1)
|
||||
user_defined = m.group(2)
|
||||
self.check_upper_limit(
|
||||
int(num_1), len(user_defined), entity_flag)
|
||||
new_entity_name = entity_name[-int(num_1):] + user_defined
|
||||
override_rule_warning = True
|
||||
if override_rule_warning:
|
||||
LOG.warning(
|
||||
"You have opted to override the %(entity)s naming format. "
|
||||
"Once changed and you have attached volumes or created "
|
||||
"new instances, you cannot revert to default or change to "
|
||||
"another format.",
|
||||
{'entity': 'storage group'
|
||||
if entity_flag else 'port group'})
|
||||
|
||||
except Exception:
|
||||
new_entity_name = None
|
||||
return new_entity_name
|
||||
|
||||
def get_name_if_default_template(self, entity_name, is_short_host_flag):
|
||||
"""Get the entity name if it is the default template
|
||||
|
||||
:param entity_name: the first number
|
||||
:param is_short_host_flag: the second number
|
||||
:returns: entity name -- string
|
||||
"""
|
||||
if is_short_host_flag:
|
||||
return self.get_host_short_name(entity_name)
|
||||
else:
|
||||
return self.get_pg_short_name(entity_name)
|
||||
|
||||
@staticmethod
|
||||
def check_upper_limit(num_1, num_2, is_host_flag):
|
||||
"""Check that the sum of number is less than upper limit.
|
||||
|
||||
:param num_1: the first number
|
||||
:param num_2: the second number
|
||||
:param is_host_flag: is short host boolean
|
||||
:raises: VolumeBackendAPIException
|
||||
"""
|
||||
if is_host_flag:
|
||||
if (num_1 + num_2) > UPPER_HOST_CHARS:
|
||||
error_message = (_("Host name exceeds the character upper "
|
||||
"limit of %(upper)d. Please check your "
|
||||
"short host template.") %
|
||||
{'upper': UPPER_HOST_CHARS})
|
||||
LOG.error(error_message)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=error_message)
|
||||
else:
|
||||
if (num_1 + num_2) > UPPER_PORT_GROUP_CHARS:
|
||||
error_message = (_("Port group name exceeds the character "
|
||||
"upper limit of %(upper)d. Please check "
|
||||
"your port group template") %
|
||||
{'upper': UPPER_PORT_GROUP_CHARS})
|
||||
LOG.error(error_message)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=error_message)
|
||||
|
||||
def prepare_string_with_uuid(
|
||||
self, template, entity_str, is_short_host_flag):
|
||||
"""Prepare string for pass three
|
||||
|
||||
:param template: the template
|
||||
:param entity_str: the entity string
|
||||
:param is_short_host_flag: is short host
|
||||
:returns: pass_two -- string
|
||||
uuid -- string
|
||||
"""
|
||||
pass_one = self.prepare_string_entity(
|
||||
template, entity_str, is_short_host_flag)
|
||||
uuid = self.get_uuid_of_input(entity_str)
|
||||
pass_two = pass_one.replace('uuid', uuid)
|
||||
return pass_two, uuid
|
||||
|
||||
@staticmethod
|
||||
def prepare_string_entity(template, entity_str, is_host_flag):
|
||||
"""Prepare string for pass two
|
||||
|
||||
:param template: the template
|
||||
:param entity_str: the entity string
|
||||
:param is_host_flag: is host boolean
|
||||
:returns: pass_one -- string
|
||||
"""
|
||||
entity_type = 'shortHostName' if is_host_flag else 'portGroupName'
|
||||
# Replace entity type with variable
|
||||
return template.replace(
|
||||
entity_type, entity_str)
|
||||
|
||||
@staticmethod
|
||||
def regex_check(template, is_short_host_flag):
|
||||
"""Check the template is in a validate format.
|
||||
|
||||
:param template: short host name template
|
||||
:param is_short_host_flag: short host boolean
|
||||
:returns: boolean,
|
||||
case -- string
|
||||
"""
|
||||
if is_short_host_flag:
|
||||
entity = 'shortHostName'
|
||||
else:
|
||||
entity = 'portGroupName'
|
||||
if re.match(r'^' + entity + r'$', template):
|
||||
return True, '1'
|
||||
elif re.match(r'^' + entity + r'\[:\d+\]uuid\[:\d+\]$', template):
|
||||
return True, '2'
|
||||
elif re.match(r'^' + entity + r'\[-\d+:\]uuid\[:\d+\]$', template):
|
||||
return True, '3'
|
||||
elif re.match(r'^' + entity + r'\[:\d+\][a-zA-Z0-9_\\-]+$', template):
|
||||
return True, '4'
|
||||
elif re.match(r'^' + entity + r'\[-\d+:\][a-zA-Z0-9_\\-]+$',
|
||||
template):
|
||||
return True, '5'
|
||||
return False, '0'
|
||||
|
||||
def get_host_name_label(self, host_name_in, host_template):
|
||||
"""Get the host name label that will be used in PowerMax Objects
|
||||
|
||||
:param host_name_in: host name as portrayed in connector object
|
||||
:param host_template:
|
||||
:returns: host_name_out
|
||||
"""
|
||||
host_name_out = self.get_host_short_name(
|
||||
host_name_in)
|
||||
if host_template:
|
||||
short_host_name = self.get_host_short_name_from_fqn(
|
||||
host_name_in)
|
||||
host_name_out = (
|
||||
self.validate_short_host_name_from_template(
|
||||
host_template, short_host_name))
|
||||
return host_name_out
|
||||
|
||||
def get_port_name_label(self, port_name_in, port_group_template):
|
||||
"""Get the port name label that will be used in PowerMax Objects
|
||||
|
||||
:rtype: object
|
||||
:param host_name_in: host name as portrayed in connector object
|
||||
:param port_group_template: port group template
|
||||
:returns: port_name_out
|
||||
"""
|
||||
port_name_out = self.get_pg_short_name(port_name_in)
|
||||
if port_group_template:
|
||||
port_name_out = (
|
||||
self.validate_port_group_name_from_template(
|
||||
port_group_template, port_name_in))
|
||||
return port_name_out
|
||||
|
||||
@staticmethod
|
||||
def get_object_components(regex_str, input_str):
|
||||
"""Get components from input string.
|
||||
|
||||
:param regex_str: the regex -- str
|
||||
:param input_str: the input string -- str
|
||||
:returns: dict
|
||||
"""
|
||||
full_str = re.compile(regex_str)
|
||||
match = full_str.match(input_str)
|
||||
return match.groupdict() if match else None
|
||||
|
||||
def get_object_components_and_correct_host(self, regex_str, input_str):
|
||||
"""Get components from input string.
|
||||
|
||||
:param regex_str: the regex -- str
|
||||
:param input_str: the input string -- str
|
||||
:returns: object components -- dict
|
||||
"""
|
||||
object_dict = self.get_object_components(regex_str, input_str)
|
||||
if object_dict and 'host' in object_dict:
|
||||
if object_dict['host'].endswith('-'):
|
||||
object_dict['host'] = object_dict['host'][:-1]
|
||||
return object_dict
|
||||
|
||||
def get_possible_initiator_name(self, host_label, protocol):
|
||||
"""Get possible initiator name based on the host
|
||||
|
||||
:param host_label: the host label -- str
|
||||
:param protocol: the protocol -- str
|
||||
:returns: initiator_group_name -- str
|
||||
"""
|
||||
protocol = self.get_short_protocol_type(protocol)
|
||||
return ("OS-%(shortHostName)s-%(protocol)s-IG"
|
||||
% {'shortHostName': host_label,
|
||||
'protocol': protocol})
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Dell EMC PowerMax driver now faciliates the user to override the
|
||||
short host name and port group name seen in PowerMax masking view
|
||||
and storage view terminology. This means the user can 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 values.
|
Loading…
Reference in New Issue
Block a user