From 3e05a03f7c04d59cc5b6aa6bb5633431a1beb2a0 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Tue, 25 Apr 2023 12:04:37 +0200 Subject: [PATCH] Deprecate LLDP in inventory in favour of a new collector Binary LLDP data is bloating inventory causing us to disable its collection by default. For other similar low-level information, such as PCI devices or DMI data, we already use inspection collectors instead. Now that the inventory format is shared with out-of-band inspection, having LLDP there makes even less sense. This change adds a new collector ``lldp`` to replace the now-deprecated inventory field. Change-Id: I56be06a7d1db28407e1128c198c12bea0809d3a3 --- doc/source/admin/how_it_works.rst | 18 +++++++--- ironic_python_agent/config.py | 4 ++- ironic_python_agent/hardware.py | 36 +++++-------------- ironic_python_agent/inspector.py | 9 +++++ ironic_python_agent/netutils.py | 24 +++++++++++++ .../notes/lldp-raw-a09174cb930bca97.yaml | 12 +++++++ setup.cfg | 1 + 7 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 releasenotes/notes/lldp-raw-a09174cb930bca97.yaml diff --git a/doc/source/admin/how_it_works.rst b/doc/source/admin/how_it_works.rst index 803ac7a83..5f4a6773a 100644 --- a/doc/source/admin/how_it_works.rst +++ b/doc/source/admin/how_it_works.rst @@ -145,8 +145,16 @@ collectors are: * ``nics`` - list of objects with keys ``name`` (NIC name) and ``numa_node`` (node ID). +``lldp`` + Collects information about the network connectivity using LLDP_. Provides + one key: + + * ``lldp_raw`` - mapping of interface names to lists of raw + type-length-value (TLV) records. + .. _hardware: https://pypi.org/project/hardware/ .. _NUMA: https://en.wikipedia.org/wiki/Non-uniform_memory_access +.. _LLDP: https://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol .. _hardware-inventory: @@ -191,10 +199,12 @@ fields: ``interfaces`` list of network interfaces with fields: ``name``, ``mac_address``, ``ipv4_address``, ``lldp``, ``vendor``, ``product``, and optionally - ``biosdevname`` (BIOS given NIC name). If configuration option - ``collect_lldp`` is set to True the ``lldp`` field will be populated - by a list of type-length-value(TLV) fields retrieved using the - Link Layer Discovery Protocol (LLDP). + ``biosdevname`` (BIOS given NIC name). + + .. note:: + For backward compatibility, interfaces may contain ``lldp`` fields. + They are deprecated, consumers should rely on the ``lldp`` inspection + collector instead. ``system_vendor`` system vendor information from SMBIOS as reported by ``dmidecode``: diff --git a/ironic_python_agent/config.py b/ironic_python_agent/config.py index 9251a3e37..056cab853 100644 --- a/ironic_python_agent/config.py +++ b/ironic_python_agent/config.py @@ -149,7 +149,9 @@ cli_opts = [ help='Whether IPA should attempt to receive LLDP packets for ' 'each network interface it discovers in the inventory. ' 'Can be supplied as "ipa-collect-lldp" ' - 'kernel parameter.'), + 'kernel parameter.', + deprecated_for_removal=True, + deprecated_reason="Use the lldp collector instead"), cfg.StrOpt('inspection_callback_url', default=APARAMS.get('ipa-inspection-callback-url'), diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index 8dd3a9612..badb07db1 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -865,6 +865,9 @@ class HardwareManager(object, metaclass=abc.ABCMeta): def list_network_interfaces(self): raise errors.IncompatibleHardwareMethodError + def collect_lldp_data(self, interface_names=None): + raise errors.IncompatibleHardwareMethodError + def get_cpus(self): raise errors.IncompatibleHardwareMethodError @@ -1198,7 +1201,6 @@ class GenericHardwareManager(HardwareManager): HARDWARE_MANAGER_VERSION = '1.1' def __init__(self): - self.sys_path = '/sys' self.lldp_data = {} def evaluate_hardware_support(self): @@ -1213,7 +1215,7 @@ class GenericHardwareManager(HardwareManager): self.wait_for_disks() return HardwareSupport.GENERIC - def collect_lldp_data(self, interface_names): + def collect_lldp_data(self, interface_names=None): """Collect and convert LLDP info from the node. In order to process the LLDP information later, the raw data needs to @@ -1222,7 +1224,8 @@ class GenericHardwareManager(HardwareManager): :param interface_names: list of names of node's interfaces. :return: a dict, containing the lldp data from every interface. """ - + if interface_names is None: + interface_names = netutils.list_interfaces() interface_names = [name for name in interface_names if name != 'lo'] lldp_data = {} try: @@ -1292,7 +1295,7 @@ class GenericHardwareManager(HardwareManager): """ global WARN_BIOSDEVNAME_NOT_FOUND - if self._is_vlan(interface_name): + if netutils.is_vlan(interface_name): LOG.debug('Interface %s is a VLAN, biosdevname not called', interface_name) return @@ -1313,35 +1316,14 @@ class GenericHardwareManager(HardwareManager): else: LOG.warning('Biosdevname returned exit code %s', e.exit_code) - def _is_device(self, interface_name): - device_path = '{}/class/net/{}/device'.format(self.sys_path, - interface_name) - return os.path.exists(device_path) - - def _is_vlan(self, interface_name): - # A VLAN interface does not have /device, check naming convention - # used when adding VLAN interface - - interface, sep, vlan = interface_name.partition('.') - - return vlan.isdigit() - - def _is_bond(self, interface_name): - device_path = '{}/class/net/{}/bonding'.format(self.sys_path, - interface_name) - return os.path.exists(device_path) - def list_network_interfaces(self): - network_interfaces_list = [] - iface_names = os.listdir('{}/class/net'.format(self.sys_path)) - iface_names = [name for name in iface_names - if self._is_vlan(name) or self._is_device(name) - or self._is_bond(name)] + iface_names = netutils.list_interfaces() if CONF.collect_lldp: self.lldp_data = dispatch_to_managers('collect_lldp_data', interface_names=iface_names) + network_interfaces_list = [] for iface_name in iface_names: try: result = dispatch_to_managers( diff --git a/ironic_python_agent/inspector.py b/ironic_python_agent/inspector.py index 8e570c3b4..c2d98d5de 100644 --- a/ironic_python_agent/inspector.py +++ b/ironic_python_agent/inspector.py @@ -364,3 +364,12 @@ def collect_pci_devices_info(data, failures): 'revision': pci_revision, 'bus': subdir}) data['pci_devices'] = pci_devices_info + + +def collect_lldp(data, failures): + """Collect LLDP information for network interfaces. + + :param data: mutable data that we'll send to inspector + :param failures: AccumulatedFailures object + """ + data['lldp_raw'] = hardware.dispatch_to_managers('collect_lldp_data') diff --git a/ironic_python_agent/netutils.py b/ironic_python_agent/netutils.py index 0b595d969..2e2898086 100644 --- a/ironic_python_agent/netutils.py +++ b/ironic_python_agent/netutils.py @@ -14,6 +14,7 @@ import ctypes import fcntl +import os import select import socket import struct @@ -247,6 +248,29 @@ def get_hostname(): return socket.gethostname() +def is_network_device(interface_name): + device_path = f'/sys/class/net/{interface_name}/device' + return os.path.exists(device_path) + + +def is_vlan(interface_name): + # A VLAN interface does not have /device, check naming convention + # used when adding VLAN interface + interface, sep, vlan = interface_name.partition('.') + return vlan.isdigit() + + +def is_bond(interface_name): + device_path = f'/sys/class/net/{interface_name}/bonding' + return os.path.exists(device_path) + + +def list_interfaces(): + iface_names = os.listdir('/sys/class/net') + return [name for name in iface_names + if is_vlan(name) or is_network_device(name) or is_bond(name)] + + def interface_has_carrier(interface_name): path = '/sys/class/net/{}/carrier'.format(interface_name) try: diff --git a/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml b/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml new file mode 100644 index 000000000..19d668bc0 --- /dev/null +++ b/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Adds a new inspection collector ``lldp`` that collects LLDP information + into the ``lldp_raw`` field. +deprecations: + - | + The LLDP information as part of the general inventory is deprecated. + Use the new ``lldp`` inspection collector to retrieve it. + - | + The ``ipa-collect-lldp`` kernel parameter and the corresponding option are + now deprecated. diff --git a/setup.cfg b/setup.cfg index 8e9e95f3d..e2a286648 100644 --- a/setup.cfg +++ b/setup.cfg @@ -55,6 +55,7 @@ ironic_python_agent.inspector.collectors = pci-devices = ironic_python_agent.inspector:collect_pci_devices_info numa-topology = ironic_python_agent.numa_inspector:collect_numa_topology_info dmi-decode = ironic_python_agent.dmi_inspector:collect_dmidecode_info + lldp = ironic_python_agent.inspector:collect_lldp [pbr] autodoc_index_modules = True