Merge "Use processed lldp data, if available, for local_link_connection plugin"
This commit is contained in:
commit
9142b7d0d7
@ -191,7 +191,9 @@ Here are some plugins that can be additionally enabled:
|
||||
port ID and chassis ID, if found it configures the local link connection
|
||||
information on the nodes Ironic ports with that data. To enable LLDP in the
|
||||
inventory from IPA ``ipa-collect-lldp=1`` should be passed as a kernel
|
||||
parameter to the IPA ramdisk.
|
||||
parameter to the IPA ramdisk. In order to avoid processing the raw LLDP
|
||||
data twice, the ``lldp_basic`` plugin should also be installed and run
|
||||
prior to this plugin.
|
||||
``lldp_basic``
|
||||
Processes LLDP data returned from inspection and parses TLVs from the
|
||||
Basic Management (802.1AB), 802.1Q, and 802.3 sets and stores the
|
||||
|
@ -15,30 +15,30 @@
|
||||
|
||||
import binascii
|
||||
|
||||
from construct import core
|
||||
from ironicclient import exc as client_exc
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic_inspector.common import ironic
|
||||
from ironic_inspector.common import lldp_parsers
|
||||
from ironic_inspector.common import lldp_tlvs as tlv
|
||||
from ironic_inspector.plugins import base
|
||||
from ironic_inspector import utils
|
||||
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
# NOTE(sambetts) Constants defined according to IEEE standard for LLDP
|
||||
# http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf
|
||||
LLDP_TLV_TYPE_CHASSIS_ID = 1
|
||||
LLDP_TLV_TYPE_PORT_ID = 2
|
||||
PORT_ID_SUBTYPE_MAC = 3
|
||||
PORT_ID_SUBTYPE_IFNAME = 5
|
||||
PORT_ID_SUBTYPE_LOCAL = 7
|
||||
STRING_PORT_SUBTYPES = [PORT_ID_SUBTYPE_IFNAME, PORT_ID_SUBTYPE_LOCAL]
|
||||
CHASSIS_ID_SUBTYPE_MAC = 4
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
REQUIRED_IRONIC_VERSION = '1.19'
|
||||
|
||||
PORT_ID_ITEM_NAME = "port_id"
|
||||
SWITCH_ID_ITEM_NAME = "switch_id"
|
||||
|
||||
LLDP_PROC_DATA_MAPPING =\
|
||||
{lldp_parsers.LLDP_CHASSIS_ID_NM: SWITCH_ID_ITEM_NAME,
|
||||
lldp_parsers.LLDP_PORT_ID_NM: PORT_ID_ITEM_NAME}
|
||||
|
||||
|
||||
class GenericLocalLinkConnectionHook(base.ProcessingHook):
|
||||
"""Process mandatory LLDP packet fields
|
||||
@ -49,33 +49,39 @@ class GenericLocalLinkConnectionHook(base.ProcessingHook):
|
||||
fields on the Ironic port that represents that NIC.
|
||||
"""
|
||||
|
||||
def _get_local_link_patch(self, tlv_type, tlv_value, port):
|
||||
def _get_local_link_patch(self, tlv_type, tlv_value, port, node_info):
|
||||
try:
|
||||
data = bytearray(binascii.unhexlify(tlv_value))
|
||||
except TypeError:
|
||||
LOG.warning("TLV value for TLV type %d not in correct"
|
||||
"format, ensure TLV value is in "
|
||||
"hexidecimal format when sent to "
|
||||
"inspector", tlv_type)
|
||||
"inspector", tlv_type, node_info=node_info)
|
||||
return
|
||||
|
||||
item = value = None
|
||||
if tlv_type == LLDP_TLV_TYPE_PORT_ID:
|
||||
# Check to ensure the port id is an allowed type
|
||||
item = "port_id"
|
||||
if data[0] in STRING_PORT_SUBTYPES:
|
||||
value = data[1:].decode()
|
||||
if data[0] == PORT_ID_SUBTYPE_MAC:
|
||||
value = str(netaddr.EUI(
|
||||
binascii.hexlify(data[1:]).decode(),
|
||||
dialect=netaddr.mac_unix_expanded))
|
||||
elif tlv_type == LLDP_TLV_TYPE_CHASSIS_ID:
|
||||
# Check to ensure the chassis id is the allowed type
|
||||
if data[0] == CHASSIS_ID_SUBTYPE_MAC:
|
||||
item = "switch_id"
|
||||
value = str(netaddr.EUI(
|
||||
binascii.hexlify(data[1:]).decode(),
|
||||
dialect=netaddr.mac_unix_expanded))
|
||||
if tlv_type == tlv.LLDP_TLV_PORT_ID:
|
||||
try:
|
||||
port_id = tlv.PortId.parse(data)
|
||||
except (core.MappingError, netaddr.AddrFormatError) as e:
|
||||
LOG.warning("TLV parse error for Port ID: %s", e,
|
||||
node_info=node_info)
|
||||
return
|
||||
|
||||
item = PORT_ID_ITEM_NAME
|
||||
value = port_id.value
|
||||
elif tlv_type == tlv.LLDP_TLV_CHASSIS_ID:
|
||||
try:
|
||||
chassis_id = tlv.ChassisId.parse(data)
|
||||
except (core.MappingError, netaddr.AddrFormatError) as e:
|
||||
LOG.warning("TLV parse error for Chassis ID: %s", e,
|
||||
node_info=node_info)
|
||||
return
|
||||
|
||||
# Only accept mac address for chassis ID
|
||||
if 'mac_address' in chassis_id.subtype:
|
||||
item = SWITCH_ID_ITEM_NAME
|
||||
value = chassis_id.value
|
||||
|
||||
if item and value:
|
||||
if (not CONF.processing.overwrite_existing and
|
||||
@ -85,6 +91,21 @@ class GenericLocalLinkConnectionHook(base.ProcessingHook):
|
||||
'path': '/local_link_connection/%s' % item,
|
||||
'value': value}
|
||||
|
||||
def _get_lldp_processed_patch(self, name, item, lldp_proc_data, port):
|
||||
|
||||
if 'lldp_processed' not in lldp_proc_data:
|
||||
return
|
||||
|
||||
value = lldp_proc_data['lldp_processed'].get(name)
|
||||
|
||||
if value:
|
||||
if (not CONF.processing.overwrite_existing and
|
||||
item in port.local_link_connection):
|
||||
return
|
||||
return {'op': 'add',
|
||||
'path': '/local_link_connection/%s' % item,
|
||||
'value': value}
|
||||
|
||||
def before_update(self, introspection_data, node_info, **kwargs):
|
||||
"""Process LLDP data and patch Ironic port local link connection"""
|
||||
inventory = utils.get_inventory(introspection_data)
|
||||
@ -111,11 +132,24 @@ class GenericLocalLinkConnectionHook(base.ProcessingHook):
|
||||
continue
|
||||
|
||||
patches = []
|
||||
for tlv_type, tlv_value in lldp_data:
|
||||
patch = self._get_local_link_patch(tlv_type, tlv_value, port)
|
||||
# First check if lldp data was already processed by lldp_basic
|
||||
# plugin which stores data in 'all_interfaces'
|
||||
proc_data = introspection_data['all_interfaces'][iface['name']]
|
||||
|
||||
for name, item in LLDP_PROC_DATA_MAPPING.items():
|
||||
patch = self._get_lldp_processed_patch(name, item,
|
||||
proc_data, port)
|
||||
if patch is not None:
|
||||
patches.append(patch)
|
||||
|
||||
# If no processed lldp data was available then parse raw lldp data
|
||||
if not patches:
|
||||
for tlv_type, tlv_value in lldp_data:
|
||||
patch = self._get_local_link_patch(tlv_type, tlv_value,
|
||||
port, node_info)
|
||||
if patch is not None:
|
||||
patches.append(patch)
|
||||
|
||||
try:
|
||||
# NOTE(sambetts) We need a newer version of Ironic API for this
|
||||
# transaction, so create a new ironic client and explicitly
|
||||
|
@ -143,3 +143,54 @@ class TestGenericLocalLinkConnectionHook(test_base.NodeTest):
|
||||
]
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patches, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch_port')
|
||||
def test_processed_data_available(self, mock_patch):
|
||||
self.data['all_interfaces'] = {
|
||||
'em1': {"ip": self.ips[0], "mac": self.macs[0],
|
||||
"lldp_processed": {
|
||||
"switch_chassis_id": "11:22:33:aa:bb:dd",
|
||||
"switch_port_id": "Ethernet2/66"}
|
||||
}
|
||||
}
|
||||
|
||||
patches = [
|
||||
{'path': '/local_link_connection/port_id',
|
||||
'value': 'Ethernet2/66', 'op': 'add'},
|
||||
{'path': '/local_link_connection/switch_id',
|
||||
'value': '11:22:33:aa:bb:dd', 'op': 'add'},
|
||||
]
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patches, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch_port')
|
||||
def test_processed_data_chassis_only(self, mock_patch):
|
||||
self.data['all_interfaces'] = {
|
||||
'em1': {"ip": self.ips[0], "mac": self.macs[0],
|
||||
"lldp_processed": {
|
||||
"switch_chassis_id": "11:22:33:aa:bb:dd"}
|
||||
}
|
||||
}
|
||||
|
||||
patches = [
|
||||
{'path': '/local_link_connection/switch_id',
|
||||
'value': '11:22:33:aa:bb:dd', 'op': 'add'}
|
||||
]
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patches, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch_port')
|
||||
def test_processed_data_port_only(self, mock_patch):
|
||||
self.data['all_interfaces'] = {
|
||||
'em1': {"ip": self.ips[0], "mac": self.macs[0],
|
||||
"lldp_processed": {
|
||||
"switch_port_id": "Ethernet2/66"}
|
||||
}
|
||||
}
|
||||
|
||||
patches = [
|
||||
{'path': '/local_link_connection/port_id',
|
||||
'value': 'Ethernet2/66', 'op': 'add'}
|
||||
]
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patches, mock_patch)
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a check from the link_local_connection plugin to use data stored by the
|
||||
lldp_basic plugin to avoid having to parse the LLDP packets twice.
|
||||
|
Loading…
x
Reference in New Issue
Block a user