Fix OOB introspection to use pxe_enabled flag in idrac driver

Baremetal nodes fail to pxe boot during OpenStack deployment.
Reason of this failure is because we are not checking the
actual pxe device interface and updating the port pxe_enabled field.

This patch make sure that everytime when port is created, it checks the
the BIOS and UEFI boot modes of node and fetch the pxe device interfaces
values matching to nic id and then update the port's pxe_enabled field.

Change-Id: I2890bf16110b713e269d4a4fe410f57273dc8e83
Story: 2004340
This commit is contained in:
digambar 2018-11-27 04:17:02 -05:00
parent e9b4737431
commit 2b74d940c9
4 changed files with 178 additions and 1 deletions

View File

@ -119,11 +119,18 @@ class DracInspect(base.InspectInterface):
{'node_uuid': node.uuid, 'error': exc})
raise exception.HardwareInspectionFailure(error=exc)
pxe_dev_nics = self._get_pxe_dev_nics(client, nics, node)
if pxe_dev_nics is None:
LOG.warning('No PXE enabled NIC was found for node '
'%(node_uuid)s.', {'node_uuid': node.uuid})
for nic in nics:
try:
port = objects.Port(task.context, address=nic.mac,
node_id=node.id)
node_id=node.id,
pxe_enabled=(nic.id in pxe_dev_nics))
port.create()
LOG.info('Port created with MAC address %(mac)s '
'for node %(node_uuid)s during inspection',
{'mac': nic.mac, 'node_uuid': node.uuid})
@ -161,3 +168,47 @@ class DracInspect(base.InspectInterface):
return cpu.cores * 2
else:
return cpu.cores
def _get_pxe_dev_nics(self, client, nics, node):
"""Get a list of pxe device interfaces.
:param client: Dracclient to list the bios settings and nics
:param nics: list of nics
:returns: Returns list of pxe device interfaces.
"""
pxe_dev_nics = []
pxe_params = ["PxeDev1EnDis", "PxeDev2EnDis",
"PxeDev3EnDis", "PxeDev4EnDis"]
pxe_nics = ["PxeDev1Interface", "PxeDev2Interface",
"PxeDev3Interface", "PxeDev4Interface"]
try:
bios_settings = client.list_bios_settings()
except drac_exceptions.BaseClientException as exc:
LOG.error('DRAC driver failed to list bios settings '
'for %(node_uuid)s. Reason: %(error)s.',
{'node_uuid': node.uuid, 'error': exc})
raise exception.HardwareInspectionFailure(error=exc)
if bios_settings["BootMode"].current_value == "Uefi":
for param, nic in zip(pxe_params, pxe_nics):
if param in bios_settings and bios_settings[
param].current_value == "Enabled":
pxe_dev_nics.append(
bios_settings[nic].current_value)
elif bios_settings["BootMode"].current_value == "Bios":
for nic in nics:
try:
nic_cap = client.list_nic_settings(nic_id=nic.id)
except drac_exceptions.BaseClientException as exc:
LOG.error('DRAC driver failed to list nic settings '
'for %(node_uuid)s. Reason: %(error)s.',
{'node_uuid': node.uuid, 'error': exc})
raise exception.HardwareInspectionFailure(error=exc)
if ("LegacyBootProto" in nic_cap and nic_cap[
'LegacyBootProto'].current_value == "PXE"):
pxe_dev_nics.append(nic.id)
return pxe_dev_nics

View File

@ -121,6 +121,20 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
'speed': '1000 Mbps',
'duplex': 'full duplex',
'media_type': 'Base T'}]
bios_boot_settings = {'BootMode': {'current_value': 'Bios'}}
uefi_boot_settings = {'BootMode': {'current_value': 'Uefi'},
'PxeDev1EnDis': {'current_value': 'Enabled'},
'PxeDev2EnDis': {'current_value': 'Disabled'},
'PxeDev3EnDis': {'current_value': 'Disabled'},
'PxeDev4EnDis': {'current_value': 'Disabled'},
'PxeDev1Interface': {
'current_value': 'NIC.Embedded.1-1-1'},
'PxeDev2Interface': None,
'PxeDev3Interface': None,
'PxeDev4Interface': None}
nic_settings = {'LegacyBootProto': {'current_value': 'PXE'},
'FQDD': 'NIC.Embedded.1-1-1'}
self.memory = [test_utils.dict_to_namedtuple(values=m) for m in memory]
self.cpus = [test_utils.dict_to_namedtuple(values=c) for c in cpus]
self.virtual_disks = [test_utils.dict_to_namedtuple(values=vd)
@ -128,6 +142,9 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
self.physical_disks = [test_utils.dict_to_namedtuple(values=pd)
for pd in physical_disks]
self.nics = [test_utils.dict_to_namedtuple(values=n) for n in nics]
self.bios_boot_settings = test_utils.dict_of_object(bios_boot_settings)
self.uefi_boot_settings = test_utils.dict_of_object(uefi_boot_settings)
self.nic_settings = test_utils.dict_of_object(nic_settings)
def test_get_properties(self):
expected = drac_common.COMMON_PROPERTIES
@ -149,6 +166,7 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
mock_client.list_cpus.return_value = self.cpus
mock_client.list_virtual_disks.return_value = self.virtual_disks
mock_client.list_nics.return_value = self.nics
mock_client.list_bios_settings.return_value = self.uefi_boot_settings
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -193,6 +211,7 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
mock_client.list_virtual_disks.return_value = []
mock_client.list_physical_disks.return_value = self.physical_disks
mock_client.list_nics.return_value = self.nics
mock_client.list_bios_settings.return_value = self.uefi_boot_settings
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -237,6 +256,8 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
mock_client.list_cpus.return_value = self.cpus
mock_client.list_virtual_disks.return_value = self.virtual_disks
mock_client.list_nics.return_value = self.nics
mock_client.list_bios_settings.return_value = self.uefi_boot_settings
mock_port_create.side_effect = exception.MACAlreadyExists("boom")
with task_manager.acquire(self.context, self.node.uuid,
@ -271,3 +292,84 @@ class DracInspectionTestCase(test_utils.BaseDracTest):
self.cpus[1])
self.assertEqual(6, cpu)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
def test__get_pxe_dev_nics_with_UEFI_boot_mode(self, mock_get_drac_client):
expected_pxe_nic = self.uefi_boot_settings[
'PxeDev1Interface'].current_value
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_bios_settings.return_value = self.uefi_boot_settings
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
pxe_dev_nics = task.driver.inspect._get_pxe_dev_nics(
mock_client, self.nics, self.node)
self.assertEqual(expected_pxe_nic, pxe_dev_nics[0])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
def test__get_pxe_dev_nics_with_BIOS_boot_mode(self, mock_get_drac_client):
expected_pxe_nic = self.nic_settings['FQDD']
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_bios_settings.return_value = self.bios_boot_settings
mock_client.list_nic_settings.return_value = self.nic_settings
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
pxe_dev_nics = task.driver.inspect._get_pxe_dev_nics(
mock_client, self.nics, self.node)
self.assertEqual(expected_pxe_nic, pxe_dev_nics[0])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
def test__get_pxe_dev_nics_list_boot_setting_failure(self,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_bios_settings.side_effect = (
drac_exceptions.BaseClientException('foo'))
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaises(exception.HardwareInspectionFailure,
task.driver.inspect._get_pxe_dev_nics,
mock_client,
self.nics,
self.node)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
def test__get_pxe_dev_nics_list_nic_setting_failure(self,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_bios_settings.return_value = self.bios_boot_settings
mock_client.list_nic_settings.side_effect = (
drac_exceptions.BaseClientException('bar'))
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaises(exception.HardwareInspectionFailure,
task.driver.inspect._get_pxe_dev_nics,
mock_client,
self.nics,
self.node)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
def test__get_pxe_dev_nics_with_empty_list(self, mock_get_drac_client):
expected_pxe_nic = []
nic_setting = []
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_bios_settings.return_value = self.bios_boot_settings
mock_client.list_nic_settings.return_value = nic_setting
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
pxe_dev_nics = task.driver.inspect._get_pxe_dev_nics(
mock_client, self.nics, self.node)
self.assertEqual(expected_pxe_nic, pxe_dev_nics)

View File

@ -31,6 +31,13 @@ class BaseDracTest(db_base.DbTestCase):
enabled_raid_interfaces=['idrac', 'fake', 'no-raid'])
class DictToObj(object):
"""Returns a dictionary into a class"""
def __init__(self, dictionary):
for key in dictionary:
setattr(self, key, dictionary[key])
def dict_to_namedtuple(name='GenericNamedTuple', values=None):
"""Converts a dict to a collections.namedtuple"""
@ -38,3 +45,13 @@ def dict_to_namedtuple(name='GenericNamedTuple', values=None):
values = {}
return collections.namedtuple(name, list(values))(**values)
def dict_of_object(data):
"""Create a dictionary object"""
for k, v in data.items():
if isinstance(v, dict):
dict_obj = DictToObj(v)
data[k] = dict_obj
return data

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fixes a bug where ironic port is not updated in node introspection as per
PXE enabled setting for ``idrac`` hardware type.
See bug `2004340
<https://storyboard.openstack.org/#!/story/2004340>`_ for details.