Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ classifier =
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 3
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12

[files]
packages =
stackhpc_inspector_plugins

[entry_points]
ironic_inspector.hooks.processing =
ironic.inspection.hooks =
ib_physnet = stackhpc_inspector_plugins.plugins.ib_physnet:IBPhysnetHook
system_name_physnet = stackhpc_inspector_plugins.plugins.system_name_physnet:SystemNamePhysnetHook
system_name_llc = stackhpc_inspector_plugins.plugins.system_name_llc:SystemNameLocalLinkConnectionHook
system_name_physnet = stackhpc_inspector_plugins.plugins.ib_physnet:SystemNamePhysnetHook
16 changes: 7 additions & 9 deletions stackhpc_inspector_plugins/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,20 @@

from oslo_config import cfg

from ironic_inspector.common.i18n import _


PORT_PHYSNET_OPTS = [
cfg.StrOpt(
'ib_physnet',
help=_('Name of the physical network that the Infiniband network is '
'on')),
help=('Name of the physical network that the Infiniband network is '
'on')),
cfg.ListOpt(
'switch_sys_name_mapping',
default=[],
help=_('Comma-separated list of '
'<switch system name>:<physical network> tuples mapping switch '
'system names received via LLDP to a physical network to apply '
'to ports that are connected to a switch with a matching '
'system name.')),
help=('Comma-separated list of '
'<switch system name>:<physical network> tuples mapping switch '
'system names received via LLDP to a physical network to apply '
'to ports that are connected to a switch with a matching '
'system name.')),
]


Expand Down
148 changes: 135 additions & 13 deletions stackhpc_inspector_plugins/plugins/ib_physnet.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Copyright (c) 2018 StackHPC Ltd.
#
# 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
Expand All @@ -13,34 +11,158 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from ironic_inspector import utils
from oslo_config import cfg
from oslo_log import log as logging

from ironic_inspector.plugins import base_physnet

LOG = utils.getProcessingLogger(__name__)
from ironic.drivers.modules.inspector.hooks import base
from ironic.drivers.modules.inspector import lldp_parsers
from ironic import objects

CONF = cfg.CONF
LOG = logging.getLogger(__name__)


class IBPhysnetHook(base_physnet.BasePhysnetHook):
"""Inspector hook to assign ports a physical network for IB interfaces.
class IBPhysnetHook(base.InspectionHook):
"""Hook to set the port's physical_network field.

This plugin sets the physical network for ports that are determined to be
Infiniband ports. The physical network is given by the configuration
option [port_physnet] ib_physnet.
Set the ironic port's physical_network field based on a CIDR to physical
network mapping in the configuration.
"""

def get_physnet(self, port, iface_name, introspection_data):
dependencies = ['validate-interfaces']

def get_physical_network(self, interface, plugin_data):
"""Return a physical network to apply to a port.

:param port: The ironic port to patch.
:param iface_name: Name of the interface.
:param introspection_data: Introspection data.
:returns: The physical network to set, or None.
"""
proc_data = introspection_data['all_interfaces'][iface_name]
iface_name = interface['name']
proc_data = plugin_data['all_interfaces'][iface_name]
if proc_data.get('client_id'):
LOG.debug("Interface %s is an Infiniband port, physnet %s",
iface_name, CONF.port_physnet.ib_physnet)
return CONF.port_physnet.ib_physnet

def __call__(self, task, inventory, plugin_data):
"""Process inspection data and patch the port's physical network."""

node_ports = objects.Port.list_by_node_id(task.context, task.node.id)
ports_dict = {p.address: p for p in node_ports}

for interface in inventory['interfaces']:
if interface['name'] not in plugin_data['all_interfaces']:
LOG.debug("No processed data for interface %s on node %s, "
"skipping physical network processing.",
interface['name'], task.node.uuid)
continue

mac_address = interface['mac_address']
port = ports_dict.get(mac_address)
if not port:
LOG.debug("Skipping physical network processing for interface "
"%s on node %s - matching port not found in Ironic.",
mac_address, task.node.uuid)
continue

# Determine the physical network for this port, using the interface
# IPs and CIDR map configuration.
phys_network = self.get_physical_network(interface, plugin_data)
if phys_network is None:
LOG.debug("Skipping physical network processing for interface "
"%s on node %s - no physical network mapping.",
mac_address, task.node.uuid)
continue

if getattr(port, 'physical_network', '') != phys_network:
port.physical_network = phys_network
port.save()
LOG.info('Updated physical_network of port %s to %s',
port.uuid, port.physical_network)


def parse_mappings(mapping_list):
"""Parse a list of mapping strings into a dictionary.

Adapted from neutron_lib.utils.helpers.parse_mappings.

:param mapping_list: A list of strings of the form '<key>:<value>'.
:returns: A dict mapping keys to values or to list of values.
:raises ValueError: Upon malformed data or duplicate keys.
"""
mappings = {}
for mapping in mapping_list:
mapping = mapping.strip()
if not mapping:
continue
split_result = mapping.split(':')
if len(split_result) != 2:
raise ValueError("Invalid mapping: '%s'" % mapping)
key = split_result[0].strip()
if not key:
raise ValueError("Missing key in mapping: '%s'" % mapping)
value = split_result[1].strip()
if not value:
raise ValueError("Missing value in mapping: '%s'" % mapping)
if key in mappings:
raise ValueError("Key %(key)s in mapping: '%(mapping)s' not "
"unique" % {'key': key, 'mapping': mapping})
mappings[key] = value
return mappings


class SystemNamePhysnetHook(IBPhysnetHook):
"""Inspector hook to assign ports a physical network based on switch name.

This plugin uses the configuration option [port_physnet]
switch_sys_name_mapping to map switch names to a physical network. If a
port has received LLDP data with a switch system name in the mapping, the
corresponding physical network will be applied to the port.
"""

def _get_switch_sys_name_mapping(self):
"""Return a dict mapping switch system names to physical networks."""
if not hasattr(self, '_switch_sys_name_mapping'):
self._switch_sys_name_mapping = parse_mappings(
CONF.port_physnet.switch_sys_name_mapping)
return self._switch_sys_name_mapping

def get_physical_network(self, interface, plugin_data):
"""Return a physical network to apply to a port.

:param port: The ironic port to patch.
:param iface_name: Name of the interface.
:param introspection_data: Introspection data.
:returns: The physical network to set, or None.
"""
# Check if LLDP data was already processed
if 'parsed_lldp' not in plugin_data:
LOG.error("No LLDP data, parse_lldp hook is required. ")
return

# check we have data for this interface
iface_name = interface['name']
lldp_proc = plugin_data['parsed_lldp'].get(iface_name)
if not lldp_proc:
LOG.debug("No LLDP data for interface %s", iface_name)
return

# Switch system name mapping.
switch_sys_name = lldp_proc.get(lldp_parsers.LLDP_SYS_NAME_NM)
if not switch_sys_name:
LOG.debug("No switch system name in LLDP data for interface %s",
iface_name)
return

mapping = self._get_switch_sys_name_mapping()
if switch_sys_name not in mapping:
LOG.debug("No config set for switch system name %s for "
"interface %s", switch_sys_name, iface_name)
return

LOG.debug("Interface %s connected to switch with system name "
"%s, physnet %s", iface_name, switch_sys_name,
mapping[switch_sys_name])
return mapping[switch_sys_name]
137 changes: 0 additions & 137 deletions stackhpc_inspector_plugins/plugins/system_name_llc.py

This file was deleted.

Loading