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_f = 'OS-fibre-PG'
|
||||||
port_group_name_i = 'OS-iscsi-PG'
|
port_group_name_i = 'OS-iscsi-PG'
|
||||||
masking_view_name_f = 'OS-HostX-F-OS-fibre-PG-MV'
|
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'
|
masking_view_name_i = 'OS-HostX-SRP_1-I-OS-iscsi-PG-MV'
|
||||||
initiatorgroup_name_f = 'OS-HostX-F-IG'
|
initiatorgroup_name_f = 'OS-HostX-F-IG'
|
||||||
initiatorgroup_name_i = 'OS-HostX-I-IG'
|
initiatorgroup_name_i = 'OS-HostX-I-IG'
|
||||||
@ -277,7 +278,8 @@ class PowerMaxData(object):
|
|||||||
'storagetype:storagegrouptags': u'good, comma, separated,list'}
|
'storagetype:storagegrouptags': u'good, comma, separated,list'}
|
||||||
vol_type_extra_specs_tags_bad = {
|
vol_type_extra_specs_tags_bad = {
|
||||||
'storagetype:storagegrouptags': u'B&d, [list]'}
|
'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 = deepcopy(extra_specs)
|
||||||
extra_specs_migrate[utils.PORTGROUPNAME] = port_group_name_f
|
extra_specs_migrate[utils.PORTGROUPNAME] = port_group_name_f
|
||||||
|
|
||||||
@ -401,7 +403,9 @@ class PowerMaxData(object):
|
|||||||
'storagegroup_name': storagegroup_name_f,
|
'storagegroup_name': storagegroup_name_f,
|
||||||
'volume_name': test_volume.name,
|
'volume_name': test_volume.name,
|
||||||
'workload': workload,
|
'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 = deepcopy(masking_view_dict)
|
||||||
masking_view_dict_no_slo.update(
|
masking_view_dict_no_slo.update(
|
||||||
|
@ -323,6 +323,10 @@ class FakeConfiguration(object):
|
|||||||
self.u4p_failover_timeout = value
|
self.u4p_failover_timeout = value
|
||||||
elif key == 'u4p_primary':
|
elif key == 'u4p_primary':
|
||||||
self.u4p_primary = value
|
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):
|
def safe_get(self, key):
|
||||||
try:
|
try:
|
||||||
|
@ -45,9 +45,11 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.mock_object(volume_utils, 'get_max_over_subscription_ratio',
|
self.mock_object(volume_utils, 'get_max_over_subscription_ratio',
|
||||||
return_value=1.0)
|
return_value=1.0)
|
||||||
configuration = tpfo.FakeConfiguration(
|
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',
|
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(
|
rest.PowerMaxRest._establish_rest_session = mock.Mock(
|
||||||
return_value=tpfo.FakeRequestsSession())
|
return_value=tpfo.FakeRequestsSession())
|
||||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||||
@ -72,7 +74,6 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
side_effect=[[], tpd.PowerMaxData.array_info_wl])
|
side_effect=[[], tpd.PowerMaxData.array_info_wl])
|
||||||
def test_gather_info_tests(self, mck_parse, mck_combo, mck_rest,
|
def test_gather_info_tests(self, mck_parse, mck_combo, mck_rest,
|
||||||
mck_nextgen, mck_ucode):
|
mck_nextgen, mck_ucode):
|
||||||
|
|
||||||
# Use-Case 1: Gather info no-opts
|
# Use-Case 1: Gather info no-opts
|
||||||
configuration = tpfo.FakeConfiguration(
|
configuration = tpfo.FakeConfiguration(
|
||||||
None, 'config_group', None, None)
|
None, 'config_group', None, None)
|
||||||
@ -83,6 +84,54 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.assertTrue(self.common.next_gen)
|
self.assertTrue(self.common.next_gen)
|
||||||
self.assertEqual(self.common.ucode_level, self.data.next_gen_ucode)
|
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):
|
def test_get_slo_workload_combinations_powermax(self):
|
||||||
array_info = self.common.get_attributes_from_cinder_config()
|
array_info = self.common.get_attributes_from_cinder_config()
|
||||||
finalarrayinfolist = self.common._get_slo_workload_combinations(
|
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)
|
array, volume, device_id, extra_specs, self.data.connector, False)
|
||||||
mock_rm.assert_called_once_with(
|
mock_rm.assert_called_once_with(
|
||||||
array, volume, device_id, volume_name,
|
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,
|
@mock.patch.object(masking.PowerMaxMasking,
|
||||||
'return_volume_to_fast_managed_group')
|
'return_volume_to_fast_managed_group')
|
||||||
@ -282,7 +332,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
array, volume, device_id, extra_specs, self.data.connector, True)
|
array, volume, device_id, extra_specs, self.data.connector, True)
|
||||||
mock_rm.assert_called_once_with(
|
mock_rm.assert_called_once_with(
|
||||||
array, volume, device_id, volume_name,
|
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()
|
mock_return.assert_called_once()
|
||||||
|
|
||||||
def test_unmap_lun(self):
|
def test_unmap_lun(self):
|
||||||
@ -296,7 +347,18 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.common._unmap_lun(volume, connector)
|
self.common._unmap_lun(volume, connector)
|
||||||
mock_remove.assert_called_once_with(
|
mock_remove.assert_called_once_with(
|
||||||
array, volume, device_id, extra_specs,
|
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')
|
@mock.patch.object(common.PowerMaxCommon, '_remove_members')
|
||||||
def test_unmap_lun_attachments(self, mock_rm):
|
def test_unmap_lun_attachments(self, mock_rm):
|
||||||
@ -327,7 +389,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.common._unmap_lun(volume, connector)
|
self.common._unmap_lun(volume, connector)
|
||||||
mock_remove.assert_called_once_with(
|
mock_remove.assert_called_once_with(
|
||||||
array, volume, device_id, extra_specs,
|
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):
|
def test_unmap_lun_not_mapped(self):
|
||||||
volume = self.data.test_volume
|
volume = self.data.test_volume
|
||||||
@ -350,7 +412,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.common._unmap_lun(volume, None)
|
self.common._unmap_lun(volume, None)
|
||||||
mock_remove.assert_called_once_with(
|
mock_remove.assert_called_once_with(
|
||||||
array, volume, device_id, extra_specs, None,
|
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):
|
def test_initialize_connection_already_mapped(self):
|
||||||
volume = self.data.test_volume
|
volume = self.data.test_volume
|
||||||
@ -613,7 +675,9 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
common.PowerMaxCommon, '_get_masking_views_from_volume',
|
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):
|
def test_find_host_lun_id_multiattach(self, mock_mask):
|
||||||
volume = self.data.test_volume
|
volume = self.data.test_volume
|
||||||
extra_specs = self.data.extra_specs
|
extra_specs = self.data.extra_specs
|
||||||
@ -631,6 +695,27 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.data.extra_specs, self.data.rep_extra_specs)
|
self.data.extra_specs, self.data.rep_extra_specs)
|
||||||
mock_tgt.assert_called_once()
|
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):
|
def test_get_masking_views_from_volume(self):
|
||||||
array = self.data.array
|
array = self.data.array
|
||||||
device_id = self.data.device_id
|
device_id = self.data.device_id
|
||||||
@ -692,6 +777,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
extra_specs[utils.WORKLOAD] = self.data.workload
|
extra_specs[utils.WORKLOAD] = self.data.workload
|
||||||
ref_mv_dict = self.data.masking_view_dict
|
ref_mv_dict = self.data.masking_view_dict
|
||||||
self.common.next_gen = False
|
self.common.next_gen = False
|
||||||
|
self.common.powermax_port_group_name_template = 'portGroupName'
|
||||||
masking_view_dict = self.common._populate_masking_dict(
|
masking_view_dict = self.common._populate_masking_dict(
|
||||||
volume, connector, extra_specs)
|
volume, connector, extra_specs)
|
||||||
self.assertEqual(ref_mv_dict, masking_view_dict)
|
self.assertEqual(ref_mv_dict, masking_view_dict)
|
||||||
@ -1142,6 +1228,31 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.data.test_volume, self.data.connector)
|
self.data.test_volume, self.data.connector)
|
||||||
self.assertEqual([self.data.wwnn1], metro_wwns)
|
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):
|
def test_get_port_group_from_masking_view(self):
|
||||||
array = self.data.array
|
array = self.data.array
|
||||||
maskingview_name = self.data.masking_view_name_f
|
maskingview_name = self.data.masking_view_name_f
|
||||||
@ -1464,7 +1575,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
|
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
|
||||||
return_value=tpd.PowerMaxData.sg_details[1])
|
return_value=tpd.PowerMaxData.sg_details[1])
|
||||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
@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',
|
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
|
||||||
return_value=True)
|
return_value=True)
|
||||||
@mock.patch.object(masking.PowerMaxMasking,
|
@mock.patch.object(masking.PowerMaxMasking,
|
||||||
@ -1500,7 +1611,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
rest.PowerMaxRest, 'get_volume',
|
rest.PowerMaxRest, 'get_volume',
|
||||||
return_value=tpd.PowerMaxData.volume_details_attached)
|
return_value=tpd.PowerMaxData.volume_details_attached)
|
||||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
@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(provision.PowerMaxProvision, 'create_storage_group')
|
||||||
@mock.patch.object(masking.PowerMaxMasking, 'add_child_sg_to_parent_sg')
|
@mock.patch.object(masking.PowerMaxMasking, 'add_child_sg_to_parent_sg')
|
||||||
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_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',
|
@mock.patch.object(rest.PowerMaxRest, 'get_storage_group',
|
||||||
side_effect=[tpd.PowerMaxData.sg_details[1], None])
|
side_effect=[tpd.PowerMaxData.sg_details[1], None])
|
||||||
@mock.patch.object(utils.PowerMaxUtils, 'get_child_sg_name',
|
@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',
|
@mock.patch.object(rest.PowerMaxRest, 'is_child_sg_in_parent_sg',
|
||||||
return_value=False)
|
return_value=False)
|
||||||
@mock.patch.object(masking.PowerMaxMasking,
|
@mock.patch.object(masking.PowerMaxMasking,
|
||||||
|
@ -148,6 +148,19 @@ class PowerMaxFCTest(test.TestCase):
|
|||||||
self.data.test_volume, self.data.connector)
|
self.data.test_volume, self.data.connector)
|
||||||
self.assertEqual({}, zoning_mappings)
|
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(
|
@mock.patch.object(
|
||||||
common.PowerMaxCommon, 'get_masking_views_from_volume',
|
common.PowerMaxCommon, 'get_masking_views_from_volume',
|
||||||
return_value=([tpd.PowerMaxData.masking_view_name_f], True))
|
return_value=([tpd.PowerMaxData.masking_view_name_f], True))
|
||||||
|
@ -833,7 +833,7 @@ class PowerMaxMaskingTest(test.TestCase):
|
|||||||
self.data.masking_view_name_f]
|
self.data.masking_view_name_f]
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
rest.PowerMaxRest, 'get_masking_views_by_initiator_group',
|
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.mask._last_volume_delete_initiator_group(
|
||||||
self.data.array, self.data.initiatorgroup_name_i,
|
self.data.array, self.data.initiatorgroup_name_i,
|
||||||
self.data.connector['host'])
|
self.data.connector['host'])
|
||||||
@ -974,7 +974,8 @@ class PowerMaxMaskingTest(test.TestCase):
|
|||||||
def test_pre_multiattach(self, mock_return):
|
def test_pre_multiattach(self, mock_return):
|
||||||
mv_dict = self.mask.pre_multiattach(
|
mv_dict = self.mask.pre_multiattach(
|
||||||
self.data.array, self.data.device_id,
|
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()
|
mock_return.assert_not_called()
|
||||||
self.assertEqual(self.data.storagegroup_name_f,
|
self.assertEqual(self.data.storagegroup_name_f,
|
||||||
mv_dict[utils.FAST_SG])
|
mv_dict[utils.FAST_SG])
|
||||||
@ -993,7 +994,8 @@ class PowerMaxMaskingTest(test.TestCase):
|
|||||||
return_value='DiamondDSS'):
|
return_value='DiamondDSS'):
|
||||||
self.mask.pre_multiattach(
|
self.mask.pre_multiattach(
|
||||||
self.data.array, self.data.device_id,
|
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(
|
utils.PowerMaxUtils.truncate_string.assert_called_once_with(
|
||||||
'DiamondDSS', 10)
|
'DiamondDSS', 10)
|
||||||
|
|
||||||
@ -1007,7 +1009,8 @@ class PowerMaxMaskingTest(test.TestCase):
|
|||||||
self, mock_return, mock_sg):
|
self, mock_return, mock_sg):
|
||||||
for x in range(0, 2):
|
for x in range(0, 2):
|
||||||
self.mask.return_volume_to_fast_managed_group(
|
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 = deepcopy(self.data.extra_specs)
|
||||||
no_slo_specs[utils.SLO] = None
|
no_slo_specs[utils.SLO] = None
|
||||||
self.mask.return_volume_to_fast_managed_group(
|
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.array, self.data.add_volume_sg_info_dict,
|
||||||
self.data.extra_specs_tags)
|
self.data.extra_specs_tags)
|
||||||
mock_except.assert_called()
|
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'}
|
'allow_extend': 'True'}
|
||||||
volume_utils.get_max_over_subscription_ratio = mock.Mock()
|
volume_utils.get_max_over_subscription_ratio = mock.Mock()
|
||||||
configuration = tpfo.FakeConfiguration(
|
configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
|
None, 'CommonReplicationTests', interval=1, retries=1,
|
||||||
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
|
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
|
||||||
san_password='smc', san_api_port=8443,
|
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
|
||||||
vmax_port_groups=[self.data.port_group_name_f],
|
vmax_port_groups=[self.data.port_group_name_f],
|
||||||
replication_device=self.replication_device)
|
replication_device=self.replication_device)
|
||||||
rest.PowerMaxRest._establish_rest_session = mock.Mock(
|
rest.PowerMaxRest._establish_rest_session = mock.Mock(
|
||||||
return_value=tpfo.FakeRequestsSession())
|
return_value=tpfo.FakeRequestsSession())
|
||||||
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
driver = fc.PowerMaxFCDriver(configuration=configuration)
|
||||||
iscsi_config = tpfo.FakeConfiguration(
|
iscsi_config = tpfo.FakeConfiguration(
|
||||||
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
|
None, 'CommonReplicationTests', interval=1, retries=1,
|
||||||
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
|
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
|
||||||
san_password='smc', san_api_port=8443,
|
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
|
||||||
vmax_port_groups=[self.data.port_group_name_i],
|
vmax_port_groups=[self.data.port_group_name_i],
|
||||||
replication_device=self.replication_device)
|
replication_device=self.replication_device)
|
||||||
iscsi_driver = iscsi.PowerMaxISCSIDriver(configuration=iscsi_config)
|
iscsi_driver = iscsi.PowerMaxISCSIDriver(configuration=iscsi_config)
|
||||||
@ -87,9 +87,9 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
'rdf_group_label': self.data.rdf_group_name,
|
'rdf_group_label': self.data.rdf_group_name,
|
||||||
'allow_extend': 'True', 'mode': 'async'}
|
'allow_extend': 'True', 'mode': 'async'}
|
||||||
async_configuration = tpfo.FakeConfiguration(
|
async_configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
|
None, 'CommonReplicationTests', interval=1, retries=1,
|
||||||
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
|
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
|
||||||
san_password='smc', san_api_port=8443,
|
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
|
||||||
vmax_port_groups=[self.data.port_group_name_f],
|
vmax_port_groups=[self.data.port_group_name_f],
|
||||||
replication_device=self.async_rep_device)
|
replication_device=self.async_rep_device)
|
||||||
self.async_driver = fc.PowerMaxFCDriver(
|
self.async_driver = fc.PowerMaxFCDriver(
|
||||||
@ -101,9 +101,9 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
'rdf_group_label': self.data.rdf_group_name,
|
'rdf_group_label': self.data.rdf_group_name,
|
||||||
'allow_extend': 'True', 'mode': 'metro'}
|
'allow_extend': 'True', 'mode': 'metro'}
|
||||||
metro_configuration = tpfo.FakeConfiguration(
|
metro_configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonReplicationTests', 1, 1, san_ip='1.1.1.1',
|
None, 'CommonReplicationTests', interval=1, retries=1,
|
||||||
san_login='smc', vmax_array=self.data.array, vmax_srp='SRP_1',
|
san_ip='1.1.1.1', san_login='smc', vmax_array=self.data.array,
|
||||||
san_password='smc', san_api_port=8443,
|
vmax_srp='SRP_1', san_password='smc', san_api_port=8443,
|
||||||
vmax_port_groups=[self.data.port_group_name_f],
|
vmax_port_groups=[self.data.port_group_name_f],
|
||||||
replication_device=self.metro_rep_device)
|
replication_device=self.metro_rep_device)
|
||||||
self.metro_driver = fc.PowerMaxFCDriver(
|
self.metro_driver = fc.PowerMaxFCDriver(
|
||||||
|
@ -450,6 +450,7 @@ class PowerMaxUtilsTest(test.TestCase):
|
|||||||
|
|
||||||
def test_get_child_sg_name(self):
|
def test_get_child_sg_name(self):
|
||||||
host_name = 'HostX'
|
host_name = 'HostX'
|
||||||
|
port_group_label = self.data.port_group_name_f
|
||||||
# Slo and rep enabled
|
# Slo and rep enabled
|
||||||
extra_specs1 = {
|
extra_specs1 = {
|
||||||
'pool_name': u'Diamond+DSS+SRP_1+000197800123',
|
'pool_name': u'Diamond+DSS+SRP_1+000197800123',
|
||||||
@ -463,23 +464,24 @@ class PowerMaxUtilsTest(test.TestCase):
|
|||||||
'rep_mode': 'Synchronous',
|
'rep_mode': 'Synchronous',
|
||||||
utils.PORTGROUPNAME: self.data.port_group_name_f}
|
utils.PORTGROUPNAME: self.data.port_group_name_f}
|
||||||
|
|
||||||
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
|
child_sg_name, do_disable_compression, rep_enabled = (
|
||||||
self.utils.get_child_sg_name(host_name, extra_specs1))
|
self.utils.get_child_sg_name(
|
||||||
|
host_name, extra_specs1, port_group_label))
|
||||||
re_name = self.data.storagegroup_name_f + '-RE'
|
re_name = self.data.storagegroup_name_f + '-RE'
|
||||||
self.assertEqual(re_name, child_sg_name)
|
self.assertEqual(re_name, child_sg_name)
|
||||||
# Disable compression
|
# Disable compression
|
||||||
extra_specs2 = deepcopy(self.data.extra_specs_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 = (
|
||||||
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
|
self.utils.get_child_sg_name(
|
||||||
self.utils.get_child_sg_name(host_name, extra_specs2))
|
host_name, extra_specs2, port_group_label))
|
||||||
cd_name = self.data.storagegroup_name_f + '-CD'
|
cd_name = self.data.storagegroup_name_f + '-CD'
|
||||||
self.assertEqual(cd_name, child_sg_name)
|
self.assertEqual(cd_name, child_sg_name)
|
||||||
# No slo
|
# No slo
|
||||||
extra_specs3 = deepcopy(self.data.extra_specs)
|
extra_specs3 = deepcopy(self.data.extra_specs)
|
||||||
extra_specs3[utils.SLO] = None
|
extra_specs3[utils.SLO] = None
|
||||||
extra_specs3[utils.PORTGROUPNAME] = self.data.port_group_name_f
|
child_sg_name, do_disable_compression, rep_enabled = (
|
||||||
child_sg_name, do_disable_compression, rep_enabled, pg_name = (
|
self.utils.get_child_sg_name(
|
||||||
self.utils.get_child_sg_name(host_name, extra_specs3))
|
host_name, extra_specs3, port_group_label))
|
||||||
self.assertEqual(self.data.no_slo_sg_name, child_sg_name)
|
self.assertEqual(self.data.no_slo_sg_name, child_sg_name)
|
||||||
|
|
||||||
def test_change_multiattach(self):
|
def test_change_multiattach(self):
|
||||||
@ -732,3 +734,285 @@ class PowerMaxUtilsTest(test.TestCase):
|
|||||||
input_list = 'one,two,three'
|
input_list = 'one,two,three'
|
||||||
output_string = self.utils.convert_list_to_string(input_list)
|
output_string = self.utils.convert_list_to_string(input_list)
|
||||||
self.assertEqual('one,two,three', output_string)
|
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.'),
|
'configured prior for server connection.'),
|
||||||
cfg.ListOpt(utils.POWERMAX_ARRAY_TAG_LIST,
|
cfg.ListOpt(utils.POWERMAX_ARRAY_TAG_LIST,
|
||||||
bounds=True,
|
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)
|
CONF.register_opts(powermax_opts, group=configuration.SHARED_CONF_GROUP)
|
||||||
@ -185,6 +191,8 @@ class PowerMaxCommon(object):
|
|||||||
self.rep_devices = []
|
self.rep_devices = []
|
||||||
self.failover = False
|
self.failover = False
|
||||||
self.powermax_array_tag_list = None
|
self.powermax_array_tag_list = None
|
||||||
|
self.powermax_short_host_name_template = None
|
||||||
|
self.powermax_port_group_name_template = None
|
||||||
|
|
||||||
# Gather environment info
|
# Gather environment info
|
||||||
self._get_replication_info()
|
self._get_replication_info()
|
||||||
@ -220,6 +228,10 @@ class PowerMaxCommon(object):
|
|||||||
utils.POWERMAX_SNAPVX_UNLINK_LIMIT)
|
utils.POWERMAX_SNAPVX_UNLINK_LIMIT)
|
||||||
self.powermax_array_tag_list = self.configuration.safe_get(
|
self.powermax_array_tag_list = self.configuration.safe_get(
|
||||||
utils.POWERMAX_ARRAY_TAG_LIST)
|
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.pool_info['backend_name'] = (
|
||||||
self.configuration.safe_get('volume_backend_name'))
|
self.configuration.safe_get('volume_backend_name'))
|
||||||
mosr = volume_utils.get_max_over_subscription_ratio(
|
mosr = volume_utils.get_max_over_subscription_ratio(
|
||||||
@ -673,7 +685,7 @@ class PowerMaxCommon(object):
|
|||||||
|
|
||||||
def _remove_members(self, array, volume, device_id,
|
def _remove_members(self, array, volume, device_id,
|
||||||
extra_specs, connector, is_multiattach,
|
extra_specs, connector, is_multiattach,
|
||||||
async_grp=None):
|
async_grp=None, host_template=None):
|
||||||
"""This method unmaps a volume from a host.
|
"""This method unmaps a volume from a host.
|
||||||
|
|
||||||
Removes volume from the storage group that belongs to a masking view.
|
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
|
reset = False if is_multiattach else True
|
||||||
self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
array, volume, device_id, volume_name,
|
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:
|
if is_multiattach:
|
||||||
self.masking.return_volume_to_fast_managed_group(
|
self.masking.return_volume_to_fast_managed_group(
|
||||||
array, device_id, extra_specs)
|
array, device_id, extra_specs)
|
||||||
@ -713,7 +726,7 @@ class PowerMaxCommon(object):
|
|||||||
async_grp = None
|
async_grp = None
|
||||||
LOG.info("Unmap volume: %(volume)s.", {'volume': volume})
|
LOG.info("Unmap volume: %(volume)s.", {'volume': volume})
|
||||||
if connector is not None:
|
if connector is not None:
|
||||||
host = self.utils.get_host_short_name(connector['host'])
|
host_name = connector.get('host')
|
||||||
attachment_list = volume.volume_attachment
|
attachment_list = volume.volume_attachment
|
||||||
LOG.debug("Volume attachment list: %(atl)s. "
|
LOG.debug("Volume attachment list: %(atl)s. "
|
||||||
"Attachment type: %(at)s",
|
"Attachment type: %(at)s",
|
||||||
@ -725,7 +738,7 @@ class PowerMaxCommon(object):
|
|||||||
if att_list is not None:
|
if att_list is not None:
|
||||||
host_list = [att.connector['host'] for att in att_list if
|
host_list = [att.connector['host'] for att in att_list if
|
||||||
att is not None and att.connector is not None]
|
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:
|
if current_host_occurances > 1:
|
||||||
LOG.info("Volume is attached to multiple instances on "
|
LOG.info("Volume is attached to multiple instances on "
|
||||||
"this host. Not removing the volume from the "
|
"this host. Not removing the volume from the "
|
||||||
@ -734,10 +747,10 @@ class PowerMaxCommon(object):
|
|||||||
else:
|
else:
|
||||||
LOG.warning("Cannot get host name from connector object - "
|
LOG.warning("Cannot get host name from connector object - "
|
||||||
"assuming force-detach.")
|
"assuming force-detach.")
|
||||||
host = None
|
host_name = None
|
||||||
|
|
||||||
device_info, is_multiattach = (
|
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:
|
if 'hostlunid' not in device_info:
|
||||||
LOG.info("Volume %s is not mapped. No volume to unmap.",
|
LOG.info("Volume %s is not mapped. No volume to unmap.",
|
||||||
volume_name)
|
volume_name)
|
||||||
@ -746,23 +759,25 @@ class PowerMaxCommon(object):
|
|||||||
if self.utils.does_vol_need_rdf_management_group(extra_specs):
|
if self.utils.does_vol_need_rdf_management_group(extra_specs):
|
||||||
async_grp = self.utils.get_async_rdf_managed_grp_name(
|
async_grp = self.utils.get_async_rdf_managed_grp_name(
|
||||||
self.rep_config)
|
self.rep_config)
|
||||||
self._remove_members(array, volume, device_info['device_id'],
|
self._remove_members(
|
||||||
extra_specs, connector, is_multiattach,
|
array, volume, device_info['device_id'], extra_specs, connector,
|
||||||
async_grp=async_grp)
|
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):
|
if self.utils.is_metro_device(self.rep_config, extra_specs):
|
||||||
# Need to remove from remote masking view
|
# Need to remove from remote masking view
|
||||||
device_info, __ = (self.find_host_lun_id(
|
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:
|
if 'hostlunid' in device_info:
|
||||||
self._remove_members(
|
self._remove_members(
|
||||||
rep_extra_specs[utils.ARRAY], volume,
|
rep_extra_specs[utils.ARRAY], volume,
|
||||||
device_info['device_id'], rep_extra_specs, connector,
|
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:
|
else:
|
||||||
# Make an attempt to clean up initiator group
|
# Make an attempt to clean up initiator group
|
||||||
self.masking.attempt_ig_cleanup(
|
self.masking.attempt_ig_cleanup(
|
||||||
connector, self.protocol, rep_extra_specs[utils.ARRAY],
|
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):
|
if is_multiattach and LOG.isEnabledFor(logging.DEBUG):
|
||||||
mv_list, sg_list = (
|
mv_list, sg_list = (
|
||||||
self._get_mvs_and_sgs_from_volume(
|
self._get_mvs_and_sgs_from_volume(
|
||||||
@ -817,7 +832,7 @@ class PowerMaxCommon(object):
|
|||||||
if self.utils.is_volume_failed_over(volume):
|
if self.utils.is_volume_failed_over(volume):
|
||||||
extra_specs = rep_extra_specs
|
extra_specs = rep_extra_specs
|
||||||
device_info_dict, is_multiattach = (
|
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(
|
masking_view_dict = self._populate_masking_dict(
|
||||||
volume, connector, extra_specs)
|
volume, connector, extra_specs)
|
||||||
masking_view_dict[utils.IS_MULTIATTACH] = is_multiattach
|
masking_view_dict[utils.IS_MULTIATTACH] = is_multiattach
|
||||||
@ -843,7 +858,7 @@ class PowerMaxCommon(object):
|
|||||||
device_info_dict['maskingview']))
|
device_info_dict['maskingview']))
|
||||||
if self.utils.is_metro_device(self.rep_config, extra_specs):
|
if self.utils.is_metro_device(self.rep_config, extra_specs):
|
||||||
remote_info_dict, is_multiattach = (
|
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))
|
extra_specs, rep_extra_specs))
|
||||||
if remote_info_dict.get('hostlunid') is None:
|
if remote_info_dict.get('hostlunid') is None:
|
||||||
# Need to attach on remote side
|
# 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.
|
# Find host lun id again after the volume is exported to the host.
|
||||||
|
|
||||||
device_info_dict, __ = self.find_host_lun_id(
|
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:
|
if 'hostlunid' not in device_info_dict:
|
||||||
# Did not successfully attach to host, so a rollback is required.
|
# Did not successfully attach to host, so a rollback is required.
|
||||||
error_message = (_("Error Attaching volume %(vol)s. Cannot "
|
error_message = (_("Error Attaching volume %(vol)s. Cannot "
|
||||||
@ -1499,13 +1514,23 @@ class PowerMaxCommon(object):
|
|||||||
device_id = self.get_remote_target_device(
|
device_id = self.get_remote_target_device(
|
||||||
extra_specs[utils.ARRAY], volume, device_id)[0]
|
extra_specs[utils.ARRAY], volume, device_id)[0]
|
||||||
extra_specs = rep_extra_specs
|
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:
|
if device_id:
|
||||||
array = extra_specs[utils.ARRAY]
|
array = extra_specs[utils.ARRAY]
|
||||||
# Return only masking views for this host
|
# Return only masking views for this host
|
||||||
host_maskingviews, all_masking_view_list = (
|
host_maskingviews, all_masking_view_list = (
|
||||||
self._get_masking_views_from_volume(
|
self._get_masking_views_from_volume(
|
||||||
array, device_id, host_name))
|
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:
|
for maskingview in host_maskingviews:
|
||||||
host_lun_id = self.rest.find_mv_connections_for_vol(
|
host_lun_id = self.rest.find_mv_connections_for_vol(
|
||||||
@ -1516,6 +1541,7 @@ class PowerMaxCommon(object):
|
|||||||
'array': array,
|
'array': array,
|
||||||
'device_id': device_id}
|
'device_id': device_id}
|
||||||
maskedvols = devicedict
|
maskedvols = devicedict
|
||||||
|
|
||||||
if not maskedvols:
|
if not maskedvols:
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Host lun id not found for volume: %(volume_name)s "
|
"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
|
:returns: masking view list, all masking view list
|
||||||
"""
|
"""
|
||||||
LOG.debug("Getting masking views from volume")
|
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)
|
mvs, __ = self._get_mvs_and_sgs_from_volume(array, device_id)
|
||||||
for mv in mvs:
|
return self._get_masking_views_from_volume_for_host(mvs, host)
|
||||||
all_masking_view_list.append(mv)
|
|
||||||
if host_compare:
|
def _get_masking_views_from_volume_for_host(
|
||||||
if host.lower() in mv.lower():
|
self, masking_views, host_name):
|
||||||
host_maskingview_list.append(mv)
|
"""Check all masking views for host_name
|
||||||
maskingview_list = (host_maskingview_list if host_compare else
|
|
||||||
all_masking_view_list)
|
:param masking_views: list of masking view
|
||||||
return maskingview_list, all_masking_view_list
|
: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):
|
def _get_mvs_and_sgs_from_volume(self, array, device_id):
|
||||||
"""Helper function to retrieve masking views and storage groups.
|
"""Helper function to retrieve masking views and storage groups.
|
||||||
@ -1664,7 +1701,10 @@ class PowerMaxCommon(object):
|
|||||||
raise exception.VolumeBackendAPIException(exception_message)
|
raise exception.VolumeBackendAPIException(exception_message)
|
||||||
|
|
||||||
protocol = self.utils.get_short_protocol_type(self.protocol)
|
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.SLO] = extra_specs[utils.SLO]
|
||||||
masking_view_dict[utils.WORKLOAD] = 'NONE' if self.next_gen else (
|
masking_view_dict[utils.WORKLOAD] = 'NONE' if self.next_gen else (
|
||||||
extra_specs[utils.WORKLOAD])
|
extra_specs[utils.WORKLOAD])
|
||||||
@ -1675,19 +1715,27 @@ class PowerMaxCommon(object):
|
|||||||
"in cinder.conf or as an extra spec. Port group "
|
"in cinder.conf or as an extra spec. Port group "
|
||||||
"cannot be left empty as creating a new masking "
|
"cannot be left empty as creating a new masking "
|
||||||
"view will fail.")
|
"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] = (
|
masking_view_dict[utils.PORTGROUPNAME] = (
|
||||||
extra_specs[utils.PORTGROUPNAME])
|
extra_specs[utils.PORTGROUPNAME])
|
||||||
masking_view_dict[utils.INITIATOR_CHECK] = (
|
masking_view_dict[utils.INITIATOR_CHECK] = (
|
||||||
self._get_initiator_check_flag())
|
self._get_initiator_check_flag())
|
||||||
|
|
||||||
child_sg_name, do_disable_compression, rep_enabled, short_pg_name = (
|
child_sg_name, do_disable_compression, rep_enabled = (
|
||||||
self.utils.get_child_sg_name(short_host_name, extra_specs))
|
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.DISABLECOMPRESSION] = do_disable_compression
|
||||||
masking_view_dict[utils.IS_RE] = rep_enabled
|
masking_view_dict[utils.IS_RE] = rep_enabled
|
||||||
mv_prefix = (
|
mv_prefix = (
|
||||||
"OS-%(shortHostName)s-%(protocol)s-%(pg)s"
|
"OS-%(shortHostName)s-%(protocol)s-%(pg)s"
|
||||||
% {'shortHostName': short_host_name,
|
% {'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
|
masking_view_dict[utils.SG_NAME] = child_sg_name
|
||||||
|
|
||||||
@ -2159,7 +2207,8 @@ class PowerMaxCommon(object):
|
|||||||
"""
|
"""
|
||||||
metro_wwns = []
|
metro_wwns = []
|
||||||
host = connector['host']
|
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)
|
extra_specs = self._initial_setup(volume)
|
||||||
rep_extra_specs = self._get_replication_extra_specs(
|
rep_extra_specs = self._get_replication_extra_specs(
|
||||||
extra_specs, self.rep_config)
|
extra_specs, self.rep_config)
|
||||||
@ -3443,8 +3492,12 @@ class PowerMaxCommon(object):
|
|||||||
{'volume_name': device_id})
|
{'volume_name': device_id})
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
target_sg_name, __, __, __ = self.utils.get_child_sg_name(
|
port_group_label = self.utils.get_port_name_label(
|
||||||
attached_host, target_extra_specs)
|
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)
|
target_sg = self.rest.get_storage_group(array, target_sg_name)
|
||||||
|
|
||||||
if not target_sg:
|
if not target_sg:
|
||||||
|
@ -117,6 +117,8 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
- Volume/Snapshot backed metadata inclusion
|
- Volume/Snapshot backed metadata inclusion
|
||||||
- Debug metadata compression and service level info fix
|
- Debug metadata compression and service level info fix
|
||||||
4.2.0 - Support of Unisphere storage group and array tags
|
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"
|
VERSION = "4.2.0"
|
||||||
@ -332,7 +334,8 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
"""
|
"""
|
||||||
loc = volume.provider_location
|
loc = volume.provider_location
|
||||||
name = ast.literal_eval(loc)
|
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 = {}
|
zoning_mappings = {}
|
||||||
try:
|
try:
|
||||||
array = name['array']
|
array = name['array']
|
||||||
@ -345,7 +348,14 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
|
|
||||||
masking_views, is_metro = (
|
masking_views, is_metro = (
|
||||||
self.common.get_masking_views_from_volume(
|
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:
|
if masking_views:
|
||||||
portgroup = (
|
portgroup = (
|
||||||
self.common.get_port_group_from_masking_view(
|
self.common.get_port_group_from_masking_view(
|
||||||
@ -379,7 +389,7 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
else:
|
else:
|
||||||
masking_views, __ = (
|
masking_views, __ = (
|
||||||
self.common.get_masking_views_from_volume(
|
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:
|
if masking_views:
|
||||||
metro_portgroup = (
|
metro_portgroup = (
|
||||||
self.common.get_port_group_from_masking_view(
|
self.common.get_port_group_from_masking_view(
|
||||||
|
@ -122,6 +122,8 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
- Volume/Snapshot backed metadata inclusion
|
- Volume/Snapshot backed metadata inclusion
|
||||||
- Debug metadata compression and service level info fix
|
- Debug metadata compression and service level info fix
|
||||||
4.2.0 - Support of Unisphere storage group and array tags
|
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"
|
VERSION = "4.2.0"
|
||||||
|
@ -240,8 +240,10 @@ class PowerMaxMasking(object):
|
|||||||
storagegroup_name = masking_view_dict[utils.SG_NAME]
|
storagegroup_name = masking_view_dict[utils.SG_NAME]
|
||||||
connector = masking_view_dict[utils.CONNECTOR]
|
connector = masking_view_dict[utils.CONNECTOR]
|
||||||
port_group_name = masking_view_dict[utils.PORTGROUPNAME]
|
port_group_name = masking_view_dict[utils.PORTGROUPNAME]
|
||||||
LOG.info("Port Group in masking view operation: %(port_group_name)s.",
|
LOG.info("Port Group in masking view operation: %(pg_name)s. "
|
||||||
{'port_group_name': port_group_name})
|
"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(
|
init_group_name, error_message = (self._get_or_create_initiator_group(
|
||||||
serial_number, init_group_name, connector, extra_specs))
|
serial_number, init_group_name, connector, extra_specs))
|
||||||
@ -1082,7 +1084,8 @@ class PowerMaxMasking(object):
|
|||||||
@coordination.synchronized("emc-vol-{device_id}")
|
@coordination.synchronized("emc-vol-{device_id}")
|
||||||
def remove_and_reset_members(
|
def remove_and_reset_members(
|
||||||
self, serial_number, volume, device_id, volume_name,
|
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.
|
"""This is called on a delete, unmap device or rollback.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
@ -1093,14 +1096,16 @@ class PowerMaxMasking(object):
|
|||||||
:param reset: reset, return to original SG (optional)
|
:param reset: reset, return to original SG (optional)
|
||||||
:param connector: the connector object (optional)
|
:param connector: the connector object (optional)
|
||||||
:param async_grp: the async rep group (optional)
|
:param async_grp: the async rep group (optional)
|
||||||
|
:param host_template: the host template (optional)
|
||||||
"""
|
"""
|
||||||
self._cleanup_deletion(
|
self._cleanup_deletion(
|
||||||
serial_number, volume, device_id, volume_name,
|
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(
|
def _cleanup_deletion(
|
||||||
self, serial_number, volume, device_id, volume_name,
|
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.
|
"""Prepare a volume for a delete operation.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
@ -1111,6 +1116,7 @@ class PowerMaxMasking(object):
|
|||||||
:param connector: the connector object
|
:param connector: the connector object
|
||||||
:param reset: flag to indicate if reset is required -- bool
|
:param reset: flag to indicate if reset is required -- bool
|
||||||
:param async_grp: the async rep group
|
:param async_grp: the async rep group
|
||||||
|
:param host_template: the host template (if it exists)
|
||||||
"""
|
"""
|
||||||
move = False
|
move = False
|
||||||
short_host_name = None
|
short_host_name = None
|
||||||
@ -1124,21 +1130,24 @@ class PowerMaxMasking(object):
|
|||||||
if len(storagegroup_names) == 1 and reset is True:
|
if len(storagegroup_names) == 1 and reset is True:
|
||||||
move = True
|
move = True
|
||||||
elif connector is not None:
|
elif connector is not None:
|
||||||
short_host_name = self.utils.get_host_short_name(
|
short_host_name = self.utils.get_host_name_label(
|
||||||
connector['host'])
|
connector.get('host'),
|
||||||
|
host_template) if connector.get('host') else None
|
||||||
move = reset
|
move = reset
|
||||||
if short_host_name:
|
if short_host_name:
|
||||||
for sg_name in storagegroup_names:
|
for sg_name in storagegroup_names:
|
||||||
if short_host_name in sg_name:
|
if short_host_name in sg_name:
|
||||||
self.remove_volume_from_sg(
|
self.remove_volume_from_sg(
|
||||||
serial_number, device_id, volume_name, sg_name,
|
serial_number, device_id, volume_name, sg_name,
|
||||||
extra_specs, connector, move)
|
extra_specs, connector, move,
|
||||||
|
host_template=host_template)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
for sg_name in storagegroup_names:
|
for sg_name in storagegroup_names:
|
||||||
self.remove_volume_from_sg(
|
self.remove_volume_from_sg(
|
||||||
serial_number, device_id, volume_name, sg_name,
|
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:
|
if reset is True and move is False:
|
||||||
self.add_volume_to_default_storage_group(
|
self.add_volume_to_default_storage_group(
|
||||||
serial_number, device_id, volume_name,
|
serial_number, device_id, volume_name,
|
||||||
@ -1146,7 +1155,7 @@ class PowerMaxMasking(object):
|
|||||||
|
|
||||||
def remove_volume_from_sg(
|
def remove_volume_from_sg(
|
||||||
self, serial_number, device_id, vol_name, storagegroup_name,
|
self, serial_number, device_id, vol_name, storagegroup_name,
|
||||||
extra_specs, connector=None, move=False):
|
extra_specs, connector=None, move=False, host_template=None):
|
||||||
"""Remove a volume from a storage group.
|
"""Remove a volume from a storage group.
|
||||||
|
|
||||||
:param serial_number: the array serial number
|
:param serial_number: the array serial number
|
||||||
@ -1156,6 +1165,7 @@ class PowerMaxMasking(object):
|
|||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
:param connector: the connector object
|
:param connector: the connector object
|
||||||
:param move: flag to indicate if move should be used instead of remove
|
: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(
|
masking_list = self.rest.get_masking_views_from_storage_group(
|
||||||
serial_number, storagegroup_name)
|
serial_number, storagegroup_name)
|
||||||
@ -1179,7 +1189,7 @@ class PowerMaxMasking(object):
|
|||||||
# Last volume in the storage group - delete sg.
|
# Last volume in the storage group - delete sg.
|
||||||
self._last_vol_in_sg(
|
self._last_vol_in_sg(
|
||||||
serial_number, device_id, vol_name, sg_name,
|
serial_number, device_id, vol_name, sg_name,
|
||||||
extra_specs, move)
|
extra_specs, move, host_template=host_template)
|
||||||
else:
|
else:
|
||||||
# Not the last volume so remove it from storage group
|
# Not the last volume so remove it from storage group
|
||||||
self._multiple_vols_in_sg(
|
self._multiple_vols_in_sg(
|
||||||
@ -1221,7 +1231,8 @@ class PowerMaxMasking(object):
|
|||||||
# Last volume in the storage group - delete sg.
|
# Last volume in the storage group - delete sg.
|
||||||
self._last_vol_in_sg(
|
self._last_vol_in_sg(
|
||||||
serial_number, device_id, vol_name, sg_name,
|
serial_number, device_id, vol_name, sg_name,
|
||||||
extra_specs, move, connector)
|
extra_specs, move, connector,
|
||||||
|
host_template=host_template)
|
||||||
else:
|
else:
|
||||||
# Not the last volume so remove it from storage group
|
# Not the last volume so remove it from storage group
|
||||||
self._multiple_vols_in_sg(
|
self._multiple_vols_in_sg(
|
||||||
@ -1235,8 +1246,9 @@ class PowerMaxMasking(object):
|
|||||||
return do_remove_volume_from_sg(masking_name, storagegroup_name,
|
return do_remove_volume_from_sg(masking_name, storagegroup_name,
|
||||||
parent_sg_name, serial_number)
|
parent_sg_name, serial_number)
|
||||||
|
|
||||||
def _last_vol_in_sg(self, serial_number, device_id, volume_name,
|
def _last_vol_in_sg(
|
||||||
storagegroup_name, extra_specs, move, connector=None):
|
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.
|
"""Steps if the volume is the last in a storage group.
|
||||||
|
|
||||||
1. Check if the volume is in a masking view.
|
1. Check if the volume is in a masking view.
|
||||||
@ -1255,6 +1267,7 @@ class PowerMaxMasking(object):
|
|||||||
:param extra_specs: extra specifications
|
:param extra_specs: extra specifications
|
||||||
:param move: flag to indicate a move instead of remove
|
:param move: flag to indicate a move instead of remove
|
||||||
:param connector: the connector object
|
:param connector: the connector object
|
||||||
|
:param host_template: the host template (if it exists)
|
||||||
:returns: status -- bool
|
:returns: status -- bool
|
||||||
"""
|
"""
|
||||||
LOG.debug("Only one volume remains in storage group "
|
LOG.debug("Only one volume remains in storage group "
|
||||||
@ -1269,7 +1282,8 @@ class PowerMaxMasking(object):
|
|||||||
else:
|
else:
|
||||||
status = self._last_vol_masking_views(
|
status = self._last_vol_masking_views(
|
||||||
serial_number, storagegroup_name, maskingview_list,
|
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
|
return status
|
||||||
|
|
||||||
def _last_vol_no_masking_views(self, serial_number, storagegroup_name,
|
def _last_vol_no_masking_views(self, serial_number, storagegroup_name,
|
||||||
@ -1314,7 +1328,8 @@ class PowerMaxMasking(object):
|
|||||||
|
|
||||||
def _last_vol_masking_views(
|
def _last_vol_masking_views(
|
||||||
self, serial_number, storagegroup_name, maskingview_list,
|
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.
|
"""Remove the last vol from an sg associated with masking views.
|
||||||
|
|
||||||
Helper function for removing the last vol from a storage group
|
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 volume_name: the volume name
|
||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
:param move: flag to indicate a move instead of remove
|
:param move: flag to indicate a move instead of remove
|
||||||
|
:param host_template: the host template (if it exists)
|
||||||
:returns: status -- bool
|
:returns: status -- bool
|
||||||
"""
|
"""
|
||||||
status = False
|
status = False
|
||||||
@ -1336,7 +1352,8 @@ class PowerMaxMasking(object):
|
|||||||
if num_vols_in_mv == 1:
|
if num_vols_in_mv == 1:
|
||||||
self._delete_mv_ig_and_sg(
|
self._delete_mv_ig_and_sg(
|
||||||
serial_number, device_id, mv, storagegroup_name,
|
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:
|
else:
|
||||||
self._remove_last_vol_and_delete_sg(
|
self._remove_last_vol_and_delete_sg(
|
||||||
serial_number, device_id, volume_name,
|
serial_number, device_id, volume_name,
|
||||||
@ -1435,7 +1452,7 @@ class PowerMaxMasking(object):
|
|||||||
|
|
||||||
def _delete_mv_ig_and_sg(
|
def _delete_mv_ig_and_sg(
|
||||||
self, serial_number, device_id, masking_view, storagegroup_name,
|
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.
|
"""Delete the masking view, storage groups and initiator group.
|
||||||
|
|
||||||
:param serial_number: array serial number
|
:param serial_number: array serial number
|
||||||
@ -1446,15 +1463,15 @@ class PowerMaxMasking(object):
|
|||||||
:param connector: the connector object
|
:param connector: the connector object
|
||||||
:param move: flag to indicate if the volume should be moved
|
:param move: flag to indicate if the volume should be moved
|
||||||
:param extra_specs: the extra specifications
|
: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(
|
initiatorgroup = self.rest.get_element_from_masking_view(
|
||||||
serial_number, masking_view, host=True)
|
serial_number, masking_view, host=True)
|
||||||
self._last_volume_delete_masking_view(serial_number, masking_view)
|
self._last_volume_delete_masking_view(serial_number, masking_view)
|
||||||
self._last_volume_delete_initiator_group(
|
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(
|
self._delete_cascaded_storage_groups(
|
||||||
serial_number, storagegroup_name, parent_sg_name,
|
serial_number, storagegroup_name, parent_sg_name,
|
||||||
extra_specs, device_id, move)
|
extra_specs, device_id, move)
|
||||||
@ -1643,7 +1660,8 @@ class PowerMaxMasking(object):
|
|||||||
self.rest.delete_storage_group(serial_number, storagegroup_name)
|
self.rest.delete_storage_group(serial_number, storagegroup_name)
|
||||||
|
|
||||||
def _last_volume_delete_initiator_group(
|
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.
|
||||||
|
|
||||||
Delete the Initiator group if it has been created by the PowerMax
|
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 serial_number: the array serial number
|
||||||
:param initiatorgroup_name: initiator group name
|
:param initiatorgroup_name: initiator group name
|
||||||
:param host: the short name of the host
|
: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:
|
if host is not None:
|
||||||
protocol = self.utils.get_short_protocol_type(self.protocol)
|
host_label = (self.utils.get_host_name_label(
|
||||||
default_ig_name = ("OS-%(shortHostName)s-%(protocol)s-IG"
|
host, host_template) if host else None)
|
||||||
% {'shortHostName': host,
|
default_ig_name = self.utils.get_possible_initiator_name(
|
||||||
'protocol': protocol})
|
host_label, self.protocol)
|
||||||
|
|
||||||
if initiatorgroup_name == default_ig_name:
|
if initiatorgroup_name == default_ig_name:
|
||||||
maskingview_names = (
|
is_deleted = _do_delete_initiator_group(
|
||||||
self.rest.get_masking_views_by_initiator_group(
|
serial_number, initiatorgroup_name)
|
||||||
serial_number, initiatorgroup_name))
|
if not is_deleted:
|
||||||
if not maskingview_names:
|
host_label = (self.utils.get_host_short_name(
|
||||||
@coordination.synchronized(
|
host) if host else None)
|
||||||
"emc-ig-{ig_name}-{serial_number}")
|
default_ig_name = self.utils.get_possible_initiator_name(
|
||||||
def _delete_ig(ig_name, serial_number):
|
host_label, self.protocol)
|
||||||
# Check initiator group hasn't been recently deleted
|
if initiatorgroup_name == default_ig_name:
|
||||||
ig_details = self.rest.get_initiator_group(
|
_do_delete_initiator_group(
|
||||||
serial_number, ig_name)
|
serial_number, initiatorgroup_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)})
|
|
||||||
else:
|
else:
|
||||||
LOG.warning("Initiator group %(ig_name)s was "
|
LOG.warning("Initiator group %(ig_name)s was "
|
||||||
"not created by the PowerMax driver so will "
|
"not created by the PowerMax driver so will "
|
||||||
@ -1715,19 +1751,13 @@ class PowerMaxMasking(object):
|
|||||||
for sg in sg_list.get('storageGroupId', []):
|
for sg in sg_list.get('storageGroupId', []):
|
||||||
if slo_wl_combo in sg:
|
if slo_wl_combo in sg:
|
||||||
fast_source_sg_name = sg
|
fast_source_sg_name = sg
|
||||||
masking_view_name = (
|
short_host_name, port_group_label = (
|
||||||
self.rest.get_masking_views_from_storage_group(
|
self._get_host_and_port_group_labels(
|
||||||
serial_number, fast_source_sg_name))[0]
|
serial_number, fast_source_sg_name))
|
||||||
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
|
|
||||||
no_slo_extra_specs = deepcopy(extra_specs)
|
no_slo_extra_specs = deepcopy(extra_specs)
|
||||||
no_slo_extra_specs[utils.SLO] = None
|
no_slo_extra_specs[utils.SLO] = None
|
||||||
no_slo_sg_name, __, __, __ = self.utils.get_child_sg_name(
|
no_slo_sg_name, __, __ = self.utils.get_child_sg_name(
|
||||||
short_host_name, no_slo_extra_specs)
|
short_host_name, no_slo_extra_specs, port_group_label)
|
||||||
source_sg_details = self.rest.get_storage_group(
|
source_sg_details = self.rest.get_storage_group(
|
||||||
serial_number, fast_source_sg_name)
|
serial_number, fast_source_sg_name)
|
||||||
parent_sg_name = source_sg_details[
|
parent_sg_name = source_sg_details[
|
||||||
@ -1763,6 +1793,34 @@ class PowerMaxMasking(object):
|
|||||||
message=exception_message)
|
message=exception_message)
|
||||||
return mv_dict
|
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(
|
def return_volume_to_fast_managed_group(
|
||||||
self, serial_number, device_id, extra_specs):
|
self, serial_number, device_id, extra_specs):
|
||||||
"""Return a volume to a fast managed group if slo is set.
|
"""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', []):
|
for sg in sg_list.get('storageGroupId', []):
|
||||||
if slo_wl_combo in sg:
|
if slo_wl_combo in sg:
|
||||||
no_slo_sg_name = sg
|
no_slo_sg_name = sg
|
||||||
masking_view_name = (
|
short_host_name, port_group_label = (
|
||||||
self.rest.get_masking_views_from_storage_group(
|
self._get_host_and_port_group_labels(
|
||||||
serial_number, no_slo_sg_name))[0]
|
serial_number, no_slo_sg_name))
|
||||||
port_group_name = self.rest.get_element_from_masking_view(
|
fast_sg_name, _, _ = self.utils.get_child_sg_name(
|
||||||
serial_number, masking_view_name, portgroup=True)
|
short_host_name, extra_specs, port_group_label)
|
||||||
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)
|
|
||||||
source_sg_details = self.rest.get_storage_group(
|
source_sg_details = self.rest.get_storage_group(
|
||||||
serial_number, no_slo_sg_name)
|
serial_number, no_slo_sg_name)
|
||||||
parent_sg_name = source_sg_details[
|
parent_sg_name = source_sg_details[
|
||||||
@ -1863,20 +1914,24 @@ class PowerMaxMasking(object):
|
|||||||
self.rest.delete_storage_group(
|
self.rest.delete_storage_group(
|
||||||
serial_number, child_sg_name)
|
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
|
"""Attempt to cleanup an orphan initiator group
|
||||||
|
|
||||||
:param connector: connector object
|
:param connector: connector object
|
||||||
:param protocol: iscsi or fc
|
:param protocol: iscsi or fc
|
||||||
:param serial_number: extra the array serial number
|
:param serial_number: extra the array serial number
|
||||||
:param force: flag to indicate if operation should be forced
|
: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)
|
protocol = self.utils.get_short_protocol_type(protocol)
|
||||||
host_name = connector['host']
|
host_name = connector.get('host')
|
||||||
short_host_name = self.utils.get_host_short_name(host_name)
|
|
||||||
init_group = (
|
host_label = self.utils.get_host_name_label(
|
||||||
("OS-%(shortHostName)s-%(protocol)s-IG"
|
host_name, host_template=host_template)
|
||||||
% {'shortHostName': short_host_name,
|
initiator_group_name = self.utils.get_possible_initiator_name(
|
||||||
'protocol': protocol}))
|
host_label, protocol)
|
||||||
|
|
||||||
self._check_ig_rollback(
|
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,
|
parent_storage_group=parent_storage_group,
|
||||||
initiator_group=initiator_group,
|
initiator_group=initiator_group,
|
||||||
port_group=port_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),
|
identifier_name=self.utils.get_volume_element_name(volume.id),
|
||||||
openstack_name=volume.display_name,
|
openstack_name=volume.display_name,
|
||||||
mv_list=mv_list, sg_list=sg_list,
|
mv_list=mv_list, sg_list=sg_list,
|
||||||
|
@ -44,6 +44,8 @@ TRUNCATE_5 = 5
|
|||||||
TRUNCATE_27 = 27
|
TRUNCATE_27 = 27
|
||||||
UCODE_5978_ELMSR = 221
|
UCODE_5978_ELMSR = 221
|
||||||
UCODE_5978 = 5978
|
UCODE_5978 = 5978
|
||||||
|
UPPER_HOST_CHARS = 16
|
||||||
|
UPPER_PORT_GROUP_CHARS = 12
|
||||||
|
|
||||||
ARRAY = 'array'
|
ARRAY = 'array'
|
||||||
SLO = 'slo'
|
SLO = 'slo'
|
||||||
@ -80,6 +82,7 @@ DEFAULT_PORT = 8443
|
|||||||
CLONE_SNAPSHOT_NAME = "snapshot_for_clone"
|
CLONE_SNAPSHOT_NAME = "snapshot_for_clone"
|
||||||
STORAGE_GROUP_TAGS = 'storagetype:storagegrouptags'
|
STORAGE_GROUP_TAGS = 'storagetype:storagegrouptags'
|
||||||
TAG_LIST = 'tag_list'
|
TAG_LIST = 'tag_list'
|
||||||
|
USED_HOST_NAME = "used_host_name"
|
||||||
|
|
||||||
# Multiattach constants
|
# Multiattach constants
|
||||||
IS_MULTIATTACH = 'multiattach'
|
IS_MULTIATTACH = 'multiattach'
|
||||||
@ -112,6 +115,9 @@ POWERMAX_SERVICE_LEVEL = 'powermax_service_level'
|
|||||||
POWERMAX_PORT_GROUPS = 'powermax_port_groups'
|
POWERMAX_PORT_GROUPS = 'powermax_port_groups'
|
||||||
POWERMAX_SNAPVX_UNLINK_LIMIT = 'powermax_snapvx_unlink_limit'
|
POWERMAX_SNAPVX_UNLINK_LIMIT = 'powermax_snapvx_unlink_limit'
|
||||||
POWERMAX_ARRAY_TAG_LIST = 'powermax_array_tag_list'
|
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):
|
class PowerMaxUtils(object):
|
||||||
@ -127,6 +133,20 @@ class PowerMaxUtils(object):
|
|||||||
def get_host_short_name(self, host_name):
|
def get_host_short_name(self, host_name):
|
||||||
"""Returns the short name for a given qualified 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
|
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
|
and returns part before the dot. If there is no dot in the host name
|
||||||
the full host name is returned.
|
the full host name is returned.
|
||||||
@ -139,7 +159,7 @@ class PowerMaxUtils(object):
|
|||||||
else:
|
else:
|
||||||
short_host_name = host_name
|
short_host_name = host_name
|
||||||
|
|
||||||
return self.generate_unique_trunc_host(short_host_name)
|
return short_host_name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_volumetype_extra_specs(volume, volume_type_id=None):
|
def get_volumetype_extra_specs(volume, volume_type_id=None):
|
||||||
@ -286,15 +306,12 @@ class PowerMaxUtils(object):
|
|||||||
:param host_name: long host name
|
:param host_name: long host name
|
||||||
:returns: truncated host name
|
:returns: truncated host name
|
||||||
"""
|
"""
|
||||||
if host_name and len(host_name) > 16:
|
if host_name and len(host_name) > UPPER_HOST_CHARS:
|
||||||
host_name = host_name.lower()
|
uuid = self.get_uuid_of_input(host_name)
|
||||||
m = hashlib.md5()
|
|
||||||
m.update(host_name.encode('utf-8'))
|
|
||||||
uuid = m.hexdigest()
|
|
||||||
new_name = ("%(host)s%(uuid)s"
|
new_name = ("%(host)s%(uuid)s"
|
||||||
% {'host': host_name[-6:],
|
% {'host': host_name[-6:],
|
||||||
'uuid': uuid})
|
'uuid': uuid})
|
||||||
host_name = self.truncate_string(new_name, 16)
|
host_name = self.truncate_string(new_name, UPPER_HOST_CHARS)
|
||||||
return host_name
|
return host_name
|
||||||
|
|
||||||
def get_pg_short_name(self, portgroup_name):
|
def get_pg_short_name(self, portgroup_name):
|
||||||
@ -303,17 +320,27 @@ class PowerMaxUtils(object):
|
|||||||
:param portgroup_name: long portgroup_name
|
:param portgroup_name: long portgroup_name
|
||||||
:returns: truncated portgroup_name
|
:returns: truncated portgroup_name
|
||||||
"""
|
"""
|
||||||
if portgroup_name and len(portgroup_name) > 12:
|
if portgroup_name and len(portgroup_name) > UPPER_PORT_GROUP_CHARS:
|
||||||
portgroup_name = portgroup_name.lower()
|
uuid = self.get_uuid_of_input(portgroup_name)
|
||||||
m = hashlib.md5()
|
|
||||||
m.update(portgroup_name.encode('utf-8'))
|
|
||||||
uuid = m.hexdigest()
|
|
||||||
new_name = ("%(pg)s%(uuid)s"
|
new_name = ("%(pg)s%(uuid)s"
|
||||||
% {'pg': portgroup_name[-6:],
|
% {'pg': portgroup_name[-6:],
|
||||||
'uuid': uuid})
|
'uuid': uuid})
|
||||||
portgroup_name = self.truncate_string(new_name, 12)
|
portgroup_name = self.truncate_string(
|
||||||
|
new_name, UPPER_PORT_GROUP_CHARS)
|
||||||
return portgroup_name
|
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
|
@staticmethod
|
||||||
def get_default_oversubscription_ratio(max_over_sub_ratio):
|
def get_default_oversubscription_ratio(max_over_sub_ratio):
|
||||||
"""Override ratio if necessary.
|
"""Override ratio if necessary.
|
||||||
@ -732,6 +759,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Get the name of the default sg from the extra specs.
|
"""Get the name of the default sg from the extra specs.
|
||||||
|
|
||||||
:param extra_specs: extra specs
|
:param extra_specs: extra specs
|
||||||
|
:param rep_mode: replication mode
|
||||||
:returns: default sg - string
|
:returns: default sg - string
|
||||||
"""
|
"""
|
||||||
do_disable_compression = self.is_compression_disabled(
|
do_disable_compression = self.is_compression_disabled(
|
||||||
@ -763,7 +791,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Get the temporary group name used for failover.
|
"""Get the temporary group name used for failover.
|
||||||
|
|
||||||
:param rep_config: the replication config
|
:param rep_config: the replication config
|
||||||
:return: temp_grp_name
|
:returns: temp_grp_name
|
||||||
"""
|
"""
|
||||||
temp_grp_name = ("OS-%(rdf)s-temp-rdf-sg"
|
temp_grp_name = ("OS-%(rdf)s-temp-rdf-sg"
|
||||||
% {'rdf': rep_config['rdf_group_label']})
|
% {'rdf': rep_config['rdf_group_label']})
|
||||||
@ -771,15 +799,15 @@ class PowerMaxUtils(object):
|
|||||||
{'name': temp_grp_name})
|
{'name': temp_grp_name})
|
||||||
return 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.
|
"""Get the child storage group name for a masking view.
|
||||||
|
|
||||||
:param host_name: the short host name
|
:param host_name: the short host name
|
||||||
:param extra_specs: the extra specifications
|
: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
|
do_disable_compression = False
|
||||||
pg_name = self.get_pg_short_name(extra_specs[PORTGROUPNAME])
|
|
||||||
rep_enabled = self.is_replication_enabled(extra_specs)
|
rep_enabled = self.is_replication_enabled(extra_specs)
|
||||||
if extra_specs[SLO]:
|
if extra_specs[SLO]:
|
||||||
slo_wl_combo = self.truncate_string(
|
slo_wl_combo = self.truncate_string(
|
||||||
@ -790,7 +818,7 @@ class PowerMaxUtils(object):
|
|||||||
% {'shortHostName': host_name,
|
% {'shortHostName': host_name,
|
||||||
'srpName': unique_name,
|
'srpName': unique_name,
|
||||||
'combo': slo_wl_combo,
|
'combo': slo_wl_combo,
|
||||||
'pg': pg_name})
|
'pg': port_group_label})
|
||||||
do_disable_compression = self.is_compression_disabled(
|
do_disable_compression = self.is_compression_disabled(
|
||||||
extra_specs)
|
extra_specs)
|
||||||
if do_disable_compression:
|
if do_disable_compression:
|
||||||
@ -799,11 +827,11 @@ class PowerMaxUtils(object):
|
|||||||
else:
|
else:
|
||||||
child_sg_name = (
|
child_sg_name = (
|
||||||
"OS-%(shortHostName)s-No_SLO-%(pg)s"
|
"OS-%(shortHostName)s-No_SLO-%(pg)s"
|
||||||
% {'shortHostName': host_name, 'pg': pg_name})
|
% {'shortHostName': host_name, 'pg': port_group_label})
|
||||||
if rep_enabled:
|
if rep_enabled:
|
||||||
rep_mode = extra_specs.get(REP_MODE, None)
|
rep_mode = extra_specs.get(REP_MODE, None)
|
||||||
child_sg_name += self.get_replication_prefix(rep_mode)
|
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
|
@staticmethod
|
||||||
def change_multiattach(extra_specs, new_type_extra_specs):
|
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 extra_specs: the source type extra specs
|
||||||
:param new_type_extra_specs: the target 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(
|
is_src_multiattach = volume_utils.is_boolean_str(
|
||||||
extra_specs.get('multiattach'))
|
extra_specs.get('multiattach'))
|
||||||
@ -824,7 +852,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Check if a volume with verbose description is valid for management.
|
"""Check if a volume with verbose description is valid for management.
|
||||||
|
|
||||||
:param source_vol: the verbose volume dict
|
:param source_vol: the verbose volume dict
|
||||||
:return: bool True/False
|
:returns: bool True/False
|
||||||
"""
|
"""
|
||||||
vol_head = source_vol['volumeHeader']
|
vol_head = source_vol['volumeHeader']
|
||||||
|
|
||||||
@ -865,7 +893,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Check if a volume with snapshot description is valid for management.
|
"""Check if a volume with snapshot description is valid for management.
|
||||||
|
|
||||||
:param source_vol: the verbose volume dict
|
:param source_vol: the verbose volume dict
|
||||||
:return: bool True/False
|
:returns: bool True/False
|
||||||
"""
|
"""
|
||||||
vol_head = source_vol['volumeHeader']
|
vol_head = source_vol['volumeHeader']
|
||||||
|
|
||||||
@ -902,7 +930,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Parse a hostname from a storage group ID.
|
"""Parse a hostname from a storage group ID.
|
||||||
|
|
||||||
:param device_info: the device info dict
|
:param device_info: the device info dict
|
||||||
:return: str -- the attached hostname
|
:returns: str -- the attached hostname
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sg_id = device_info.get("storageGroupId")[0]
|
sg_id = device_info.get("storageGroupId")[0]
|
||||||
@ -962,7 +990,7 @@ class PowerMaxUtils(object):
|
|||||||
"""Compare number of cylinders of source and target.
|
"""Compare number of cylinders of source and target.
|
||||||
|
|
||||||
:param cylinders_source: number of cylinders on source
|
: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):
|
if float(cylinders_source) > float(cylinder_target):
|
||||||
exception_message = (
|
exception_message = (
|
||||||
@ -1054,3 +1082,330 @@ class PowerMaxUtils(object):
|
|||||||
"""
|
"""
|
||||||
return ','.join(map(str, list_input)) if isinstance(
|
return ','.join(map(str, list_input)) if isinstance(
|
||||||
list_input, list) else list_input
|
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…
x
Reference in New Issue
Block a user