From 696b9f6024d142c02bd429a7f14300c9ffdeb0b8 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 10:58:30 +0000 Subject: [PATCH 01/11] Fix _send_command for non-xml data --- napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index a4b3397..80faaab 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -66,12 +66,10 @@ def __init__( def _send_command(self, command, xml_format=False): """Send command to device""" - # if true: - # append xml to the end of the command if xml_format: - command = command + " " + "xml" - output = self.device.send_command(command, expect_string=r"#$") - return output + command += " xml" + output = self.device.send_command(command, expect_string=r"#$") + return output # elif "info" in command: # output = self.device.send_command(command, expect_string=r"#$") # return output @@ -274,6 +272,9 @@ def get_facts(self): if "serial-no" in dummy_data: serial_number = dummy_data["serial-no"] facts["serial_number"] = serial_number + if "variant" in dummy_data: + variant = dummy_data["variant"] + facts["model"] += f" ({variant})" # get uptime for line in uptime_output.splitlines(): From cdf6580f97333428faf15433b342684ac646df49 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 11:05:55 +0000 Subject: [PATCH 02/11] Cleanup comments and fix indentation level --- .../napalm_nokia_olt/nokia_olt.py | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index 80faaab..9b4e504 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -70,12 +70,6 @@ def _send_command(self, command, xml_format=False): command += " xml" output = self.device.send_command(command, expect_string=r"#$") return output - # elif "info" in command: - # output = self.device.send_command(command, expect_string=r"#$") - # return output - # else: - # output = self.device.send_command(command, expect_string=r"(#|$)") - # return output def open(self): """Open an SSH tunnel connection to the device.""" @@ -90,8 +84,6 @@ def open(self): **self.netmiko_optional_args, ) self._prep_session() - # ensure in enable mode - # self.device.enable() def close(self): """Close the connection to the device.""" @@ -583,13 +575,13 @@ def get_equipment_diagnostics_sfp(self): return f"No available ** {command} ** data from the {self.hostname}" def get_vlan_name(self): - """Returns VLANs info""" - command = "show vlan name" - data = self._send_command(command, xml_format=True) - if data: - return self.convert_xml_to_dict(data) - else: - return f"No available ** {command} ** data from the {self.hostname}" + """Returns VLANs info""" + command = "show vlan name" + data = self._send_command(command, xml_format=True) + if data: + return self.convert_xml_to_dict(data) + else: + return f"No available ** {command} ** data from the {self.hostname}" def get_vlan_bridge_port_fdb(self): """Returns VLANs info details""" From 478fa5109ce567400a98b66a7c8817dca901f0c6 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 11:38:17 +0000 Subject: [PATCH 03/11] Accelerate get_facts by using different port command --- napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index 9b4e504..ddec0f0 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -219,7 +219,7 @@ def get_facts(self): os_command = "show software-mngt version ansi" uptime_command = "show core1-uptime" sn_command = "show equipment shelf 1/1 detail" - port_command = "show interface port" + port_command = "show equipment ont interface" hostname_output = self._send_command(hostname_command, xml_format=True) os_output = self._send_command(os_command, xml_format=True) @@ -283,11 +283,8 @@ def get_facts(self): # get interface_list for elem in port_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) - if "port" in dummy_data: - port_raw = dummy_data["port"] - if "vlan" in port_raw: - continue - port_list.append(port_raw) + if "ont-idx" in dummy_data: + port_list.append(dummy_data["ont-idx"]) port_list.sort() facts["interface_list"] = port_list return facts From a2abe74e3a984c8c0221722b75a9a22ff023b800 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 11:44:26 +0000 Subject: [PATCH 04/11] Fix order of interfaces in get_facts --- napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index ddec0f0..6af542a 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -285,7 +285,7 @@ def get_facts(self): dummy_data = self._convert_xml_elem_to_dict(elem=elem) if "ont-idx" in dummy_data: port_list.append(dummy_data["ont-idx"]) - port_list.sort() + port_list.sort(key=lambda p: list(map(int, p.split("/")))) facts["interface_list"] = port_list return facts From a24eb0ef33063688f107e0f4fc62f9eae32f56b4 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 13:49:20 +0000 Subject: [PATCH 05/11] Return correct data for get_interfaces method --- .../napalm_nokia_olt/nokia_olt.py | 74 +++++++++++++++---- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index 6af542a..75e9c68 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -631,22 +631,64 @@ def get_ntp_servers(self): return dict(ntp_servers) def get_interfaces(self): - """Returns a list of all interfaces in a dict""" - port_command = "show interface port" - port_output = self._send_command(port_command, xml_format=True) - port_xml_tree = ET.fromstring(port_output) + """ + Returns a dictionary of dictionaries for all interfaces + + Example Output: + { + '1/1/1/1/1': {'is_enabled': True, + 'is_up': True, + 'description': 'test description', + 'mac_address': '0000.0000.0000', + 'last_flapped': -1.0, + 'mtu': 0, + 'speed': 1000} + } - interfaces = {} - port_list = [] + mac_address is not implemented + last_flapped is not implemented + mtu is not implemented - # get a list of interfaces - for elem in port_xml_tree.findall(".//instance"): + """ + pon_command = "show equipment ont status pon" + xpon_command = "show equipment ont status x-pon" + pon_output = self._send_command(pon_command, xml_format=True) + xpon_output = self._send_command(xpon_command, xml_format=True) + pon_xml_tree = ET.fromstring(pon_output) + xpon_xml_tree = ET.fromstring(xpon_output) + + interface_dict = {} + + # parse 1GE ports + for elem in pon_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) - if "port" in dummy_data: - port_raw = dummy_data["port"] - if "vlan" in port_raw: - continue - port_list.append(port_raw) - port_list.sort() - interfaces["interface_list"] = port_list - return interfaces \ No newline at end of file + if "ont" in dummy_data: + is_enabled = bool("up" in dummy_data.get("admin-status", "")) + is_up = bool("up" in dummy_data.get("oper-status", "")) + interface_dict[dummy_data["ont"]] = { + "is_enabled": is_enabled, + "is_up": is_up, + "description": dummy_data.get("desc1", ""), + "mac_address": "0000.0000.0000", + "last_flapped": -1.0, + "mtu": 0, + "speed": 1000, + } + + # parse 10GE ports (X-PON) + for elem in xpon_xml_tree.findall(".//instance"): + dummy_data = self._convert_xml_elem_to_dict(elem=elem) + if "ont" in dummy_data: + is_enabled = bool("up" in dummy_data.get("admin-status", "")) + is_up = bool("up" in dummy_data.get("oper-status", "")) + interface_dict[dummy_data["ont"]] = { + "is_enabled": is_enabled, + "is_up": is_up, + "description": dummy_data.get("desc1", ""), + "mac_address": "0000.0000.0000", + "last_flapped": -1.0, + "mtu": 0, + "speed": 10000, + } + + return interface_dict \ No newline at end of file From 563f605265de8fb3f2da47cc2d46584edcf46c48 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 14:10:23 +0000 Subject: [PATCH 06/11] Change vlan_id type to int to comply with other drivers --- napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index 75e9c68..dc936dd 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -307,7 +307,7 @@ def get_vlans(self): # create default dict and get vlan_id and name for elem in output_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) - primary_key = dummy_data["id"] + primary_key = int(dummy_data["id"]) if primary_key not in vlans: vlans[primary_key] = {} vlans[primary_key]["name"] = dummy_data["name"] @@ -316,7 +316,7 @@ def get_vlans(self): # get tagged/untagged ports for elem in tag_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) - vlan_id = dummy_data["vlan-id"] + vlan_id = int(dummy_data["vlan-id"]) port_raw = dummy_data["vlan-port"] port = ":".join( port_raw.replace("vlan-port", "uni").split(":")[0:2] From 68d2323a30de688bd4930999834073967815a296 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Mon, 5 Feb 2024 14:22:04 +0000 Subject: [PATCH 07/11] Unify interface names in get_vlans output --- napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index dc936dd..477b4eb 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -318,9 +318,7 @@ def get_vlans(self): dummy_data = self._convert_xml_elem_to_dict(elem=elem) vlan_id = int(dummy_data["vlan-id"]) port_raw = dummy_data["vlan-port"] - port = ":".join( - port_raw.replace("vlan-port", "uni").split(":")[0:2] - ) + port = port_raw.split(":")[1] if ( "single-tagged" in dummy_data["transmit-mode"] or "untagged" in dummy_data["transmit-mode"] From 5007031cd8327daf6a794fe4fd40bda7d6d5dab2 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Thu, 8 Feb 2024 15:35:25 +0000 Subject: [PATCH 08/11] Add methods is_aliva, get_lldp_neighbors, get_lldp_neighbors_detail --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01874b5..befec3d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@
  • get_config
  • get_facts
  • get_vlans
  • +
  • is_alive
  • +
  • get_interfaces
  • +
  • get_lldp_neighbors
  • +
  • get_lldp_neighbors_detail
  • get_equipment_ont_status_xpon
  • get_equipment_ont_interfaces
  • get_equipment_ont_status_pon
  • @@ -28,7 +32,6 @@
  • get_equipment_temperature
  • cli
  • get_ntp_servers
  • -
  • get_interfaces
  • send_single_command
  • -

    Each function will always return the output as dict data-type

    \ No newline at end of file +

    Each function will always return the output as dict data-type

    From 1f693b3884ee7fd1c6db76b334dc51dd39d3d8d9 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Thu, 8 Feb 2024 15:36:12 +0000 Subject: [PATCH 09/11] Add new methods get_lldp_neighbors and get_lldp_neighbors_detail --- .../napalm_nokia_olt/nokia_olt.py | 93 ++++++++++++++++++- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py index 477b4eb..75396c6 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py +++ b/napalm_nokia_olt/napalm_nokia_olt/nokia_olt.py @@ -4,6 +4,7 @@ from __future__ import print_function from __future__ import unicode_literals import socket +import re from netmiko import ConnectHandler from napalm.base.base import NetworkDriver import xml.etree.ElementTree as ET @@ -630,7 +631,7 @@ def get_ntp_servers(self): def get_interfaces(self): """ - Returns a dictionary of dictionaries for all interfaces + Returns a dictionary of dictionaries for all ONT interfaces Example Output: { @@ -656,7 +657,7 @@ def get_interfaces(self): xpon_xml_tree = ET.fromstring(xpon_output) interface_dict = {} - + # parse 1GE ports for elem in pon_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) @@ -672,7 +673,7 @@ def get_interfaces(self): "mtu": 0, "speed": 1000, } - + # parse 10GE ports (X-PON) for elem in xpon_xml_tree.findall(".//instance"): dummy_data = self._convert_xml_elem_to_dict(elem=elem) @@ -689,4 +690,88 @@ def get_interfaces(self): "speed": 10000, } - return interface_dict \ No newline at end of file + return interface_dict + + def get_lldp_neighbors(self): + """Returns a dictionary with LLDP neighbors""" + port_command = "show port" + port_data = self._send_command(port_command, xml_format=False) + ports = [] + lldp = {} + port_regex = r"^(?!=|-|\s|Port|Id.).+$" + remote_host_regex = r"^System Name[\s:]+(.+)$" + remote_port_regex = r"""^Port Id[\s:]+([0-9a-zA-Z:]+[\n][\s"\/a-zA-Z0-9]+$)""" + all_ports = re.findall(port_regex, port_data, re.MULTILINE) + for line in all_ports: + line = line.split() + if len(line) > 0: + if not line[-1] == "vport": + ports.append(line[0]) + for port in ports: + lldp_command = f"show port {port} ethernet lldp remote-info" + lldp_data = self._send_command(lldp_command, xml_format=False) + remote_host = re.search(remote_host_regex, lldp_data, re.MULTILINE) + if remote_host: + lldp[port] = [{"hostname": remote_host.group(1), "port": ""}] + try: + remote_port_data = re.search(remote_port_regex, lldp_data, re.MULTILINE) + remote_port = remote_port_data.group(1).split()[1] + lldp[port][0]["port"] = remote_port.replace('"','') + except IndexError: + continue + return lldp + + def get_lldp_neighbors_detail(self): + """Returns a detailed view of the LLDP neighbors as a dictionary""" + port_command = "show port" + port_data = self._send_command(port_command, xml_format=False) + ports = [] + lldp = {} + port_regex = r"^(?!=|-|\s|Port|Id.).+$" + remote_host_regex = r"^System Name[\s:]+(.+)$" + remote_port_regex = r"""^Port Id[\s:]+([0-9a-zA-Z:]+[\n][\s"\/a-zA-Z0-9]+$)""" + remote_chassis_regex = r"^Chassis Id[\s]{3,}[:\s](.+)$" + remote_port_descr_regex = r"^Port Description[:\s]+(.+)$" + remote_sys_descr_regex = r"(?<=^System Description)\s*:\s(.*)(?=\n\n\n)" + remote_sys_cap_regex = r"^Supported Caps[\s:]+(.+)$" + remote_sys_en_cap_regex = r"^Enabled Caps[\s:]+(.+)$" + all_ports = re.findall(port_regex, port_data, re.MULTILINE) + for line in all_ports: + line = line.split() + if len(line) > 0: + if not line[-1] == "vport": + ports.append(line[0]) + for port in ports: + lldp_command = f"show port {port} ethernet lldp remote-info" + lldp_data = self._send_command(lldp_command, xml_format=False) + remote_host = re.findall(remote_host_regex, lldp_data, re.MULTILINE) + if len(remote_host) > 0: + lldp[port] = [ + {"parent_interface": port, + "remote_chassis_id": "", + "remote_system_name": remote_host[0], + "remote_port": "", + "remote_port_description": "", + "remote_system_description": "", + "remote_system_capab": "", + "remote_system_enable_capab": "", + } + ] + remote_chassis_id_data = re.search(remote_chassis_regex, lldp_data, re.MULTILINE) + remote_port_descr_data = re.search(remote_port_descr_regex, lldp_data, re.MULTILINE) + remote_sys_descr_data = re.search(remote_sys_descr_regex, lldp_data, flags = re.S | re.M) + remote_sys_cap_data = re.search(remote_sys_cap_regex, lldp_data, re.MULTILINE) + remote_sys_en_cap_data = re.search(remote_sys_en_cap_regex, lldp_data, re.MULTILINE) + lldp[port][0]["remote_chassis_id"] = remote_chassis_id_data.group(1) + lldp[port][0]["remote_port_description"] = remote_port_descr_data.group(1) + lldp[port][0]["remote_system_description"] = remote_sys_descr_data.group(1) + lldp[port][0]["remote_system_description"] = ' '.join(lldp[port][0]["remote_system_description"].split()) + lldp[port][0]["remote_system_capab"] = remote_sys_cap_data.group(1) + lldp[port][0]["remote_system_enable_capab"] = remote_sys_en_cap_data.group(1) + try: + remote_port_data = re.search(remote_port_regex, lldp_data, re.MULTILINE) + remote_port = remote_port_data.group(1).split()[1] + lldp[port][0]["remote_port"] = remote_port.replace('"','') + except IndexError: + continue + return lldp From 656eb5b42c643f468ba43f458c6d0e8836145558 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Thu, 8 Feb 2024 15:43:40 +0000 Subject: [PATCH 10/11] Remove dependency sshFRIEND --- napalm_nokia_olt/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napalm_nokia_olt/setup.py b/napalm_nokia_olt/setup.py index 1311932..beb31c8 100644 --- a/napalm_nokia_olt/setup.py +++ b/napalm_nokia_olt/setup.py @@ -26,5 +26,5 @@ "License :: OSI Approved :: BSD License", ], include_package_data=True, - install_requires=('napalm>=3','xmltodict', 'sshFRIEND'), + install_requires=('napalm>=3','xmltodict'), ) From f246e3798226054a08a0935ffd05470b74f72675 Mon Sep 17 00:00:00 2001 From: "106186411+skarpe-github@users.noreply.github.com" Date: Thu, 8 Feb 2024 15:44:45 +0000 Subject: [PATCH 11/11] Update to version 0.0.48 --- napalm_nokia_olt/napalm_nokia_olt/__init__.py | 2 +- napalm_nokia_olt/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/napalm_nokia_olt/napalm_nokia_olt/__init__.py b/napalm_nokia_olt/napalm_nokia_olt/__init__.py index 564f437..77a665f 100644 --- a/napalm_nokia_olt/napalm_nokia_olt/__init__.py +++ b/napalm_nokia_olt/napalm_nokia_olt/__init__.py @@ -4,4 +4,4 @@ from napalm_nokia_olt.nokia_olt import NokiaOltDriver __all__ = ("NokiaOltDriver",) -__version__ = "0.0.47" +__version__ = "0.0.48" diff --git a/napalm_nokia_olt/setup.py b/napalm_nokia_olt/setup.py index beb31c8..df5b1f7 100644 --- a/napalm_nokia_olt/setup.py +++ b/napalm_nokia_olt/setup.py @@ -9,7 +9,7 @@ setuptools.setup( name="napalm_nokia_olt", - version="0.0.47", + version="0.0.48", author="Dave Macias", author_email = "davama@gmail.com", description=("Network Automation and Programmability Abstraction "