Merge "PowerMax Driver - Port status check"

This commit is contained in:
Zuul 2021-03-02 18:04:08 +00:00 committed by Gerrit Code Review
commit afa04aa093
6 changed files with 153 additions and 7 deletions

View File

@ -1645,3 +1645,36 @@ class PowerMaxData(object):
'SourceDeviceID': device_id,
'SourceDeviceLabel': device_label,
'SnapIdList': [snap_id]}
port_info = {
"symmetrixPort": {
"director_status": "Online",
"maskingview": [
"Test_MV",
],
"port_status": "ON",
"symmetrixPortKey": {
"directorId": "FA-1D",
"portId": "4"
},
"portgroup": [
"Test_PG"
]
}
}
port_info_off = deepcopy(port_info)
port_info_off.update({"symmetrixPort": {
"director_status": "Offline",
"port_status": "OFF"}})
port_info_no_status = deepcopy(port_info)
port_info_no_status.update({"symmetrixPort": {
"symmetrixPortKey": {
"directorId": "FA-1D",
"portId": "4"
}
}})
port_info_no_details = deepcopy(port_info)
port_info_no_details.pop("symmetrixPort")

View File

@ -80,9 +80,11 @@ class PowerMaxMaskingTest(test.TestCase):
self.driver.masking._sanity_port_group_check,
None, self.data.array)
@mock.patch.object(
masking.PowerMaxMasking, '_check_director_and_port_status')
@mock.patch.object(rest.PowerMaxRest, 'get_portgroup',
return_value=tpd.PowerMaxData.portgroup)
def test_sanity_port_group_check(self, mock_pg):
def test_sanity_port_group_check(self, mock_pg, mock_check):
self.driver.masking._sanity_port_group_check(
self.data.port_group_name_f, self.data.array)
@ -196,6 +198,8 @@ class PowerMaxMaskingTest(test.TestCase):
self.data.storagegroup_name_i, self.data.storagegroup_name_f,
self.data.extra_specs, True)
@mock.patch.object(
masking.PowerMaxMasking, '_check_director_and_port_status')
@mock.patch.object(rest.PowerMaxRest, 'get_masking_view',
side_effect=[tpd.PowerMaxData.maskingview,
tpd.PowerMaxData.maskingview, None])
@ -206,7 +210,7 @@ class PowerMaxMaskingTest(test.TestCase):
@mock.patch.object(masking.PowerMaxMasking, '_create_new_masking_view',
return_value=None)
def test_get_or_create_masking_view(self, mock_create_mv, mock_validate_mv,
mock_get_mv):
mock_get_mv, mock_check):
for x in range(0, 3):
self.driver.masking._get_or_create_masking_view(
self.data.array, self.maskingviewdict,
@ -358,11 +362,13 @@ class PowerMaxMaskingTest(test.TestCase):
self.assertEqual(7, mock_get_sg.call_count)
self.assertEqual(1, mock_move.call_count)
@mock.patch.object(
masking.PowerMaxMasking, '_check_director_and_port_status')
@mock.patch.object(
rest.PowerMaxRest, 'get_portgroup',
side_effect=([tpd.PowerMaxData.port_group_name_i, None]))
def test_check_port_group(
self, mock_get_pg):
self, mock_get_pg, mock_check):
for x in range(0, 2):
_, msg = self.driver.masking._check_port_group(
self.data.array, self.maskingviewdict['maskingview_name'])
@ -1230,3 +1236,58 @@ class PowerMaxMaskingTest(test.TestCase):
self.data.parent_sg_i)
mock_create.assert_not_called
mock_add.assert_not_called()
@mock.patch.object(rest.PowerMaxRest, 'get_port',
return_value=tpd.PowerMaxData.port_info)
@mock.patch.object(rest.PowerMaxRest, 'get_port_ids',
return_value=['FA-1D:4'])
def test_check_director_and_port_status(self, mock_port_ids, mock_port):
self.mask._check_director_and_port_status(
self.data.array, self.data.port_group_name_f)
@mock.patch.object(rest.PowerMaxRest, 'get_port',
return_value=tpd.PowerMaxData.port_info_off)
@mock.patch.object(rest.PowerMaxRest, 'get_port_ids',
return_value=['FA-1D:4'])
def test_check_director_and_port_status_invalid_status(
self, mock_port_ids, mock_port):
exception_message = (
r"The director status is Offline and the port status is OFF for "
r"dir:port FA-1D:4.")
with self.assertRaisesRegex(
exception.VolumeBackendAPIException,
exception_message):
self.mask._check_director_and_port_status(
self.data.array, self.data.port_group_name_f)
@mock.patch.object(rest.PowerMaxRest, 'get_port',
return_value=tpd.PowerMaxData.port_info_no_status)
@mock.patch.object(rest.PowerMaxRest, 'get_port_ids',
return_value=['FA-1D:4'])
def test_check_director_and_port_status_no_status(
self, mock_port_ids, mock_port):
exception_message = (
r"Unable to get the director or port status for dir:port "
r"FA-1D:4.")
with self.assertRaisesRegex(
exception.VolumeBackendAPIException,
exception_message):
self.mask._check_director_and_port_status(
self.data.array, self.data.port_group_name_f)
@mock.patch.object(rest.PowerMaxRest, 'get_port',
return_value=tpd.PowerMaxData.port_info_no_details)
@mock.patch.object(rest.PowerMaxRest, 'get_port_ids',
return_value=['FA-1D:4'])
def test_check_director_and_port_status_no_details(
self, mock_port_ids, mock_port):
exception_message = (
r"Unable to get port information for dir:port FA-1D:4.")
with self.assertRaisesRegex(
exception.VolumeBackendAPIException,
exception_message):
self.mask._check_director_and_port_status(
self.data.array, self.data.port_group_name_f)

View File

@ -198,10 +198,12 @@ class PowerMaxReplicationTest(test.TestCase):
self.iscsi_common.initialize_connection,
self.data.test_volume, self.data.connector)
@mock.patch.object(
masking.PowerMaxMasking, '_check_director_and_port_status')
@mock.patch.object(
masking.PowerMaxMasking, 'pre_multiattach',
return_value=tpd.PowerMaxData.masking_view_dict_multiattach)
def test_attach_metro_volume(self, mock_pre):
def test_attach_metro_volume(self, mock_pre, mock_check):
rep_extra_specs = deepcopy(tpd.PowerMaxData.rep_extra_specs)
rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
hostlunid, remote_port_group = self.common._attach_metro_volume(

View File

@ -129,9 +129,10 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
- Fix to enable legacy volumes to live migrate (#1867163)
- Use of snap id instead of generation (bp powermax-snapset-ids)
- Support for Failover Abilities (bp/powermax-failover-abilities)
4.4.0 - Early check for status of port
"""
VERSION = "4.3.0"
VERSION = "4.4.0"
# ThirdPartySystems wiki
CI_WIKI_NAME = "EMC_VMAX_CI"

View File

@ -135,9 +135,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
- Fix to enable legacy volumes to live migrate (#1867163)
- Use of snap id instead of generation (bp powermax-snapset-ids)
- Support for Failover Abilities (bp/powermax-failover-abilities)
4.4.0 - Early check for status of port
"""
VERSION = "4.3.0"
VERSION = "4.4.0"
# ThirdPartySystems wiki
CI_WIKI_NAME = "EMC_VMAX_CI"

View File

@ -215,6 +215,9 @@ class PowerMaxMasking(object):
if not portgroup:
exc_msg = ("Failed to get portgroup %(pg)s."
% {'pg': port_group_name})
else:
self._check_director_and_port_status(
serial_number, port_group_name)
else:
exc_msg = "Port group cannot be left empty."
if exc_msg:
@ -226,6 +229,48 @@ class PowerMaxMasking(object):
raise exception.VolumeBackendAPIException(
message=exception_message)
def _check_director_and_port_status(self, serial_number, port_group_name):
"""Check the status of the director and port.
:param serial_number: the array serial number
:param port_group_name: the port group name (can be None)
"""
exc_msg = None
port_id_list = self.rest.get_port_ids(serial_number, port_group_name)
if not port_id_list:
exc_msg = ("Unable to get ports from portgroup %(pgn)s "
% {'pgn': port_group_name})
for port in port_id_list:
port_info = self.rest.get_port(serial_number, port)
if port_info:
# Check that the director and port are online
port_details = port_info.get("symmetrixPort")
if port_details:
director_status = port_details.get('director_status')
port_status = port_details.get('port_status')
if not director_status or not port_status:
exc_msg = ("Unable to get the director or port status "
"for dir:port %(port)s." % {'port': port})
elif not (director_status.lower() == 'online' and (
port_status.lower() == 'on')):
exc_msg = ("The director status is %(ds)s and the "
"port status is %(ps)s for dir:port "
"%(port)s."
% {'ds': director_status,
'ps': port_status,
'port': port})
else:
exc_msg = ("Unable to get port details for dir:port "
"%(port)s." % {'port': port})
else:
exc_msg = ("Unable to get port information for dir:port "
"%(port)s."
% {'port': port})
if exc_msg:
LOG.error(exc_msg)
raise exception.VolumeBackendAPIException(
message=exc_msg)
def _create_new_masking_view(
self, serial_number, masking_view_dict,
maskingview_name, default_sg_name, extra_specs):
@ -581,7 +626,10 @@ class PowerMaxMasking(object):
"""
msg = None
portgroup = self.rest.get_portgroup(serial_number, portgroup_name)
if portgroup is None:
if portgroup:
self._check_director_and_port_status(
serial_number, portgroup_name)
else:
msg = ("Cannot get port group: %(portgroup)s from the array "
"%(array)s. Portgroups must be pre-configured - please "
"check the array."