mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
statd: Add LLDP operational data retrieval
Enable statd to fetch LLDP neighbor data via lldpcli for verifying LLDP admin status. Currently, only chassis ID and chassis ID subtype are retrieved.
This commit is contained in:
@@ -76,10 +76,6 @@ module infix-lldp {
|
||||
deviation "/lldp:lldp/lldp:port/lldp:rx-statistics" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/lldp:lldp/lldp:port/lldp:remote-systems-data" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
|
||||
deviation "/lldp:lldp/lldp:port/lldp:message-fast-tx" {
|
||||
deviate not-supported;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
import logging.handlers
|
||||
import subprocess
|
||||
import json
|
||||
import sys # (built-in module)
|
||||
import os
|
||||
@@ -76,6 +75,9 @@ def main():
|
||||
elif args.model == 'ietf-system':
|
||||
from . import ietf_system
|
||||
yang_data = ietf_system.operational()
|
||||
elif args.model == 'ieee802-dot1ab-lldp':
|
||||
from . import infix_lldp
|
||||
yang_data = infix_lldp.operational()
|
||||
else:
|
||||
common.LOG.warning("Unsupported model %s", args.model)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
from .host import HOST
|
||||
from collections import defaultdict
|
||||
|
||||
def operational():
|
||||
"""Retrieve LLDP neighbor information and store in remote-systems-data under the correct port."""
|
||||
|
||||
# Reference: https://www.ieee802.org/1/files/public/YANGs/ieee802-types.yang
|
||||
subtype_mapping = {
|
||||
"component": "chassis-component",
|
||||
"ifalias": "interface-alias",
|
||||
"port": "port-component",
|
||||
"mac": "mac-address",
|
||||
"ip": "network-address",
|
||||
"ifname": "interface-name",
|
||||
"local": "local"
|
||||
}
|
||||
|
||||
DEFAULT_MAC = "00-00-00-00-00-00"
|
||||
|
||||
port_data = defaultdict(lambda: {"remote-systems-data": [], "dest-mac-address": None})
|
||||
|
||||
data = HOST.run_json(["lldpcli", "show", "neighbors", "-f", "json"])
|
||||
|
||||
interfaces = data.get("lldp", {}).get("interface", [])
|
||||
|
||||
if isinstance(interfaces, dict):
|
||||
interfaces = [interfaces]
|
||||
|
||||
for iface_entry in interfaces:
|
||||
for iface_name, iface_data in iface_entry.items():
|
||||
remote_index = int(iface_data.get("rid", 0))
|
||||
time_mark = parse_time(iface_data.get("age"))
|
||||
|
||||
chassis = iface_data.get("chassis", {})
|
||||
chassis_id_type, chassis_id_value = extract_chassis_id(chassis, subtype_mapping)
|
||||
|
||||
port_info = iface_data.get("port", {})
|
||||
port_id_type = subtype_mapping.get(port_info.get("id", {}).get("type"), "unknown")
|
||||
port_id_value = port_info.get("id", {}).get("value", "")
|
||||
|
||||
dest_mac_address = (
|
||||
chassis_id_value.replace(":", "-") if chassis_id_type == "mac-address" else
|
||||
port_id_value.replace(":", "-") if port_id_type == "mac-address" else
|
||||
DEFAULT_MAC
|
||||
)
|
||||
|
||||
remote_entry = {
|
||||
"time-mark": time_mark,
|
||||
"remote-index": remote_index,
|
||||
"chassis-id-subtype": chassis_id_type,
|
||||
"chassis-id": chassis_id_value,
|
||||
"port-id-subtype": port_id_type,
|
||||
"port-id": port_id_value
|
||||
}
|
||||
|
||||
port_data[iface_name]["remote-systems-data"].append(remote_entry)
|
||||
|
||||
if port_data[iface_name]["dest-mac-address"] is None:
|
||||
port_data[iface_name]["dest-mac-address"] = dest_mac_address
|
||||
|
||||
formatted_output = {
|
||||
"ieee802-dot1ab-lldp:lldp": {
|
||||
"port": [
|
||||
{
|
||||
"name": port_name,
|
||||
"dest-mac-address": port_info["dest-mac-address"],
|
||||
"remote-systems-data": port_info["remote-systems-data"]
|
||||
}
|
||||
for port_name, port_info in port_data.items() if port_info["remote-systems-data"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return formatted_output
|
||||
|
||||
def extract_chassis_id(chassis_block, subtype_mapping):
|
||||
if "id" in chassis_block:
|
||||
id_info = chassis_block["id"]
|
||||
return subtype_mapping.get(id_info.get("type"), "unknown"), id_info.get("value", "")
|
||||
|
||||
for _, value in chassis_block.items():
|
||||
if isinstance(value, dict) and "id" in value:
|
||||
id_info = value["id"]
|
||||
return subtype_mapping.get(id_info.get("type"), "unknown"), id_info.get("value", "")
|
||||
|
||||
return "unknown", ""
|
||||
|
||||
def parse_time(time_str):
|
||||
"""Convert LLDP time format to seconds"""
|
||||
import re
|
||||
if time_str:
|
||||
match = re.search(r"(\d+)\s*day[s]*,\s*(\d+):(\d+):(\d+)", time_str)
|
||||
if match:
|
||||
days, hours, minutes, seconds = map(int, match.groups())
|
||||
return days * 86400 + hours * 3600 + minutes * 60 + seconds
|
||||
return 0
|
||||
@@ -41,6 +41,7 @@
|
||||
#define XPATH_ROUTING_OSPF XPATH_ROUTING_BASE "/ospf"
|
||||
#define XPATH_CONTAIN_BASE "/infix-containers:containers"
|
||||
#define XPATH_DHCP_SERVER_BASE "/infix-dhcp-server:dhcp-server"
|
||||
#define XPATH_LLDP_BASE "/ieee802-dot1ab-lldp:lldp"
|
||||
|
||||
TAILQ_HEAD(sub_head, sub);
|
||||
|
||||
@@ -344,6 +345,8 @@ static int subscribe_to_all(struct statd *statd)
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-system", XPATH_SYSTEM_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ieee802-dot1ab-lldp", XPATH_LLDP_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
#ifdef CONTAINERS
|
||||
if (subscribe(statd, "infix-containers", XPATH_CONTAIN_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
Reference in New Issue
Block a user