Source code for ironic.drivers.modules.drac.inspect

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""
DRAC inspection interface
"""
from ironic.common import boot_modes
from ironic.drivers.modules.drac import utils as drac_utils
from ironic.drivers.modules import inspect_utils
from ironic.drivers.modules.redfish import inspect as redfish_inspect
from ironic.drivers.modules.redfish import utils as redfish_utils
from oslo_log import log


_PXE_DEV_ENABLED_INTERFACES = [('PxeDev1EnDis', 'PxeDev1Interface'),
                               ('PxeDev2EnDis', 'PxeDev2Interface'),
                               ('PxeDev3EnDis', 'PxeDev3Interface'),
                               ('PxeDev4EnDis', 'PxeDev4Interface')]
_BIOS_ENABLED_VALUE = 'Enabled'
LOG = log.getLogger(__name__)

[docs] class DracRedfishInspect(redfish_inspect.RedfishInspect): """iDRAC Redfish interface for inspection-related actions."""
[docs] def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential properties. It fails if any of the essential properties are not received from the node. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :returns: The resulting state of inspection. """ # Ensure we create a port for every NIC port found for consistency # with our previous WSMAN inspect behavior and to work around a bug # in some versions of the firmware where the port state is not being # reported correctly. ethernet_interfaces_mac = list(self._get_mac_address(task).values()) inspect_utils.create_ports_if_not_exist(task, ethernet_interfaces_mac) return super(DracRedfishInspect, self).inspect_hardware(task)
def _get_system_vendor_info(self, task, system): """Get system vendor information for Dell systems. Overrides the parent to use SKU (Dell service tag) as the serial number instead of the Redfish SerialNumber field, which on Dell systems contains the motherboard serial. :param task: a TaskManager instance. :param system: a Redfish system object. :returns: a dictionary of system vendor information. """ system_vendor = super()._get_system_vendor_info(task, system) if system.sku: system_vendor['serial_number'] = str(system.sku) return system_vendor def _get_mac_address(self, task): """Get a list of MAC addresses :param task: a TaskManager instance. :returns: a mapping of interface identities to MAC addresses. """ system = redfish_utils.get_system(task.node) # Get dictionary of ethernet interfaces if system.ethernet_interfaces and system.ethernet_interfaces.summary: ethernet_interfaces = system.ethernet_interfaces.get_members() ethernet_interfaces_mac = { interface.identity: interface.mac_address for interface in ethernet_interfaces} return ethernet_interfaces_mac else: return {} def _get_pxe_port_macs(self, task): """Get a list of PXE port MAC addresses. :param task: a TaskManager instance. :returns: Returns list of PXE port MAC addresses. """ system = redfish_utils.get_system(task.node) ethernet_interfaces_mac = self._get_mac_address(task) pxe_port_macs = [] if system.boot.mode == boot_modes.UEFI: # When a server is in UEFI boot mode, the PXE NIC ports are # stored in the PxeDevXEnDis and PxeDevXInterface BIOS # settings. Get the PXE NIC ports from these settings and # their MAC addresses. for param, nic in _PXE_DEV_ENABLED_INTERFACES: if system.bios.attributes[param] == _BIOS_ENABLED_VALUE: nic_id = system.bios.attributes[nic] # Get MAC address of the given nic_id mac_address = ethernet_interfaces_mac[nic_id] pxe_port_macs.append(mac_address) elif system.boot.mode == boot_modes.LEGACY_BIOS: # When a server is in BIOS boot mode, whether or not a # NIC port is set to PXE boot is stored on the NIC port # itself internally to the BMC. Getting this information # requires using an OEM extension to export the system # configuration, as the redfish standard does not specify # how to get it, and Dell does not have OEM redfish calls # to selectively retrieve it at this time. # Get instance of Sushy OEM manager object pxe_port_macs_list = drac_utils.execute_oem_manager_method( task, 'get PXE port MAC addresses', lambda m: m.get_pxe_port_macs_bios(ethernet_interfaces_mac)) pxe_port_macs = [mac for mac in pxe_port_macs_list] return pxe_port_macs def _collect_lldp_data(self, task, system): """Collect LLDP data using Dell OEM SwitchConnection endpoints. Dell iDRAC provides LLDP neighbor information through OEM DellSwitchConnection endpoints. We return parsed LLDP data directly. :param task: A TaskManager instance :param system: Sushy system object :returns: Dict mapping interface names to parsed LLDP data """ parsed_lldp = {} try: # Get Dell switch connection data switch_data = self._get_dell_switch_connections(task) # Convert directly to parsed LLDP format for connection in switch_data: # FQDD is Fully Qualified Device Descriptor (Dell term) # Example: NIC.Integrated.1-1-1 fqdd = connection.get('FQDD') switch_mac = connection.get('SwitchConnectionID') switch_port = connection.get('SwitchPortConnectionID') # Skip unconnected interfaces if (not fqdd or not switch_mac or not switch_port or switch_mac == 'No Link' or switch_port == 'No Link'): continue parsed_lldp[fqdd] = { 'switch_chassis_id': switch_mac, 'switch_port_id': switch_port } LOG.debug("Generated parsed LLDP data for %d interfaces", len(parsed_lldp)) except Exception as e: LOG.debug("Dell OEM LLDP collection failed, falling back to " "standard: %s", e) # Fallback to standard Redfish LLDP collection return super(DracRedfishInspect, self)._collect_lldp_data( task, system) return parsed_lldp def _get_dell_switch_connections(self, task): """Fetch Dell switch connection data via OEM. :param task: A TaskManager instance :returns: List of switch connection dictionaries """ system = redfish_utils.get_system(task.node) # Access Sushy's private connection object try: conn = system._conn base_url = conn._url except AttributeError as e: LOG.debug("Failed to access Sushy connection object: %s", e) return [] # Dell OEM endpoint for switch connections # This URL structure is specific to Dell iDRAC Redfish implementation switch_url = (f"{base_url}/redfish/v1/Systems/{system.identity}" "/NetworkPorts/Oem/Dell/DellSwitchConnections") LOG.debug("Fetching Dell switch connections from: %s", switch_url) try: response = conn.get(switch_url) data = response.json() members = data.get('Members', []) LOG.debug("Retrieved %d Dell switch connections", len(members)) return members except Exception as e: LOG.debug("Failed to get Dell switch connections: %s", e) return []