From 83ac26c48c9b1bd03445a4fa6b48b6965d2bea86 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Fri, 6 Apr 2018 18:42:51 +0300 Subject: [PATCH 1/8] [lldp_syncd] add new OIDs - lldpRemTable & lldpLocPortTable --- src/lldp_syncd/__init__.py | 1 + src/lldp_syncd/daemon.py | 55 ++++++++++++-- src/lldp_syncd/dbsyncd.py | 56 ++++++++++++++ src/lldp_syncd/main.py | 4 + tests/mock_tables/LLDP_ENTRY_TABLE.json | 99 +++++++++++++++++++++++++ tests/test_lldpSyncDaemon.py | 41 +++++++++- 6 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 src/lldp_syncd/dbsyncd.py diff --git a/src/lldp_syncd/__init__.py b/src/lldp_syncd/__init__.py index a8a1837..005031b 100644 --- a/src/lldp_syncd/__init__.py +++ b/src/lldp_syncd/__init__.py @@ -5,3 +5,4 @@ logger.addHandler(logging.NullHandler()) from .daemon import LldpSyncDaemon +from .dbsyncd import DBSyncDaemon \ No newline at end of file diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index b1e941a..f8f7573 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -10,7 +10,7 @@ from sonic_syncd import SonicSyncDaemon from . import logger -from .conventions import LldpPortIdSubtype, LldpChassisIdSubtype +from .conventions import LldpPortIdSubtype, LldpChassisIdSubtype, LldpSystemCapabilitiesMap LLDPD_TIME_FORMAT = '%H:%M:%S' @@ -109,6 +109,38 @@ class ChassisIdSubtypeMap(int, Enum): # chassis = int(LldpChassisIdSubtype.chassisComponent) # (unsupported by lldpd) local = int(LldpPortIdSubtype.local) + def get_sys_capability_list(self, if_attributes): + """ + Get a list of capabilities from interface attributes dictionary. + :param if_attributes: interface attributes + :return: list of capabilities + """ + try: + # [{'enabled': ..., 'type': 'capability1'}, {'enabled': ..., 'type': 'capability2'}] + capability_list = if_attributes['chassis'].values()[0]['capability'] + except KeyError: + logger.error("Failed to get system capabilities") + # {'enabled': ..., 'type': 'capability'} + if not isinstance(capability_list, list): + capability_list = [capability_list] + return capability_list + + def parse_sys_capabilities(self, capability_list, enabled=False): + """ + Get a bit map of capabilities, accoding to textual convention. + :param capability_list: list of capabilities + :param enabled: if true, consider only the enabled capabilities + :return: string representing a bit map + """ + sys_cap = 0x00 + for capability in capability_list: + try: + if (not enabled) or capability["enabled"]: + sys_cap |= 128 >> LldpSystemCapabilitiesMap[capability["type"].lower()] + except KeyError: + logger.warning("Unknown capability {}".format(capability["type"])) + return "%0.2X 00" % sys_cap + def __init__(self, update_interval=None): super(LldpSyncDaemon, self).__init__() self._update_interval = update_interval or DEFAULT_UPDATE_INTERVAL @@ -148,8 +180,8 @@ def parse_update(self, lldp_json): LldpRemEntry ::= SEQUENCE { lldpRemTimeMark TimeFilter, - *lldpRemLocalPortNum LldpPortNumber, - *lldpRemIndex Integer32, + lldpRemLocalPortNum LldpPortNumber, + lldpRemIndex Integer32, lldpRemChassisIdSubtype LldpChassisIdSubtype, lldpRemChassisId LldpChassisId, lldpRemPortIdSubtype LldpPortIdSubtype, @@ -157,11 +189,10 @@ def parse_update(self, lldp_json): lldpRemPortDesc SnmpAdminString, lldpRemSysName SnmpAdminString, lldpRemSysDesc SnmpAdminString, - *lldpRemSysCapSupported LldpSystemCapabilitiesMap, - *lldpRemSysCapEnabled LldpSystemCapabilitiesMap + lldpRemSysCapSupported LldpSystemCapabilitiesMap, + lldpRemSysCapEnabled LldpSystemCapabilitiesMap } """ - # TODO: *Implement try: interface_list = lldp_json['lldp'].get('interface') or [] parsed_interfaces = defaultdict(dict) @@ -182,6 +213,17 @@ def parse_update(self, lldp_json): # lldpRemTimeMark TimeFilter, parsed_interfaces[if_name].update({'lldp_rem_time_mark': str(parse_time(if_attributes.get('age')))}) + # lldpRemIndex + parsed_interfaces[if_name].update({'lldp_rem_index': str(if_attributes.get('rid'))}) + + capability_list = self.get_sys_capability_list(if_attributes) + # lldpSysCapSupported + parsed_interfaces[if_name].update({'lldp_rem_sys_cap_supported': + self.parse_sys_capabilities(capability_list)}) + # lldpSysCapEnabled + parsed_interfaces[if_name].update({'lldp_rem_sys_cap_enabled': + self.parse_sys_capabilities(capability_list, enabled=True)}) + return parsed_interfaces except (KeyError, ValueError): logger.exception("Failed to parse LLDPd JSON. \n{}\n -- ".format(lldp_json)) @@ -239,6 +281,7 @@ def sync(self, parsed_update): # Repopulate LLDP_ENTRY_TABLE by adding all elements from parsed_update for interface, if_attributes in parsed_update.items(): + if re.match(SONIC_ETHERNET_RE_PATTERN, interface) is None: logger.warning("Ignoring interface '{}'".format(interface)) continue diff --git a/src/lldp_syncd/dbsyncd.py b/src/lldp_syncd/dbsyncd.py new file mode 100644 index 0000000..6772d9c --- /dev/null +++ b/src/lldp_syncd/dbsyncd.py @@ -0,0 +1,56 @@ +import subprocess +from swsssdk import ConfigDBConnector + +from sonic_syncd import SonicSyncDaemon +from . import logger + + +class DBSyncDaemon(SonicSyncDaemon): + """ + A Thread that listens to changes in CONFIG DB, + and contains handlers to configure lldpd accordingly. + """ + + def __init__(self): + super(DBSyncDaemon, self).__init__() + self.config_db = ConfigDBConnector() + self.config_db.connect() + logger.info("[lldp dbsyncd] Connected to configdb") + self.port_table = {} + + def run_command(self, command): + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + stdout = p.communicate()[0] + p.wait() + if p.returncode != 0: + logger.error('[lldp dbsyncd] command execution returned {}. \ + Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) + + def port_handler(self, key, data): + """ + Handle updates in 'PORT' table. + """ + # we're interested only in description for now + if self.port_table[key].get("description") != data.get("description"): + new_descr = data.get("description", "") + logger.info("[lldp dbsyncd] Port {} description changed to {}." + .format(key, new_descr)) + self.run_command("lldpcli configure lldp portidsubtype local {} \ + description '{}'".format(key, new_descr)) + # update local cache + self.port_table[key] = data + + def run(self): + self.port_table = self.config_db.get_table('PORT') + # supply LLDP_LOC_ENTRY_TABLE and lldpd with correct values on start + for port_name, attributes in self.port_table.items(): + self.run_command("lldpcli configure lldp portidsubtype local \ + {} description '{}'".format( + port_name, attributes.get("description", " "))) + + # subscribe for further changes + self.config_db.subscribe('PORT', lambda table, key, data: + self.port_handler(key, data)) + + logger.info("[lldp dbsyncd] Subscribed to configdb PORT table") + self.config_db.listen() diff --git a/src/lldp_syncd/main.py b/src/lldp_syncd/main.py index 4f81d41..0042dc9 100644 --- a/src/lldp_syncd/main.py +++ b/src/lldp_syncd/main.py @@ -1,5 +1,6 @@ from . import logger from .daemon import LldpSyncDaemon +from .dbsyncd import DBSyncDaemon DEFAULT_UPDATE_FREQUENCY = 10 @@ -8,8 +9,11 @@ def main(update_frequency=None): try: lldp_syncd = LldpSyncDaemon(update_frequency or DEFAULT_UPDATE_FREQUENCY) logger.info('Starting SONiC LLDP sync daemon...') + dbsyncd = DBSyncDaemon() lldp_syncd.start() + dbsyncd.start() lldp_syncd.join() + dbsyncd.join() except KeyboardInterrupt: logger.info("ctrl-C captured, shutting down.") except Exception: diff --git a/tests/mock_tables/LLDP_ENTRY_TABLE.json b/tests/mock_tables/LLDP_ENTRY_TABLE.json index 12c14dc..e11813b 100644 --- a/tests/mock_tables/LLDP_ENTRY_TABLE.json +++ b/tests/mock_tables/LLDP_ENTRY_TABLE.json @@ -1,5 +1,8 @@ { "LLDP_ENTRY_TABLE:Ethernet100": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -10,6 +13,9 @@ "lldp_rem_port_id": "Ethernet26" }, "LLDP_ENTRY_TABLE:Ethernet4": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -20,6 +26,9 @@ "lldp_rem_port_id": "Ethernet2" }, "LLDP_ENTRY_TABLE:Ethernet104": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -30,6 +39,9 @@ "lldp_rem_port_id": "Ethernet27" }, "LLDP_ENTRY_TABLE:Ethernet0": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -40,6 +52,9 @@ "lldp_rem_port_id": "Ethernet1" }, "LLDP_ENTRY_TABLE:Ethernet108": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -50,6 +65,9 @@ "lldp_rem_port_id": "Ethernet28" }, "LLDP_ENTRY_TABLE:Ethernet8": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -60,6 +78,9 @@ "lldp_rem_port_id": "Ethernet3" }, "LLDP_ENTRY_TABLE:Ethernet96": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -70,6 +91,9 @@ "lldp_rem_port_id": "Ethernet25" }, "LLDP_ENTRY_TABLE:Ethernet92": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -80,6 +104,9 @@ "lldp_rem_port_id": "Ethernet24" }, "LLDP_ENTRY_TABLE:Ethernet124": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -90,6 +117,9 @@ "lldp_rem_port_id": "Ethernet32" }, "LLDP_ENTRY_TABLE:Ethernet120": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -100,6 +130,9 @@ "lldp_rem_port_id": "Ethernet31" }, "LLDP_ENTRY_TABLE:Ethernet40": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -110,6 +143,9 @@ "lldp_rem_port_id": "Ethernet11" }, "LLDP_ENTRY_TABLE:Ethernet44": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -120,6 +156,9 @@ "lldp_rem_port_id": "Ethernet12" }, "LLDP_ENTRY_TABLE:Ethernet48": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -130,6 +169,9 @@ "lldp_rem_port_id": "Ethernet13" }, "LLDP_ENTRY_TABLE:Ethernet24": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -140,6 +182,9 @@ "lldp_rem_port_id": "Ethernet7" }, "LLDP_ENTRY_TABLE:Ethernet68": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -150,6 +195,9 @@ "lldp_rem_port_id": "Ethernet18" }, "LLDP_ENTRY_TABLE:eth0": { + "lldp_rem_index": 2, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm going to need you to come in on saturday", @@ -160,6 +208,9 @@ "lldp_rem_port_id": "ge-0/0/17" }, "LLDP_ENTRY_TABLE:Ethernet20": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -170,6 +221,9 @@ "lldp_rem_port_id": "Ethernet6" }, "LLDP_ENTRY_TABLE:Ethernet60": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -180,6 +234,9 @@ "lldp_rem_port_id": "Ethernet16" }, "LLDP_ENTRY_TABLE:Ethernet64": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -190,6 +247,9 @@ "lldp_rem_port_id": "Ethernet17" }, "LLDP_ENTRY_TABLE:Ethernet112": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -200,6 +260,9 @@ "lldp_rem_port_id": "Ethernet29" }, "LLDP_ENTRY_TABLE:Ethernet116": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -210,6 +273,9 @@ "lldp_rem_port_id": "Ethernet30" }, "LLDP_ENTRY_TABLE:Ethernet84": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -220,6 +286,9 @@ "lldp_rem_port_id": "Ethernet22" }, "LLDP_ENTRY_TABLE:Ethernet80": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -230,6 +299,9 @@ "lldp_rem_port_id": "Ethernet21" }, "LLDP_ENTRY_TABLE:Ethernet88": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -240,6 +312,9 @@ "lldp_rem_port_id": "Ethernet23" }, "LLDP_ENTRY_TABLE:Ethernet52": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -250,6 +325,9 @@ "lldp_rem_port_id": "Ethernet14" }, "LLDP_ENTRY_TABLE:Ethernet76": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -260,6 +338,9 @@ "lldp_rem_port_id": "Ethernet20" }, "LLDP_ENTRY_TABLE:Ethernet56": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -270,6 +351,9 @@ "lldp_rem_port_id": "Ethernet15" }, "LLDP_ENTRY_TABLE:Ethernet72": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -280,6 +364,9 @@ "lldp_rem_port_id": "Ethernet19" }, "LLDP_ENTRY_TABLE:Ethernet32": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -290,6 +377,9 @@ "lldp_rem_port_id": "Ethernet9" }, "LLDP_ENTRY_TABLE:Ethernet28": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -300,6 +390,9 @@ "lldp_rem_port_id": "Ethernet8" }, "LLDP_ENTRY_TABLE:Ethernet36": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -310,6 +403,9 @@ "lldp_rem_port_id": "Ethernet10" }, "LLDP_ENTRY_TABLE:Ethernet16": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", @@ -320,6 +416,9 @@ "lldp_rem_port_id": "Ethernet5" }, "LLDP_ENTRY_TABLE:Ethernet12": { + "lldp_rem_index": 1, + "lldp_rem_sys_cap_enabled": "28 00", + "lldp_rem_sys_cap_supported": "28 00", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", diff --git a/tests/test_lldpSyncDaemon.py b/tests/test_lldpSyncDaemon.py index a9bde20..250113b 100644 --- a/tests/test_lldpSyncDaemon.py +++ b/tests/test_lldpSyncDaemon.py @@ -9,11 +9,13 @@ from unittest import TestCase import json +import mock import re import lldp_syncd import lldp_syncd.conventions import lldp_syncd.daemon -from swsssdk import SonicV2Connector +import lldp_syncd.dbsyncd +from swsssdk import SonicV2Connector, ConfigDBConnector INPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'subproc_outputs') @@ -86,3 +88,40 @@ def test_sync_roundtrip(self): def test_timeparse(self): self.assertEquals(lldp_syncd.daemon.parse_time("0 day, 05:09:02"), make_seconds(0, 5, 9, 2)) self.assertEquals(lldp_syncd.daemon.parse_time("2 days, 05:59:02"), make_seconds(2, 5, 59, 2)) + + +class TestLldpSyncDaemonDBSync(TestCase): + + def setUp(self): + with mock.patch.object(lldp_syncd.DBSyncDaemon, "__init__", lambda _: None): + self.daemon = lldp_syncd.DBSyncDaemon() + self.daemon.port_table = {"Ethernet0": + {'description': "Hedgehog", "speed":50000}, + "Ethernet4": + {'description': "Red door'", "speed":50000}} + + def test_port_handler_descr(self): + """ + test handling update of description of port + """ + with mock.patch.object(lldp_syncd.DBSyncDaemon, "run_command", mock.Mock()): + self.daemon.port_handler("Ethernet4", {"description": "black door", "speed":50000}) + self.daemon.run_command.assert_called_once_with( + "lldpcli configure lldp portidsubtype local Ethernet4 description 'black door'") + + def test_port_handler_speed(self): + """ + test updating port speed(no action expected) + """ + with mock.patch.object(lldp_syncd.DBSyncDaemon, "run_command", mock.Mock()): + self.daemon.port_handler("Ethernet0", {"speed": 100000, "description": "Hedgehog"}) + self.daemon.run_command.assert_not_called() + + def test_port_handler_delete_descr(self): + """ + test handling update when description field is removed + """ + with mock.patch.object(lldp_syncd.DBSyncDaemon, "run_command", mock.Mock()): + self.daemon.port_handler("Ethernet4", {"speed":50000}) + self.daemon.run_command.assert_called_once_with( + "lldpcli configure lldp portidsubtype local Ethernet4 description ''") \ No newline at end of file From 8caefe6c3bfe68a731e04479217f937ea5ec8c59 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Thu, 5 Jul 2018 18:39:50 +0300 Subject: [PATCH 2/8] [lldp_syncd] add new OIDs - lldpLocalSystemData --- src/lldp_syncd/daemon.py | 81 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index f8f7573..af563cf 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -153,23 +153,27 @@ def source_update(self): """ cmd = ['/usr/sbin/lldpctl', '-f', 'json'] logger.debug("Invoking lldpctl with: {}".format(cmd)) + cmd_local = ['/usr/sbin/lldpcli', '-f', 'json', 'show', 'chassis'] + logger.debug("Invoking lldpcli with: {}".format(cmd_local)) - try: - # execute the subprocess command - lldpctl_output = subprocess.check_output(cmd) - except subprocess.CalledProcessError: - logger.exception("lldpctl exited with non-zero status") - return None - - try: - # parse the scrapped output - lldpctl_json = json.loads(lldpctl_output) - except ValueError: - logger.exception("Failed to parse lldpctl output") - return None - else: + def scrap_output(cmd): + try: + # execute the subprocess command + lldpctl_output = subprocess.check_output(cmd) + lldpctl_json = json.loads(lldpctl_output) + except subprocess.CalledProcessError: + logger.exception("lldpctl exited with non-zero status") + return {} + except ValueError: + logger.exception("Failed to parse lldpctl output") + return {} return lldpctl_json + lldp_json = scrap_output(cmd) + lldp_json['lldp_loc_chassis'] = scrap_output(cmd_local) + + return lldp_json + def parse_update(self, lldp_json): """ Parse lldpd output to extract @@ -206,9 +210,13 @@ def parse_update(self, lldp_json): if_attributes = interface_list[if_name] if 'port' in if_attributes: - parsed_interfaces[if_name].update(self.parse_port(if_attributes['port'])) + rem_port_keys = ('lldp_rem_port_id_subtype', 'lldp_rem_port_id', 'lldp_rem_port_desc') + parsed_interfaces[if_name].update(zip(rem_port_keys, self.parse_port(if_attributes['port']))) if 'chassis' in if_attributes: - parsed_interfaces[if_name].update(self.parse_chassis(if_attributes['chassis'])) + rem_chassis_keys = ('lldp_rem_chassis_id_subtype', 'lldp_rem_chassis_id', + 'lldp_rem_sys_name', 'lldp_rem_sys_desc') + parsed_interfaces[if_name].update(zip(rem_chassis_keys, + self.parse_chassis(if_attributes['chassis']))) # lldpRemTimeMark TimeFilter, parsed_interfaces[if_name].update({'lldp_rem_time_mark': str(parse_time(if_attributes.get('age')))}) @@ -223,6 +231,13 @@ def parse_update(self, lldp_json): # lldpSysCapEnabled parsed_interfaces[if_name].update({'lldp_rem_sys_cap_enabled': self.parse_sys_capabilities(capability_list, enabled=True)}) + if lldp_json['lldp_loc_chassis']: + loc_chassis_keys = ('lldp_loc_chassis_id_subtype', 'lldp_loc_chassis_id', + 'lldp_loc_sys_name', 'lldp_loc_sys_desc') + + parsed_interfaces['local-chassis'].update(zip(loc_chassis_keys, + self.parse_chassis(lldp_json['lldp_loc_chassis'] + ['local-chassis']['chassis']))) return parsed_interfaces except (KeyError, ValueError): @@ -238,16 +253,11 @@ def parse_chassis(self, chassis_attributes): logger.exception("Could not infer system information from: {}".format(chassis_attributes)) chassis_id_subtype = chassis_id = rem_name = rem_desc = None - return { - # lldpRemChassisIdSubtype LldpChassisIdSubtype, - 'lldp_rem_chassis_id_subtype': chassis_id_subtype, - # lldpRemChassisId LldpChassisId, - 'lldp_rem_chassis_id': chassis_id, - # lldpRemSysName SnmpAdminString, - 'lldp_rem_sys_name': rem_name, - # lldpRemSysDesc SnmpAdminString, - 'lldp_rem_sys_desc': rem_desc, - } + return (chassis_id_subtype, + chassis_id, + rem_name, + rem_desc, + ) def parse_port(self, port_attributes): port_identifiers = port_attributes.get('id') @@ -259,14 +269,10 @@ def parse_port(self, port_attributes): logger.exception("Could not infer chassis subtype from: {}".format(port_attributes)) subtype, value = None - return { - # lldpRemPortIdSubtype LldpPortIdSubtype, - 'lldp_rem_port_id_subtype': subtype, - # lldpRemPortId LldpPortId, - 'lldp_rem_port_id': value, - # lldpRemSysDesc SnmpAdminString, - 'lldp_rem_port_desc': port_attributes.get('descr') - } + return (subtype, + value, + port_attributes.get('descr'), + ) def sync(self, parsed_update): """ @@ -278,7 +284,12 @@ def sync(self, parsed_update): client = self.db_connector.redis_clients[self.db_connector.APPL_DB] pattern = '{}:*'.format(LldpSyncDaemon.LLDP_ENTRY_TABLE) self.db_connector.delete_all_by_pattern(self.db_connector.APPL_DB, pattern) - + # push local chassis data to APP DB + for k, v in parsed_update['local-chassis'].items(): + self.db_connector.set(self.db_connector.APPL_DB, "LLDP_LOC_CHASSIS", k, v, blocking=True) + logger.debug("sync'd: {}".format(json.dumps(parsed_update['local-chassis'], indent=3))) + # leave only interfaces in parsed_update + parsed_update.pop('local-chassis') # Repopulate LLDP_ENTRY_TABLE by adding all elements from parsed_update for interface, if_attributes in parsed_update.items(): From 880760c8e2b1329daedeac2be4e2de070e67a33d Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Mon, 9 Jul 2018 18:50:36 +0300 Subject: [PATCH 3/8] enhance code style, set default port descr to ' ' instead of '' --- src/lldp_syncd/dbsyncd.py | 15 +++++++-------- tests/test_lldpSyncDaemon.py | 10 +++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/lldp_syncd/dbsyncd.py b/src/lldp_syncd/dbsyncd.py index 6772d9c..5085693 100644 --- a/src/lldp_syncd/dbsyncd.py +++ b/src/lldp_syncd/dbsyncd.py @@ -23,8 +23,8 @@ def run_command(self, command): stdout = p.communicate()[0] p.wait() if p.returncode != 0: - logger.error('[lldp dbsyncd] command execution returned {}. \ - Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) + logger.error("[lldp dbsyncd] command execution returned {}. " + "Command: '{}', stdout: '{}'".format(p.returncode, command, stdout)) def port_handler(self, key, data): """ @@ -32,11 +32,11 @@ def port_handler(self, key, data): """ # we're interested only in description for now if self.port_table[key].get("description") != data.get("description"): - new_descr = data.get("description", "") + new_descr = data.get("description", " ") logger.info("[lldp dbsyncd] Port {} description changed to {}." .format(key, new_descr)) - self.run_command("lldpcli configure lldp portidsubtype local {} \ - description '{}'".format(key, new_descr)) + self.run_command("lldpcli configure lldp portidsubtype local {} description '{}'" + .format(key, new_descr)) # update local cache self.port_table[key] = data @@ -44,9 +44,8 @@ def run(self): self.port_table = self.config_db.get_table('PORT') # supply LLDP_LOC_ENTRY_TABLE and lldpd with correct values on start for port_name, attributes in self.port_table.items(): - self.run_command("lldpcli configure lldp portidsubtype local \ - {} description '{}'".format( - port_name, attributes.get("description", " "))) + self.run_command("lldpcli configure lldp portidsubtype local {} description '{}'" + .format(port_name, attributes.get("description", " "))) # subscribe for further changes self.config_db.subscribe('PORT', lambda table, key, data: diff --git a/tests/test_lldpSyncDaemon.py b/tests/test_lldpSyncDaemon.py index 250113b..35a0d9d 100644 --- a/tests/test_lldpSyncDaemon.py +++ b/tests/test_lldpSyncDaemon.py @@ -96,16 +96,16 @@ def setUp(self): with mock.patch.object(lldp_syncd.DBSyncDaemon, "__init__", lambda _: None): self.daemon = lldp_syncd.DBSyncDaemon() self.daemon.port_table = {"Ethernet0": - {'description': "Hedgehog", "speed":50000}, + {'description': "Hedgehog", "speed": 50000}, "Ethernet4": - {'description': "Red door'", "speed":50000}} + {'description': "Red door'", "speed": 50000}} def test_port_handler_descr(self): """ test handling update of description of port """ with mock.patch.object(lldp_syncd.DBSyncDaemon, "run_command", mock.Mock()): - self.daemon.port_handler("Ethernet4", {"description": "black door", "speed":50000}) + self.daemon.port_handler("Ethernet4", {"description": "black door", "speed": 50000}) self.daemon.run_command.assert_called_once_with( "lldpcli configure lldp portidsubtype local Ethernet4 description 'black door'") @@ -122,6 +122,6 @@ def test_port_handler_delete_descr(self): test handling update when description field is removed """ with mock.patch.object(lldp_syncd.DBSyncDaemon, "run_command", mock.Mock()): - self.daemon.port_handler("Ethernet4", {"speed":50000}) + self.daemon.port_handler("Ethernet4", {"speed": 50000}) self.daemon.run_command.assert_called_once_with( - "lldpcli configure lldp portidsubtype local Ethernet4 description ''") \ No newline at end of file + "lldpcli configure lldp portidsubtype local Ethernet4 description ' '") From 0f00688e4316e69b5a3e2d273f3dec7cbf1d454c Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Fri, 13 Jul 2018 17:39:35 +0300 Subject: [PATCH 4/8] [lldp_syncd] introduce cache to avoid writing to db every 5s --- src/lldp_syncd/daemon.py | 56 +++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index af563cf..6cee634 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -56,7 +56,7 @@ class LldpSyncDaemon(SonicSyncDaemon): within the same Redis instance on a switch """ LLDP_ENTRY_TABLE = 'LLDP_ENTRY_TABLE' - + LLDP_LOC_CHASSIS_TABLE = 'LLDP_LOC_CHASSIS' @unique class PortIdSubtypeMap(int, Enum): """ @@ -147,6 +147,9 @@ def __init__(self, update_interval=None): self.db_connector = SonicV2Connector() self.db_connector.connect(self.db_connector.APPL_DB) + self.chassis_cache = {} + self.interfaces_cache = {} + def source_update(self): """ Invoke lldpctl and format as JSON @@ -271,33 +274,50 @@ def parse_port(self, port_attributes): return (subtype, value, - port_attributes.get('descr'), + port_attributes.get('descr', ''), ) + def cache_diff(self, cache, update): + """ + Find difference in keys between update and local cache dicts + :param cache: Local cache dict + :param update: Update dict + :return: new, changed, deleted keys tuple + """ + new_keys = [key for key in update.keys() if key not in cache.keys()] + changed_keys = list(set([key for key in update.keys() + cache.keys() + if update[key] != cache.get(key)])) + deleted_keys = [key for key in cache.keys() if key not in update.keys()] + return new_keys, changed_keys, deleted_keys + def sync(self, parsed_update): """ Sync LLDP information to redis DB. """ logger.debug("Initiating LLDPd sync to Redis...") - - # First, delete all entries from the LLDP_ENTRY_TABLE client = self.db_connector.redis_clients[self.db_connector.APPL_DB] - pattern = '{}:*'.format(LldpSyncDaemon.LLDP_ENTRY_TABLE) - self.db_connector.delete_all_by_pattern(self.db_connector.APPL_DB, pattern) - # push local chassis data to APP DB - for k, v in parsed_update['local-chassis'].items(): - self.db_connector.set(self.db_connector.APPL_DB, "LLDP_LOC_CHASSIS", k, v, blocking=True) - logger.debug("sync'd: {}".format(json.dumps(parsed_update['local-chassis'], indent=3))) - # leave only interfaces in parsed_update - parsed_update.pop('local-chassis') - # Repopulate LLDP_ENTRY_TABLE by adding all elements from parsed_update - for interface, if_attributes in parsed_update.items(): + # push local chassis data to APP DB + chassis_update = parsed_update.pop('local-chassis') + if chassis_update != self.chassis_cache: + self.db_connector.delete(self.db_connector.APPL_DB, LldpSyncDaemon.LLDP_LOC_CHASSIS_TABLE) + for k, v in chassis_update.items(): + self.db_connector.set(self.db_connector.APPL_DB, + LldpSyncDaemon.LLDP_LOC_CHASSIS_TABLE, k, v, blocking=True) + logger.debug("sync'd: {}".format(json.dumps(chassis_update, indent=3))) + + new, changed, deleted = self.cache_diff(self.interfaces_cache, parsed_update) + # Delete LLDP_ENTRIES which were modified or are missing + for interface in changed + deleted: + table_key = ':'.join([LldpSyncDaemon.LLDP_ENTRY_TABLE, interface]) + self.db_connector.delete(self.db_connector.APPL_DB, table_key) + # Repopulate LLDP_ENTRY_TABLE by adding all changed elements + for interface in changed + new: if re.match(SONIC_ETHERNET_RE_PATTERN, interface) is None: logger.warning("Ignoring interface '{}'".format(interface)) continue - for k, v in if_attributes.items(): - # port_table_key = LLDP_ENTRY_TABLE:INTERFACE_NAME; - table_key = ':'.join([LldpSyncDaemon.LLDP_ENTRY_TABLE, interface]) + # port_table_key = LLDP_ENTRY_TABLE:INTERFACE_NAME; + table_key = ':'.join([LldpSyncDaemon.LLDP_ENTRY_TABLE, interface]) + for k, v in parsed_update[interface].items(): self.db_connector.set(self.db_connector.APPL_DB, table_key, k, v, blocking=True) - logger.debug("sync'd: \n{}".format(json.dumps(if_attributes, indent=3))) + logger.debug("sync'd: \n{}".format(json.dumps(parsed_update[interface], indent=3))) From 0deebf0e8fd1e85ef17b9ba50715a700d215faf8 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Tue, 17 Jul 2018 18:48:08 +0300 Subject: [PATCH 5/8] review comments - merge conflict, exception handling, pep8 --- src/lldp_syncd/daemon.py | 97 ++++++++++++++++--------- tests/mock_tables/LLDP_ENTRY_TABLE.json | 70 +++++++++--------- tests/subproc_outputs/lldpctl.json | 60 ++++++++++----- 3 files changed, 140 insertions(+), 87 deletions(-) diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index 6cee634..bfabd3e 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -44,7 +44,8 @@ def parse_time(time_str): """ days, hour_min_secs = re.split(LLDPD_UPTIME_RE_SPLIT_PATTERN, time_str) struct_time = time.strptime(hour_min_secs, LLDPD_TIME_FORMAT) - time_delta = datetime.timedelta(days=int(days), hours=struct_time.tm_hour, minutes=struct_time.tm_min, + time_delta = datetime.timedelta(days=int(days), hours=struct_time.tm_hour, + minutes=struct_time.tm_min, seconds=struct_time.tm_sec) return int(time_delta.total_seconds()) @@ -57,6 +58,7 @@ class LldpSyncDaemon(SonicSyncDaemon): """ LLDP_ENTRY_TABLE = 'LLDP_ENTRY_TABLE' LLDP_LOC_CHASSIS_TABLE = 'LLDP_LOC_CHASSIS' + @unique class PortIdSubtypeMap(int, Enum): """ @@ -118,11 +120,12 @@ def get_sys_capability_list(self, if_attributes): try: # [{'enabled': ..., 'type': 'capability1'}, {'enabled': ..., 'type': 'capability2'}] capability_list = if_attributes['chassis'].values()[0]['capability'] + # {'enabled': ..., 'type': 'capability'} + if not isinstance(capability_list, list): + capability_list = [capability_list] except KeyError: logger.error("Failed to get system capabilities") - # {'enabled': ..., 'type': 'capability'} - if not isinstance(capability_list, list): - capability_list = [capability_list] + return None return capability_list def parse_sys_capabilities(self, capability_list, enabled=False): @@ -132,6 +135,11 @@ def parse_sys_capabilities(self, capability_list, enabled=False): :param enabled: if true, consider only the enabled capabilities :return: string representing a bit map """ + # if chassis is incomplete, missing capabilities + # TODO check if it does not break snmpagent + if not capability_list: + return None + sys_cap = 0x00 for capability in capability_list: try: @@ -163,13 +171,17 @@ def scrap_output(cmd): try: # execute the subprocess command lldpctl_output = subprocess.check_output(cmd) - lldpctl_json = json.loads(lldpctl_output) except subprocess.CalledProcessError: - logger.exception("lldpctl exited with non-zero status") - return {} + logger.exception("lldpctl exited with non-zero status") + return None + + try: + # parse the scrapped output + lldpctl_json = json.loads(lldpctl_output) except ValueError: logger.exception("Failed to parse lldpctl output") - return {} + return None + return lldpctl_json lldp_json = scrap_output(cmd) @@ -213,16 +225,24 @@ def parse_update(self, lldp_json): if_attributes = interface_list[if_name] if 'port' in if_attributes: - rem_port_keys = ('lldp_rem_port_id_subtype', 'lldp_rem_port_id', 'lldp_rem_port_desc') - parsed_interfaces[if_name].update(zip(rem_port_keys, self.parse_port(if_attributes['port']))) + rem_port_keys = ('lldp_rem_port_id_subtype', + 'lldp_rem_port_id', + 'lldp_rem_port_desc') + parsed_port = zip(rem_port_keys, self.parse_port(if_attributes['port'])) + parsed_interfaces[if_name].update(parsed_port) + if 'chassis' in if_attributes: - rem_chassis_keys = ('lldp_rem_chassis_id_subtype', 'lldp_rem_chassis_id', - 'lldp_rem_sys_name', 'lldp_rem_sys_desc') - parsed_interfaces[if_name].update(zip(rem_chassis_keys, - self.parse_chassis(if_attributes['chassis']))) + rem_chassis_keys = ('lldp_rem_chassis_id_subtype', + 'lldp_rem_chassis_id', + 'lldp_rem_sys_name', + 'lldp_rem_sys_desc') + parsed_chassis = zip(rem_chassis_keys, + self.parse_chassis(if_attributes['chassis'])) + parsed_interfaces[if_name].update(parsed_chassis) # lldpRemTimeMark TimeFilter, - parsed_interfaces[if_name].update({'lldp_rem_time_mark': str(parse_time(if_attributes.get('age')))}) + parsed_interfaces[if_name].update({'lldp_rem_time_mark': + str(parse_time(if_attributes.get('age')))}) # lldpRemIndex parsed_interfaces[if_name].update({'lldp_rem_index': str(if_attributes.get('rid'))}) @@ -230,17 +250,20 @@ def parse_update(self, lldp_json): capability_list = self.get_sys_capability_list(if_attributes) # lldpSysCapSupported parsed_interfaces[if_name].update({'lldp_rem_sys_cap_supported': - self.parse_sys_capabilities(capability_list)}) + self.parse_sys_capabilities(capability_list)}) # lldpSysCapEnabled parsed_interfaces[if_name].update({'lldp_rem_sys_cap_enabled': - self.parse_sys_capabilities(capability_list, enabled=True)}) + self.parse_sys_capabilities( + capability_list, enabled=True)}) if lldp_json['lldp_loc_chassis']: - loc_chassis_keys = ('lldp_loc_chassis_id_subtype', 'lldp_loc_chassis_id', - 'lldp_loc_sys_name', 'lldp_loc_sys_desc') - - parsed_interfaces['local-chassis'].update(zip(loc_chassis_keys, - self.parse_chassis(lldp_json['lldp_loc_chassis'] - ['local-chassis']['chassis']))) + loc_chassis_keys = ('lldp_loc_chassis_id_subtype', + 'lldp_loc_chassis_id', + 'lldp_loc_sys_name', + 'lldp_loc_sys_desc') + parsed_chassis = zip(loc_chassis_keys, + self.parse_chassis(lldp_json['lldp_loc_chassis'] + ['local-chassis']['chassis'])) + parsed_interfaces['local-chassis'].update(parsed_chassis) return parsed_interfaces except (KeyError, ValueError): @@ -248,18 +271,26 @@ def parse_update(self, lldp_json): def parse_chassis(self, chassis_attributes): try: - (rem_name, rem_attributes), = chassis_attributes.items() - chassis_id_subtype = str(self.ChassisIdSubtypeMap[rem_attributes['id']['type']].value) - chassis_id = rem_attributes['id']['value'] - rem_desc = rem_attributes.get('descr') + if 'id' in chassis_attributes and 'id' not in chassis_attributes['id']: + sys_name = '' + attributes = chassis_attributes + id_attributes = chassis_attributes['id'] + else: + (sys_name, attributes) = chassis_attributes.items()[0] + id_attributes = attributes.get('id', '') + + chassis_id_subtype = str(self.ChassisIdSubtypeMap[id_attributes['type']].value) + chassis_id = id_attributes.get('value', '') + descr = attributes.get('descr', '') except (KeyError, ValueError): - logger.exception("Could not infer system information from: {}".format(chassis_attributes)) - chassis_id_subtype = chassis_id = rem_name = rem_desc = None + logger.exception("Could not infer system information from: {}" + .format(chassis_attributes)) + chassis_id_subtype = chassis_id = sys_name = descr = '' return (chassis_id_subtype, chassis_id, - rem_name, - rem_desc, + sys_name, + descr, ) def parse_port(self, port_attributes): @@ -295,12 +326,12 @@ def sync(self, parsed_update): Sync LLDP information to redis DB. """ logger.debug("Initiating LLDPd sync to Redis...") - client = self.db_connector.redis_clients[self.db_connector.APPL_DB] # push local chassis data to APP DB chassis_update = parsed_update.pop('local-chassis') if chassis_update != self.chassis_cache: - self.db_connector.delete(self.db_connector.APPL_DB, LldpSyncDaemon.LLDP_LOC_CHASSIS_TABLE) + self.db_connector.delete(self.db_connector.APPL_DB, + LldpSyncDaemon.LLDP_LOC_CHASSIS_TABLE) for k, v in chassis_update.items(): self.db_connector.set(self.db_connector.APPL_DB, LldpSyncDaemon.LLDP_LOC_CHASSIS_TABLE, k, v, blocking=True) diff --git a/tests/mock_tables/LLDP_ENTRY_TABLE.json b/tests/mock_tables/LLDP_ENTRY_TABLE.json index e11813b..7bdc4cf 100644 --- a/tests/mock_tables/LLDP_ENTRY_TABLE.json +++ b/tests/mock_tables/LLDP_ENTRY_TABLE.json @@ -7,7 +7,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18544, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet26" @@ -20,7 +20,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet2" @@ -33,7 +33,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18544, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet27" @@ -59,7 +59,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18544, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet28" @@ -72,22 +72,22 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet3" }, "LLDP_ENTRY_TABLE:Ethernet96": { "lldp_rem_index": 1, - "lldp_rem_sys_cap_enabled": "28 00", - "lldp_rem_sys_cap_supported": "28 00", + "lldp_rem_sys_cap_enabled": null, + "lldp_rem_sys_cap_supported": null, "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", - "lldp_rem_sys_desc": "I'm a little teapot.", + "lldp_rem_sys_desc": "", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, - "lldp_rem_sys_name": "switch13", + "lldp_rem_sys_name": "", "lldp_rem_port_id": "Ethernet25" }, "LLDP_ENTRY_TABLE:Ethernet92": { @@ -98,7 +98,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 32, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet24" @@ -111,7 +111,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 4834, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet32" @@ -124,7 +124,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 3844, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet31" @@ -137,7 +137,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 572, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet11" @@ -150,7 +150,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1202, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet12" @@ -163,7 +163,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1202, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet13" @@ -176,7 +176,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1775, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet7" @@ -189,7 +189,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet18" @@ -215,7 +215,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet6" @@ -228,7 +228,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet16" @@ -241,7 +241,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet17" @@ -254,7 +254,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 2103, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet29" @@ -267,7 +267,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1113, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet30" @@ -280,7 +280,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 362, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet22" @@ -293,7 +293,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 362, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet21" @@ -306,7 +306,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 92, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet23" @@ -319,7 +319,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet14" @@ -332,7 +332,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet20" @@ -345,7 +345,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet15" @@ -358,7 +358,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet19" @@ -371,7 +371,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1142, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet9" @@ -384,7 +384,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18513, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet8" @@ -397,7 +397,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 152, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet10" @@ -410,7 +410,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet5" @@ -423,7 +423,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": null, + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet4" diff --git a/tests/subproc_outputs/lldpctl.json b/tests/subproc_outputs/lldpctl.json index 87e103e..d4199bb 100644 --- a/tests/subproc_outputs/lldpctl.json +++ b/tests/subproc_outputs/lldpctl.json @@ -1,4 +1,40 @@ { + "lldp_loc_chassis":{ + "local-chassis": { + "chassis": { + "arc-switch1025": { + "mgmt-ip": [ + "10.1.0.32", + "fc00:1::32" + ], + "id": { + "type": "mac", + "value": "7c:fe:90:63:f4:66" + }, + "capability": [ + { + "type": "Bridge", + "enabled": true + }, + { + "type": "Router", + "enabled": true + }, + { + "type": "Wlan", + "enabled": false + }, + { + "type": "Station", + "enabled": false + } + ], + "descr": "Debian GNU/Linux 8 (jessie) Linux 3.16.0-5-amd64 #1 SMP Debian 3.16.51-3+deb8u1 (2018-01-08) x86_64", + "ttl": "120" + } + } + } + }, "lldp": { "interface": [ { @@ -1313,25 +1349,11 @@ }, "via": "LLDP", "chassis": { - "switch13": { - "mgmt-ip": "10.3.147.196", - "id": { - "type": "mac", - "value": "00:11:22:33:44:55" - }, - "ttl": "120", - "descr": "I'm a little teapot.", - "capability": [ - { - "type": "Bridge", - "enabled": true - }, - { - "type": "Router", - "enabled": true - } - ] - } + "id": { + "type": "mac", + "value": "00:11:22:33:44:55" + }, + "ttl": "120" }, "age": "0 day, 05:09:03", "vlan": { From 42ec989b58dea59bee2512cc62dee2f62c8fc480 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Tue, 17 Jul 2018 20:37:09 +0300 Subject: [PATCH 6/8] LLDP_ENTRY_TABLE fix indentation --- tests/mock_tables/LLDP_ENTRY_TABLE.json | 58 ++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/mock_tables/LLDP_ENTRY_TABLE.json b/tests/mock_tables/LLDP_ENTRY_TABLE.json index 7bdc4cf..675382e 100644 --- a/tests/mock_tables/LLDP_ENTRY_TABLE.json +++ b/tests/mock_tables/LLDP_ENTRY_TABLE.json @@ -33,7 +33,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18544, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet27" @@ -59,7 +59,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18544, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet28" @@ -72,7 +72,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet3" @@ -85,7 +85,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "", "lldp_rem_port_id": "Ethernet25" @@ -98,7 +98,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 32, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet24" @@ -111,7 +111,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 4834, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet32" @@ -124,7 +124,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 3844, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet31" @@ -137,7 +137,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 572, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet11" @@ -150,7 +150,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1202, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet12" @@ -163,7 +163,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1202, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet13" @@ -176,7 +176,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1775, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet7" @@ -189,7 +189,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet18" @@ -215,7 +215,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet6" @@ -228,7 +228,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet16" @@ -241,7 +241,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet17" @@ -254,7 +254,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 2103, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet29" @@ -267,7 +267,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1113, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet30" @@ -280,7 +280,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 362, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet22" @@ -293,7 +293,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 362, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet21" @@ -306,7 +306,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 92, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet23" @@ -319,7 +319,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet14" @@ -332,7 +332,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet20" @@ -345,7 +345,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet15" @@ -358,7 +358,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18543, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet19" @@ -371,7 +371,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 1142, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet9" @@ -384,7 +384,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18513, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet8" @@ -397,7 +397,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 152, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet10" @@ -410,7 +410,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet5" @@ -423,7 +423,7 @@ "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "I'm a little teapot.", "lldp_rem_time_mark": 18545, - "lldp_rem_port_desc": "", + "lldp_rem_port_desc": "", "lldp_rem_chassis_id_subtype": 4, "lldp_rem_sys_name": "switch13", "lldp_rem_port_id": "Ethernet4" From 0124733304223359d8b1245b9ad9940a62c490f8 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Wed, 18 Jul 2018 14:23:02 +0300 Subject: [PATCH 7/8] review comments - move scrap_output() out of class, return '' for missing system capabilities --- src/lldp_syncd/daemon.py | 48 ++++++++++++------------- tests/mock_tables/LLDP_ENTRY_TABLE.json | 4 +-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index bfabd3e..523373a 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -20,6 +20,22 @@ LLDPD_UPTIME_RE_SPLIT_PATTERN = r' days?, ' MANAGEMENT_PORT_NAME = 'eth0' +def _scrap_output(cmd): + try: + # execute the subprocess command + lldpctl_output = subprocess.check_output(cmd) + except subprocess.CalledProcessError: + logger.exception("lldpctl exited with non-zero status") + return None + + try: + # parse the scrapped output + lldpctl_json = json.loads(lldpctl_output) + except ValueError: + logger.exception("Failed to parse lldpctl output") + return None + + return lldpctl_json def parse_time(time_str): """ @@ -125,7 +141,7 @@ def get_sys_capability_list(self, if_attributes): capability_list = [capability_list] except KeyError: logger.error("Failed to get system capabilities") - return None + return [] return capability_list def parse_sys_capabilities(self, capability_list, enabled=False): @@ -135,10 +151,9 @@ def parse_sys_capabilities(self, capability_list, enabled=False): :param enabled: if true, consider only the enabled capabilities :return: string representing a bit map """ - # if chassis is incomplete, missing capabilities - # TODO check if it does not break snmpagent + # chassis is incomplete, missing capabilities if not capability_list: - return None + return "" sys_cap = 0x00 for capability in capability_list: @@ -167,25 +182,8 @@ def source_update(self): cmd_local = ['/usr/sbin/lldpcli', '-f', 'json', 'show', 'chassis'] logger.debug("Invoking lldpcli with: {}".format(cmd_local)) - def scrap_output(cmd): - try: - # execute the subprocess command - lldpctl_output = subprocess.check_output(cmd) - except subprocess.CalledProcessError: - logger.exception("lldpctl exited with non-zero status") - return None - - try: - # parse the scrapped output - lldpctl_json = json.loads(lldpctl_output) - except ValueError: - logger.exception("Failed to parse lldpctl output") - return None - - return lldpctl_json - - lldp_json = scrap_output(cmd) - lldp_json['lldp_loc_chassis'] = scrap_output(cmd_local) + lldp_json = _scrap_output(cmd) + lldp_json['lldp_loc_chassis'] = _scrap_output(cmd_local) return lldp_json @@ -316,8 +314,8 @@ def cache_diff(self, cache, update): :return: new, changed, deleted keys tuple """ new_keys = [key for key in update.keys() if key not in cache.keys()] - changed_keys = list(set([key for key in update.keys() + cache.keys() - if update[key] != cache.get(key)])) + changed_keys = list(set(key for key in update.keys() + cache.keys() + if update[key] != cache.get(key))) deleted_keys = [key for key in cache.keys() if key not in update.keys()] return new_keys, changed_keys, deleted_keys diff --git a/tests/mock_tables/LLDP_ENTRY_TABLE.json b/tests/mock_tables/LLDP_ENTRY_TABLE.json index 675382e..a1c7a15 100644 --- a/tests/mock_tables/LLDP_ENTRY_TABLE.json +++ b/tests/mock_tables/LLDP_ENTRY_TABLE.json @@ -79,8 +79,8 @@ }, "LLDP_ENTRY_TABLE:Ethernet96": { "lldp_rem_index": 1, - "lldp_rem_sys_cap_enabled": null, - "lldp_rem_sys_cap_supported": null, + "lldp_rem_sys_cap_enabled": "", + "lldp_rem_sys_cap_supported": "", "lldp_rem_port_id_subtype": 5, "lldp_rem_chassis_id": "00:11:22:33:44:55", "lldp_rem_sys_desc": "", From 348906f723fbae4c7b1be5bafef3e9bacc0bfcb5 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Wed, 18 Jul 2018 22:26:12 +0300 Subject: [PATCH 8/8] review comments - test for isinstance dict, move _scrap_output() --- src/lldp_syncd/daemon.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/lldp_syncd/daemon.py b/src/lldp_syncd/daemon.py index 523373a..82dcff4 100644 --- a/src/lldp_syncd/daemon.py +++ b/src/lldp_syncd/daemon.py @@ -20,22 +20,6 @@ LLDPD_UPTIME_RE_SPLIT_PATTERN = r' days?, ' MANAGEMENT_PORT_NAME = 'eth0' -def _scrap_output(cmd): - try: - # execute the subprocess command - lldpctl_output = subprocess.check_output(cmd) - except subprocess.CalledProcessError: - logger.exception("lldpctl exited with non-zero status") - return None - - try: - # parse the scrapped output - lldpctl_json = json.loads(lldpctl_output) - except ValueError: - logger.exception("Failed to parse lldpctl output") - return None - - return lldpctl_json def parse_time(time_str): """ @@ -137,7 +121,7 @@ def get_sys_capability_list(self, if_attributes): # [{'enabled': ..., 'type': 'capability1'}, {'enabled': ..., 'type': 'capability2'}] capability_list = if_attributes['chassis'].values()[0]['capability'] # {'enabled': ..., 'type': 'capability'} - if not isinstance(capability_list, list): + if isinstance(capability_list, dict): capability_list = [capability_list] except KeyError: logger.error("Failed to get system capabilities") @@ -173,6 +157,24 @@ def __init__(self, update_interval=None): self.chassis_cache = {} self.interfaces_cache = {} + @staticmethod + def _scrap_output(cmd): + try: + # execute the subprocess command + lldpctl_output = subprocess.check_output(cmd) + except subprocess.CalledProcessError: + logger.exception("lldpctl exited with non-zero status") + return None + + try: + # parse the scrapped output + lldpctl_json = json.loads(lldpctl_output) + except ValueError: + logger.exception("Failed to parse lldpctl output") + return None + + return lldpctl_json + def source_update(self): """ Invoke lldpctl and format as JSON @@ -182,8 +184,8 @@ def source_update(self): cmd_local = ['/usr/sbin/lldpcli', '-f', 'json', 'show', 'chassis'] logger.debug("Invoking lldpcli with: {}".format(cmd_local)) - lldp_json = _scrap_output(cmd) - lldp_json['lldp_loc_chassis'] = _scrap_output(cmd_local) + lldp_json = self._scrap_output(cmd) + lldp_json['lldp_loc_chassis'] = self._scrap_output(cmd_local) return lldp_json