From c4f9bec562d8d96ea332087277f7d568ee904868 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Mon, 26 Oct 2020 08:52:20 -0700 Subject: [PATCH] [minigraph.py]: Add support for parsing mux cable (#5676) Find LogicalLinks in minigraph and parse the port information. A new field called `mux_cable` is added to each port's entry in the Port table in config DB: ``` PORT|Ethernet0: { "alias": "Ethernet4/1" ... "mux_cable": "true" } ``` If a mux cable is present on a port, the value for `mux_cable` will be `"true"`. If no mux cable is present, the attribute will either be omitted (default behavior) or set to `"false"`. --- src/sonic-config-engine/minigraph.py | 20 +++++++++++++++-- .../tests/simple-sample-graph-case.xml | 22 +++++++++++++++++++ src/sonic-config-engine/tests/test_cfggen.py | 4 ++-- .../tests/test_minigraph_case.py | 11 ++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index d53a56d7da..294fb7faaa 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -90,6 +90,7 @@ def parse_png(png, hname): mgmt_port = '' port_speeds = {} console_ports = {} + mux_cable_ports = {} for child in png: if child.tag == str(QName(ns, "DeviceInterfaceLinks")): for link in child.findall(str(QName(ns, "DeviceLinkBase"))): @@ -162,7 +163,16 @@ def parse_png(png, hname): elif node.tag == str(QName(ns, "EndDevice")): mgmt_dev = node.text - return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speeds, console_ports) + if child.tag == str(QName(ns, "DeviceInterfaceLinks")): + for link in child.findall(str(QName(ns, 'DeviceLinkBase'))): + if link.find(str(QName(ns, "ElementType"))).text == "LogicalLink": + intf_name = link.find(str(QName(ns, "EndPort"))).text + if intf_name in port_alias_map: + intf_name = port_alias_map[intf_name] + + mux_cable_ports[intf_name] = "true" + + return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speeds, console_ports, mux_cable_ports) def parse_asic_external_link(link, asic_name, hostname): neighbors = {} @@ -831,6 +841,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw port_speed_png = {} port_descriptions = {} console_ports = {} + mux_cable_ports = {} syslog_servers = [] dhcp_servers = [] ntp_servers = [] @@ -873,7 +884,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname) elif child.tag == str(QName(ns, "PngDec")): - (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speed_png, console_ports) = parse_png(child, hostname) + (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speed_png, console_ports, mux_cable_ports) = parse_png(child, hostname) elif child.tag == str(QName(ns, "UngDec")): (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname) elif child.tag == str(QName(ns, "MetadataDeclaration")): @@ -1009,6 +1020,11 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw if port.get('speed') == '100000' and linkmetas.get(alias, {}).get('FECDisabled', '').lower() != 'true': port['fec'] = 'rs' + # If connected to a smart cable, get the connection position + for port_name, port in ports.items(): + if port_name in mux_cable_ports: + port['mux_cable'] = mux_cable_ports[port_name] + # set port description if parsed from deviceinfo for port_name in port_descriptions: # ignore port not in port_config.ini diff --git a/src/sonic-config-engine/tests/simple-sample-graph-case.xml b/src/sonic-config-engine/tests/simple-sample-graph-case.xml index acfcd4a6a7..3188409b1f 100644 --- a/src/sonic-config-engine/tests/simple-sample-graph-case.xml +++ b/src/sonic-config-engine/tests/simple-sample-graph-case.xml @@ -209,6 +209,28 @@ switch-02t1 port1 + + LogicalLink + 10000 + false + switch-t0 + fortyGigE0/4 + true + mux-cable + L + true + + + LogicalLink + 10000 + false + switch-t0 + fortyGigE0/8 + true + mux-cable + U + true + diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index 1fe1ad0384..0d2f94899e 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -346,9 +346,9 @@ class TestCfgGen(TestCase): self.assertEqual( utils.to_dict(output.strip()), utils.to_dict( - "{'Ethernet8': {'lanes': '37,38,39,40', 'description': 'Interface description', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/8', 'admin_status': 'up', 'speed': '40000'}, " + "{'Ethernet8': {'lanes': '37,38,39,40', 'description': 'Interface description', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/8', 'admin_status': 'up', 'speed': '40000', 'mux_cable': 'true'}, " "'Ethernet0': {'lanes': '29,30,31,32', 'description': 'switch-01t1:port1', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/0', 'admin_status': 'up', 'speed': '10000'}, " - "'Ethernet4': {'lanes': '25,26,27,28', 'description': 'fortyGigE0/4', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/4', 'admin_status': 'up', 'speed': '25000'}, " + "'Ethernet4': {'lanes': '25,26,27,28', 'description': 'fortyGigE0/4', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/4', 'admin_status': 'up', 'speed': '25000', 'mux_cable': 'true'}, " "'Ethernet108': {'alias': 'fortyGigE0/108', 'pfc_asym': 'off', 'lanes': '81,82,83,84', 'description': 'fortyGigE0/108', 'mtu': '9100'}, " "'Ethernet100': {'alias': 'fortyGigE0/100', 'pfc_asym': 'off', 'lanes': '125,126,127,128', 'description': 'fortyGigE0/100', 'mtu': '9100'}, " "'Ethernet104': {'alias': 'fortyGigE0/104', 'pfc_asym': 'off', 'lanes': '85,86,87,88', 'description': 'fortyGigE0/104', 'mtu': '9100'}, " diff --git a/src/sonic-config-engine/tests/test_minigraph_case.py b/src/sonic-config-engine/tests/test_minigraph_case.py index f9c390b653..42e5e63ac6 100644 --- a/src/sonic-config-engine/tests/test_minigraph_case.py +++ b/src/sonic-config-engine/tests/test_minigraph_case.py @@ -2,6 +2,7 @@ import os import subprocess import tests.common_utils as utils +import minigraph from unittest import TestCase @@ -159,3 +160,13 @@ class TestCfgGenCaseInsensitive(TestCase): output = self.run_script(argument) self.assertEqual(output.strip(), "{}") + def test_mux_cable_parsing(self): + result = minigraph.parse_xml(self.sample_graph, port_config_file=self.port_config) + + expected_mux_cable_ports = ["Ethernet4", "Ethernet8"] + port_table = result['PORT'] + for port_name, port in port_table.items(): + if port_name in expected_mux_cable_ports: + self.assertTrue(port["mux_cable"]) + else: + self.assertTrue("mux_cable" not in port)