Skip to content

Commit d91aba9

Browse files
committed
Port plugins to ironic integrated inspector
1 parent 42875a3 commit d91aba9

File tree

11 files changed

+288
-376
lines changed

11 files changed

+288
-376
lines changed

setup.cfg

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ classifier =
1414
License :: OSI Approved :: Apache Software License
1515
Operating System :: OS Independent
1616
Programming Language :: Python
17-
Programming Language :: Python :: 2
17+
Programming Language :: Python :: 3
18+
Programming Language :: Python :: 3.9
19+
Programming Language :: Python :: 3.10
20+
Programming Language :: Python :: 3.11
21+
Programming Language :: Python :: 3.12
1822

1923
[files]
2024
packages =
2125
stackhpc_inspector_plugins
2226

2327
[entry_points]
24-
ironic_inspector.hooks.processing =
28+
ironic.inspection.hooks =
2529
ib_physnet = stackhpc_inspector_plugins.plugins.ib_physnet:IBPhysnetHook
26-
system_name_physnet = stackhpc_inspector_plugins.plugins.system_name_physnet:SystemNamePhysnetHook
27-
system_name_llc = stackhpc_inspector_plugins.plugins.system_name_llc:SystemNameLocalLinkConnectionHook
30+
system_name_physnet = stackhpc_inspector_plugins.plugins.ib_physnet:SystemNamePhysnetHook

stackhpc_inspector_plugins/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@
1515

1616
# Import configuration module to register configuration options.
1717
from stackhpc_inspector_plugins import conf # noqa: F401
18+
19+
# NOTE(TheJulia): This is to force oslo_service from trying to use eventlet.
20+
from oslo_service import backend
21+
backend.init_backend(backend.BackendType.THREADING)

stackhpc_inspector_plugins/conf.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,20 @@
1515

1616
from oslo_config import cfg
1717

18-
from ironic_inspector.common.i18n import _
19-
2018

2119
PORT_PHYSNET_OPTS = [
2220
cfg.StrOpt(
2321
'ib_physnet',
24-
help=_('Name of the physical network that the Infiniband network is '
25-
'on')),
22+
help=('Name of the physical network that the Infiniband network is '
23+
'on')),
2624
cfg.ListOpt(
2725
'switch_sys_name_mapping',
2826
default=[],
29-
help=_('Comma-separated list of '
30-
'<switch system name>:<physical network> tuples mapping switch '
31-
'system names received via LLDP to a physical network to apply '
32-
'to ports that are connected to a switch with a matching '
33-
'system name.')),
27+
help=('Comma-separated list of '
28+
'<switch system name>:<physical network> tuples mapping switch '
29+
'system names received via LLDP to a physical network to apply '
30+
'to ports that are connected to a switch with a matching '
31+
'system name.')),
3432
]
3533

3634

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Copyright (c) 2018 StackHPC Ltd.
2-
#
31
# Licensed under the Apache License, Version 2.0 (the "License");
42
# you may not use this file except in compliance with the License.
53
# You may obtain a copy of the License at
@@ -13,34 +11,142 @@
1311
# See the License for the specific language governing permissions and
1412
# limitations under the License.
1513

16-
from ironic_inspector import utils
1714
from oslo_config import cfg
15+
from oslo_log import log as logging
1816

19-
from ironic_inspector.plugins import base_physnet
20-
21-
LOG = utils.getProcessingLogger(__name__)
17+
from ironic.drivers.modules.inspector.hooks import base
18+
from ironic.drivers.modules.inspector import lldp_parsers
19+
from ironic import objects
2220

2321
CONF = cfg.CONF
22+
LOG = logging.getLogger(__name__)
2423

2524

26-
class IBPhysnetHook(base_physnet.BasePhysnetHook):
27-
"""Inspector hook to assign ports a physical network for IB interfaces.
25+
class IBPhysnetHook(base.InspectionHook):
26+
"""Hook to set the port's physical_network field.
2827
29-
This plugin sets the physical network for ports that are determined to be
30-
Infiniband ports. The physical network is given by the configuration
31-
option [port_physnet] ib_physnet.
28+
Set the ironic port's physical_network field based on a CIDR to physical
29+
network mapping in the configuration.
3230
"""
3331

34-
def get_physnet(self, port, iface_name, introspection_data):
32+
dependencies = ['validate-interfaces']
33+
34+
def get_physical_network(self, interface, plugin_data):
3535
"""Return a physical network to apply to a port.
3636
3737
:param port: The ironic port to patch.
3838
:param iface_name: Name of the interface.
3939
:param introspection_data: Introspection data.
4040
:returns: The physical network to set, or None.
4141
"""
42-
proc_data = introspection_data['all_interfaces'][iface_name]
42+
iface_name = interface['name']
43+
proc_data = plugin_data['all_interfaces'][iface_name]
4344
if proc_data.get('client_id'):
4445
LOG.debug("Interface %s is an Infiniband port, physnet %s",
4546
iface_name, CONF.port_physnet.ib_physnet)
4647
return CONF.port_physnet.ib_physnet
48+
49+
def __call__(self, task, inventory, plugin_data):
50+
"""Process inspection data and patch the port's physical network."""
51+
52+
node_ports = objects.Port.list_by_node_id(task.context, task.node.id)
53+
ports_dict = {p.address: p for p in node_ports}
54+
55+
for interface in inventory['interfaces']:
56+
if interface['name'] not in plugin_data['all_interfaces']:
57+
continue
58+
59+
mac_address = interface['mac_address']
60+
port = ports_dict.get(mac_address)
61+
if not port:
62+
LOG.debug("Skipping physical network processing for interface "
63+
"%s on node %s - matching port not found in Ironic.",
64+
mac_address, task.node.uuid)
65+
continue
66+
67+
# Determine the physical network for this port, using the interface
68+
# IPs and CIDR map configuration.
69+
phys_network = self.get_physical_network(interface, plugin_data)
70+
if phys_network is None:
71+
LOG.debug("Skipping physical network processing for interface "
72+
"%s on node %s - no physical network mapping.",
73+
mac_address,
74+
task.node.uuid)
75+
continue
76+
77+
if getattr(port, 'physical_network', '') != phys_network:
78+
port.physical_network = phys_network
79+
port.save()
80+
LOG.info('Updated physical_network of port %s to %s',
81+
port.uuid, port.physical_network)
82+
83+
84+
def parse_mappings(mapping_list):
85+
"""Parse a list of mapping strings into a dictionary.
86+
87+
Adapted from neutron_lib.utils.helpers.parse_mappings.
88+
89+
:param mapping_list: A list of strings of the form '<key>:<value>'.
90+
:returns: A dict mapping keys to values or to list of values.
91+
:raises ValueError: Upon malformed data or duplicate keys.
92+
"""
93+
mappings = {}
94+
for mapping in mapping_list:
95+
mapping = mapping.strip()
96+
if not mapping:
97+
continue
98+
split_result = mapping.split(':')
99+
if len(split_result) != 2:
100+
raise ValueError("Invalid mapping: '%s'" % mapping)
101+
key = split_result[0].strip()
102+
if not key:
103+
raise ValueError("Missing key in mapping: '%s'" % mapping)
104+
value = split_result[1].strip()
105+
if not value:
106+
raise ValueError("Missing value in mapping: '%s'" % mapping)
107+
if key in mappings:
108+
raise ValueError("Key %(key)s in mapping: '%(mapping)s' not "
109+
"unique" % {'key': key, 'mapping': mapping})
110+
mappings[key] = value
111+
return mappings
112+
113+
114+
class SystemNamePhysnetHook(IBPhysnetHook):
115+
"""Inspector hook to assign ports a physical network based on switch name.
116+
117+
This plugin uses the configuration option [port_physnet]
118+
switch_sys_name_mapping to map switch names to a physical network. If a
119+
port has received LLDP data with a switch system name in the mapping, the
120+
corresponding physical network will be applied to the port.
121+
"""
122+
123+
def _get_switch_sys_name_mapping(self):
124+
"""Return a dict mapping switch system names to physical networks."""
125+
if not hasattr(self, '_switch_sys_name_mapping'):
126+
self._switch_sys_name_mapping = parse_mappings(
127+
CONF.port_physnet.switch_sys_name_mapping)
128+
return self._switch_sys_name_mapping
129+
130+
def get_physical_network(self, interface, plugin_data):
131+
"""Return a physical network to apply to a port.
132+
133+
:param port: The ironic port to patch.
134+
:param iface_name: Name of the interface.
135+
:param introspection_data: Introspection data.
136+
:returns: The physical network to set, or None.
137+
"""
138+
# Check if LLDP data was already processed by lldp_basic plugin
139+
# which stores data in 'all_interfaces'
140+
iface_name = interface['name']
141+
proc_data = plugin_data['all_interfaces'][iface_name]
142+
if 'parsed_lldp' not in proc_data:
143+
return
144+
145+
lldp_proc = proc_data['parsed_lldp']
146+
147+
# Switch system name mapping.
148+
switch_sys_name = lldp_proc.get(lldp_parsers.LLDP_SYS_NAME_NM)
149+
if switch_sys_name:
150+
mapping = self._get_switch_sys_name_mapping()
151+
if switch_sys_name in mapping:
152+
return mapping[switch_sys_name]

stackhpc_inspector_plugins/plugins/system_name_physnet.py

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)