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_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 []