From ba92a081ce1981e3a1ba8991c4fb19c2122c6ade Mon Sep 17 00:00:00 2001 From: kktheballer Date: Wed, 30 Dec 2020 15:18:21 -0800 Subject: [PATCH] Minigraph ECMP parsing support (cleaner format) (#4985) Why I did it To support FG_ECMP scenarios - How I did it Modified minigraph parser to parse ECMP fields in the case they are present in minigraph - How to verify it Loaded ensuing config_db file on a DUT to verify the fields are parsed and configure device correctly --- src/sonic-config-engine/minigraph.py | 162 +- .../tests/fg-ecmp-sample-minigraph.xml | 1591 +++++++++++++++++ .../tests/mellanox-sample-port-config.ini | 65 + src/sonic-config-engine/tests/test_cfggen.py | 30 + 4 files changed, 1839 insertions(+), 9 deletions(-) create mode 100644 src/sonic-config-engine/tests/fg-ecmp-sample-minigraph.xml create mode 100644 src/sonic-config-engine/tests/mellanox-sample-port-config.ini diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index b8a87f372abb..660606b4519d 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -10,6 +10,7 @@ from lxml import etree as ET from lxml.etree import QName + from portconfig import get_port_config from sonic_py_common.multi_asic import get_asic_id_from_name from sonic_py_common.interface import backplane_prefix @@ -105,7 +106,73 @@ def parse_device(device): return (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id) -def parse_png(png, hname): +def calculate_lcm_for_ecmp (nhdevices_bank_map, nhip_bank_map): + banks_enumerated = {} + lcm_array = [] + for value in nhdevices_bank_map.values(): + for key in nhip_bank_map.keys(): + if nhip_bank_map[key] == value: + if value not in banks_enumerated: + banks_enumerated[value] = 1 + else: + banks_enumerated[value] = banks_enumerated[value] + 1 + for bank_enumeration in banks_enumerated.values(): + lcm_list = range(1, bank_enumeration+1) + lcm_comp = lcm_list[0] + for i in lcm_list[1:]: + lcm_comp = lcm_comp * i / calculate_gcd(lcm_comp, i) + lcm_array.append(lcm_comp) + + LCM = sum(lcm_array) + return int(LCM) + +def calculate_gcd(x, y): + while y != 0: + (x, y) = (y, x % y) + return int(x) + +def formulate_fine_grained_ecmp(version, dpg_ecmp_content, port_device_map, port_alias_map): + family = "" + tag = "" + neigh_key = [] + if version == "ipv4": + family = "IPV4" + tag = "fgnhg_v4" + elif version == "ipv6": + family = "IPV6" + tag = "fgnhg_v6" + + port_nhip_map = dpg_ecmp_content['port_nhip_map'] + nhgaddr = dpg_ecmp_content['nhgaddr'] + nhg_int = dpg_ecmp_content['nhg_int'] + + nhip_device_map = {port_nhip_map[x]: port_device_map[x] for x in port_device_map + if x in port_nhip_map} + nhip_devices = sorted(list(set(nhip_device_map.values()))) + nhdevices_ip_bank_map = {device: bank for bank, device in enumerate(nhip_devices)} + nhip_bank_map = {ip: nhdevices_ip_bank_map[device] for ip, device in nhip_device_map.items()} + LCM = calculate_lcm_for_ecmp(nhdevices_ip_bank_map, nhip_bank_map) + + FG_NHG_MEMBER = {ip: {"FG_NHG": tag, "bank": bank} for ip, bank in nhip_bank_map.items()} + nhip_port_map = dict(zip(port_nhip_map.values(), port_nhip_map.keys())) + + + + for nhip, memberinfo in FG_NHG_MEMBER.items(): + if nhip in nhip_port_map: + memberinfo["link"] = port_alias_map[nhip_port_map[nhip]] + FG_NHG_MEMBER[nhip] = memberinfo + + FG_NHG_PREFIX = {nhgaddr: {"FG_NHG": tag}} + FG_NHG = {tag: {"bucket_size": LCM}} + for ip in nhip_bank_map: + neigh_key.append(str(nhg_int + "|" + ip)) + NEIGH = {neigh_key: {"family": family} for neigh_key in neigh_key} + + fine_grained_content = {"FG_NHG_MEMBER": FG_NHG_MEMBER, "FG_NHG": FG_NHG, "FG_NHG_PREFIX": FG_NHG_PREFIX, "NEIGH": NEIGH} + return fine_grained_content + +def parse_png(png, hname, dpg_ecmp_content = None): neighbors = {} devices = {} console_dev = '' @@ -116,6 +183,13 @@ def parse_png(png, hname): console_ports = {} mux_cable_ports = {} is_storage_device = False + port_device_map = {} + png_ecmp_content = {} + FG_NHG_MEMBER = {} + FG_NHG_PREFIX = {} + FG_NHG = {} + NEIGH = {} + for child in png: if child.tag == str(QName(ns, "DeviceInterfaceLinks")): for link in child.findall(str(QName(ns, "DeviceLinkBase"))): @@ -141,6 +215,11 @@ def parse_png(png, hname): } continue + if linktype == "DeviceInterfaceLink": + endport = link.find(str(QName(ns, "EndPort"))).text + startdevice = link.find(str(QName(ns, "StartDevice"))).text + port_device_map[endport] = startdevice + if linktype != "DeviceInterfaceLink" and linktype != "UnderlayInterfaceLink": continue @@ -196,6 +275,7 @@ def parse_png(png, hname): elif node.tag == str(QName(ns, "EndDevice")): mgmt_dev = node.text + 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": @@ -205,7 +285,19 @@ def parse_png(png, hname): mux_cable_ports[intf_name] = "true" - return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speeds, console_ports, mux_cable_ports, is_storage_device) + if (len(dpg_ecmp_content)): + for version, content in dpg_ecmp_content.items(): # version is ipv4 or ipv6 + fine_grained_content = formulate_fine_grained_ecmp(version, content, port_device_map, port_alias_map) # port_alias_map + FG_NHG_MEMBER.update(fine_grained_content['FG_NHG_MEMBER']) + FG_NHG_PREFIX.update(fine_grained_content['FG_NHG_PREFIX']) + FG_NHG.update(fine_grained_content['FG_NHG']) + NEIGH.update(fine_grained_content['NEIGH']) + + png_ecmp_content = {"FG_NHG_PREFIX": FG_NHG_PREFIX, "FG_NHG_MEMBER": FG_NHG_MEMBER, "FG_NHG": FG_NHG, + "NEIGH": NEIGH} + + return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speeds, console_ports, mux_cable_ports, is_storage_device, png_ecmp_content) + def parse_asic_external_link(link, asic_name, hostname): neighbors = {} @@ -295,6 +387,8 @@ def parse_asic_png(png, asic_name, hostname): if lo_prefix_v6: device_data['lo_addr_v6']= lo_prefix_v6 devices[name] = device_data + + return (neighbors, devices, port_speeds) def parse_loopback_intf(child): @@ -339,12 +433,13 @@ def parse_dpg(dpg, hname): ipintfs = child.find(str(QName(ns, "IPInterfaces"))) intfs = {} + ip_intfs_map = {} for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))): intfalias = ipintf.find(str(QName(ns, "AttachTo"))).text intfname = port_alias_map.get(intfalias, intfalias) ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text intfs[(intfname, ipprefix)] = {} - + ip_intfs_map[ipprefix] = intfalias lo_intfs = parse_loopback_intf(child) mvrfConfigs = child.find(str(QName(ns, "MgmtVrfConfigs"))) @@ -381,7 +476,46 @@ def parse_dpg(dpg, hname): pcs[pcintfname] = {'members': pcmbr_list, 'fallback': pcintf.find(str(QName(ns, "Fallback"))).text, 'min_links': str(int(math.ceil(len() * 0.75)))} else: pcs[pcintfname] = {'members': pcmbr_list, 'min_links': str(int(math.ceil(len(pcmbr_list) * 0.75)))} - + port_nhipv4_map = {} + port_nhipv6_map = {} + nhgaddr = ["", ""] + nhg_int = "" + nhportlist = [] + dpg_ecmp_content = {} + ipnhs = child.find(str(QName(ns, "IPNextHops"))) + if ipnhs is not None: + for ipnh in ipnhs.findall(str(QName(ns, "IPNextHop"))): + if ipnh.find(str(QName(ns, "Type"))).text == 'FineGrainedECMPGroupMember': + ipnhfmbr = ipnh.find(str(QName(ns, "AttachTo"))).text + ipnhaddr = ipnh.find(str(QName(ns, "Address"))).text + nhportlist.append(ipnhfmbr) + if "." in ipnhaddr: + port_nhipv4_map[ipnhfmbr] = ipnhaddr + elif ":" in ipnhaddr: + port_nhipv6_map[ipnhfmbr] = ipnhaddr + + if port_nhipv4_map is not None and port_nhipv6_map is not None: + subnet_check_ip = list(port_nhipv4_map.values())[0] + for subnet_range in ip_intfs_map: + if ("." in subnet_range): + a = ipaddress.ip_address(UNICODE_TYPE(subnet_check_ip)) + n = list(ipaddress.ip_network(UNICODE_TYPE(subnet_range), False).hosts()) + if a in n: + nhg_int = ip_intfs_map[subnet_range] + dwnstrms = child.find(str(QName(ns, "DownstreamSummarySet"))) + for dwnstrm in dwnstrms.findall(str(QName(ns, "DownstreamSummary"))): + dwnstrmentry = str(ET.tostring(dwnstrm)) + if ("FineGrainedECMPGroupDestination" in dwnstrmentry): + subnet_ip = dwnstrm.find(str(QName(ns1, "Subnet"))).text + truncsubnet_ip = subnet_ip.split("/")[0] + if "." in (truncsubnet_ip): + nhgaddr[0] = subnet_ip + elif ":" in (truncsubnet_ip): + nhgaddr[1] = subnet_ip + ipv4_content = {"port_nhip_map": port_nhipv4_map, "nhgaddr": nhgaddr[0], "nhg_int": nhg_int} + ipv6_content = {"port_nhip_map": port_nhipv6_map, "nhgaddr": nhgaddr[1], "nhg_int": nhg_int} + dpg_ecmp_content['ipv4'] = ipv4_content + dpg_ecmp_content['ipv6'] = ipv6_content vlanintfs = child.find(str(QName(ns, "VlanInterfaces"))) vlan_intfs = [] vlans = {} @@ -503,6 +637,7 @@ def parse_dpg(dpg, hname): except: print("Warning: Ignoring Control Plane ACL %s without type" % aclname, file=sys.stderr) + mg_tunnels = child.find(str(QName(ns, "TunnelInterfaces"))) if mg_tunnels is not None: table_key_to_mg_key_map = {"encap_ecn_mode": "EcnEncapsulationMode", @@ -521,7 +656,7 @@ def parse_dpg(dpg, hname): if mg_key in mg_tunnel.attrib: tunnelintfs[tunnel_type][tunnel_name][table_key] = mg_tunnel.attrib[mg_key] - return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnelintfs + return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnelintfs, dpg_ecmp_content return None, None, None, None, None, None, None, None, None, None def parse_host_loopback(dpg, hname): @@ -927,6 +1062,8 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw bgp_monitors = [] bgp_asn = None intfs = None + dpg_ecmp_content = {} + png_ecmp_content = {} vlan_intfs = None pc_intfs = None tunnel_intfs = None @@ -988,13 +1125,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw for child in root: if asic_name is None: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs) = parse_dpg(child, hostname) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs, dpg_ecmp_content) = parse_dpg(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_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, mux_cable_ports, is_storage_device) = parse_png(child, hostname) + (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speed_png, console_ports, mux_cable_ports, is_storage_device, png_ecmp_content) = parse_png(child, hostname, dpg_ecmp_content) elif child.tag == str(QName(ns, "UngDec")): - (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname) + (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname, None) elif child.tag == str(QName(ns, "MetadataDeclaration")): (syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type) = parse_meta(child, hostname) elif child.tag == str(QName(ns, "LinkMetadataDeclaration")): @@ -1003,7 +1140,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw (port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku) else: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs) = parse_dpg(child, asic_name) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs, dpg_ecmp_content) = parse_dpg(child, asic_name) host_lo_intfs = parse_host_loopback(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices) @@ -1305,6 +1442,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw 'client_crt_cname': 'client.restapi.sonic' } } + + if len(png_ecmp_content): + results['FG_NHG_MEMBER'] = png_ecmp_content['FG_NHG_MEMBER'] + results['FG_NHG_PREFIX'] = png_ecmp_content['FG_NHG_PREFIX'] + results['FG_NHG'] = png_ecmp_content['FG_NHG'] + results['NEIGH'] = png_ecmp_content['NEIGH'] + # Do not configure the minigraph's mirror session, which is currently unused # mirror_sessions = {} # if erspan_dst: diff --git a/src/sonic-config-engine/tests/fg-ecmp-sample-minigraph.xml b/src/sonic-config-engine/tests/fg-ecmp-sample-minigraph.xml new file mode 100644 index 000000000000..f8fcf1e0fce7 --- /dev/null +++ b/src/sonic-config-engine/tests/fg-ecmp-sample-minigraph.xml @@ -0,0 +1,1591 @@ + + + + + + + false + str2-sn3800-azd + 10.0.0.56 + ARISTA01T1 + 10.0.0.57 + 1 + 10 + 3 + + + str2-sn3800-azd + FC00::71 + ARISTA01T1 + FC00::72 + 1 + 10 + 3 + + + false + str2-sn3800-azd + 10.0.0.58 + ARISTA02T1 + 10.0.0.59 + 1 + 10 + 3 + + + str2-sn3800-azd + FC00::75 + ARISTA02T1 + FC00::76 + 1 + 10 + 3 + + + false + str2-sn3800-azd + 10.0.0.60 + ARISTA03T1 + 10.0.0.61 + 1 + 10 + 3 + + + str2-sn3800-azd + FC00::79 + ARISTA03T1 + FC00::7A + 1 + 10 + 3 + + + false + str2-sn3800-azd + 10.0.0.62 + ARISTA04T1 + 10.0.0.63 + 1 + 10 + 3 + + + str2-sn3800-azd + FC00::7D + ARISTA04T1 + FC00::7E + 1 + 10 + 3 + + + + + 65100 + str2-sn3800-azd + + +
10.0.0.57
+ + + +
+ +
10.0.0.59
+ + + +
+ +
10.0.0.61
+ + + +
+ +
10.0.0.63
+ + + +
+ + BGPPeer +
10.1.0.32
+ + + + BGPSLBPassive + 10.255.0.0/25 +
+ + BGPPeer +
10.1.0.32
+ + + + BGPVac + 192.168.0.0/21 +
+
+ +
+ + 64600 + ARISTA01T1 + + + + 64600 + ARISTA02T1 + + + + 64600 + ARISTA03T1 + + + + 64600 + ARISTA04T1 + + +
+
+ + + + + + HostIP + Loopback0 + + 10.1.0.32/32 + + 10.1.0.32/32 + + + HostIP1 + Loopback0 + + FC00:1::32/128 + + FC00:1::32/128 + + + + + HostIP + eth0 + + 10.3.146.183/23 + + 10.3.146.183/23 + + + V6HostIP + eth0 + + FC00:2::32/64 + + FC00:2::32/64 + + + + + + + str2-sn3800-azd + + + PortChannel0001 + etp29 + + + + PortChannel0002 + etp30 + + + + PortChannel0003 + etp31 + + + + PortChannel0004 + etp32 + + + + + + Vlan1000 + etp2;etp3;etp4;etp5;etp6;etp7;etp8;etp9;etp10;etp11;etp12;etp13;etp14;etp15;etp16;etp17;etp18;etp19;etp20;etp21;etp22;etp23;etp24;etp25 + False + 0.0.0.0/0 + + 192.0.0.1;192.0.0.2;192.0.0.3;192.0.0.4;192.0.0.5;192.0.0.6;192.0.0.7;192.0.0.8;192.0.0.9;192.0.0.10;192.0.0.11;192.0.0.12;192.0.0.13;192.0.0.14;192.0.0.15;192.0.0.16;192.0.0.17;192.0.0.18;192.0.0.19;192.0.0.20;192.0.0.21;192.0.0.22;192.0.0.23;192.0.0.24;192.0.0.25;192.0.0.26;192.0.0.27;192.0.0.28;192.0.0.29;192.0.0.30;192.0.0.31;192.0.0.32;192.0.0.33;192.0.0.34;192.0.0.35;192.0.0.36;192.0.0.37;192.0.0.38;192.0.0.39;192.0.0.40;192.0.0.41;192.0.0.42;192.0.0.43;192.0.0.44;192.0.0.45;192.0.0.46;192.0.0.47;192.0.0.48 + 1000 + 1000 + 192.168.0.0/21 + + + + + + PortChannel0001 + 10.0.0.56/31 + + + + PortChannel0001 + FC00::71/126 + + + + PortChannel0002 + 10.0.0.58/31 + + + + PortChannel0002 + FC00::75/126 + + + + PortChannel0003 + 10.0.0.60/31 + + + + PortChannel0003 + FC00::79/126 + + + + PortChannel0004 + 10.0.0.62/31 + + + + PortChannel0004 + FC00::7D/126 + + + + Vlan1000 + 192.168.0.1/21 + + + + Vlan31 + 200.200.200.0/27 + + + + + + SNMP_ACL + SNMP + SNMP + + + ERSPAN + fg-ecmp-sample-minigraphfg-ecmp-sample-minigraph + Everflow + Everflow + + + ERSPANV6 + EverflowV6 + EverflowV6 + + + VTY_LINE + ssh-only + SSH + + + PortChannel0001;PortChannel0002;PortChannel0003;PortChannel0004 + DataAcl + DataPlane + + + + + + false + 100.50.25.12/32 + FineGrainedECMPGroupDestination + + + true + 2603:10b0:10d:e07::/64 + DeviceAggregate + + + false + fc:5::/128 + FineGrainedECMPGroupDestination + + + + + IPNextHop + + etp10 +
200.200.200.1
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp11 +
200.200.200.2
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp12 +
200.200.200.3
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp13 +
200.200.200.4
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp14 +
200.200.200.5
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp15 +
200.200.200.6
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp16 +
200.200.200.7
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp17 +
200.200.200.8
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp18 +
200.200.200.9
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp19 +
200.200.200.10
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp10 +
200:200:200:200::1
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp11 +
200:200:200:200::2
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp12 +
200:200:200:200::3
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp13 +
200:200:200:200::4
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp14 +
200:200:200:200::5
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp15 +
200:200:200:200::6
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp16 +
200:200:200:200::7
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp17 +
200:200:200:200::8
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp18 +
200:200:200:200::9
+ FineGrainedECMPGroupMember +
+ + IPNextHop + + etp19 +
200:200:200:200::10
+ FineGrainedECMPGroupMember +
+
+
+
+ + + + DeviceInterfaceLink + ARISTA01T1 + Ethernet1 + str2-sn3800-azd + etp29 + + + DeviceInterfaceLink + ARISTA02T1 + Ethernet1 + str2-sn3800-azd + etp30 + + + DeviceInterfaceLink + ARISTA03T1 + Ethernet1 + str2-sn3800-azd + etp31 + + + DeviceInterfaceLink + ARISTA04T1 + Ethernet1 + str2-sn3800-azd + etp32 + + + DeviceInterfaceLink + str2-sn3800-azd + etp2 + Servers0 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp5 + Servers3 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp6 + Servers4 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp9 + Servers7 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp10 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp11 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp12 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp13 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp14 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp15 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp16 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp17 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp18 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp19 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp20 + Servers10 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp21 + Servers11 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp22 + Servers20 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp23 + Servers21 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp24 + Servers22 + eth0 + + + DeviceInterfaceLink + str2-sn3800-azd + etp25 + Servers23 + eth0 + + + + + str2-sn3800-azd + ACS-MSN3800 + + 10.3.146.183 + + + + ARISTA04T1 + + 172.16.134.63 + + Arista-VM + + + ARISTA03T1 + + 172.16.134.62 + + Arista-VM + + + ARISTA02T1 + + 172.16.134.61 + + Arista-VM + + + ARISTA01T1 + + 172.16.134.60 + + Arista-VM + + + + + + true + + + DeviceInterface + + true + true + 1 + etp1 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp2 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp3 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp4 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp5 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp6 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp7 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp8 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp9 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp10 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp11 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp12 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp13 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp14 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp15 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp16 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp17 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp18 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp19 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp20 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp21 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp22 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp23 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp24 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp25 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp26 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp27 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp28 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp29 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp30 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp31 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp32 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp33 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp34 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp35 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp36 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp37 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp38 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp39 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp40 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp41 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp42 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp43 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp44 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp45 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp46 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp47 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp48 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp49 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp50 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp51 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp52 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp53 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp54 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp55 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp56 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp57 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp58 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp59 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp60 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp61 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp62 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp63 + + false + 0 + 0 + 100000 + + + DeviceInterface + + true + true + 1 + etp64 + + false + 0 + 0 + 100000 + + + true + 0 + ACS-MSN3800 + + + + + + + str2-sn3800-azd + + + DeploymentId + + 26 + + + QosProfile + + Profile0 + + + DhcpResources + + 192.0.0.1;192.0.0.2;192.0.0.3;192.0.0.4;192.0.0.5;192.0.0.6;192.0.0.7;192.0.0.8;192.0.0.9;192.0.0.10;192.0.0.11;192.0.0.12;192.0.0.13;192.0.0.14;192.0.0.15;192.0.0.16;192.0.0.17;192.0.0.18;192.0.0.19;192.0.0.20;192.0.0.21;192.0.0.22;192.0.0.23;192.0.0.24;192.0.0.25;192.0.0.26;192.0.0.27;192.0.0.28;192.0.0.29;192.0.0.30;192.0.0.31;192.0.0.32;192.0.0.33;192.0.0.34;192.0.0.35;192.0.0.36;192.0.0.37;192.0.0.38;192.0.0.39;192.0.0.40;192.0.0.41;192.0.0.42;192.0.0.43;192.0.0.44;192.0.0.45;192.0.0.46;192.0.0.47;192.0.0.48 + + + NtpResources + + 10.20.8.129;10.20.8.130 + + + SnmpResources + + 10.3.145.98 + + + SyslogResources + + 10.3.145.8;100.127.20.21 + + + TacacsGroup + + Starlab + + + TacacsServer + + 100.127.20.21 + + + ForcedMgmtRoutes + + 10.3.145.98/31;10.3.145.8;100.127.20.16/28;10.3.149.170/31;40.122.216.24;13.91.48.226;10.3.145.14 + + + ErspanDestinationIpv4 + + 10.20.6.16 + + + + + + + str2-sn3800-azd + ACS-MSN3800 +
\ No newline at end of file diff --git a/src/sonic-config-engine/tests/mellanox-sample-port-config.ini b/src/sonic-config-engine/tests/mellanox-sample-port-config.ini new file mode 100644 index 000000000000..2af5aa67ff3a --- /dev/null +++ b/src/sonic-config-engine/tests/mellanox-sample-port-config.ini @@ -0,0 +1,65 @@ +# name lanes alias index +Ethernet0 0,1,2,3 etp1 1 +Ethernet4 4,5,6,7 etp2 2 +Ethernet8 8,9,10,11 etp3 3 +Ethernet12 12,13,14,15 etp4 4 +Ethernet16 16,17,18,19 etp5 5 +Ethernet20 20,21,22,23 etp6 6 +Ethernet24 24,25,26,27 etp7 7 +Ethernet28 28,29,30,31 etp8 8 +Ethernet32 32,33,34,35 etp9 9 +Ethernet36 36,37,38,39 etp10 10 +Ethernet40 40,41,42,43 etp11 11 +Ethernet44 44,45,46,47 etp12 12 +Ethernet48 48,49,50,51 etp13 13 +Ethernet52 52,53,54,55 etp14 14 +Ethernet56 56,57,58,59 etp15 15 +Ethernet60 60,61,62,63 etp16 16 +Ethernet64 64,65,66,67 etp17 17 +Ethernet68 68,69,70,71 etp18 18 +Ethernet72 72,73,74,75 etp19 19 +Ethernet76 76,77,78,79 etp20 20 +Ethernet80 80,81,82,83 etp21 21 +Ethernet84 84,85,86,87 etp22 22 +Ethernet88 88,89,90,91 etp23 23 +Ethernet92 92,93,94,95 etp24 24 +Ethernet96 96,97,98,99 etp25 25 +Ethernet100 100,101,102,103 etp26 26 +Ethernet104 104,105,106,107 etp27 27 +Ethernet108 108,109,110,111 etp28 28 +Ethernet112 112,113,114,115 etp29 29 +Ethernet116 116,117,118,119 etp30 30 +Ethernet120 120,121,122,123 etp31 31 +Ethernet124 124,125,126,127 etp32 32 +Ethernet128 128,129,130,131 etp33 33 +Ethernet132 132,133,134,135 etp34 34 +Ethernet136 136,137,138,139 etp35 35 +Ethernet140 140,141,142,143 etp36 36 +Ethernet144 144,145,146,147 etp37 37 +Ethernet148 148,149,150,151 etp38 38 +Ethernet152 152,153,154,155 etp39 39 +Ethernet156 156,157,158,159 etp40 40 +Ethernet160 160,161,162,163 etp41 41 +Ethernet164 164,165,166,167 etp42 42 +Ethernet168 168,169,170,171 etp43 43 +Ethernet172 172,173,174,175 etp44 44 +Ethernet176 176,177,178,179 etp45 45 +Ethernet180 180,181,182,183 etp46 46 +Ethernet184 184,185,186,187 etp47 47 +Ethernet188 188,189,190,191 etp48 48 +Ethernet192 192,193,194,195 etp49 49 +Ethernet196 196,197,198,199 etp50 50 +Ethernet200 200,201,202,203 etp51 51 +Ethernet204 204,205,206,207 etp52 52 +Ethernet208 208,209,210,211 etp53 53 +Ethernet212 212,213,214,215 etp54 54 +Ethernet216 216,217,218,219 etp55 55 +Ethernet220 220,221,222,223 etp56 56 +Ethernet224 224,225,226,227 etp57 57 +Ethernet228 228,229,230,231 etp58 58 +Ethernet232 232,233,234,235 etp59 59 +Ethernet236 236,237,238,239 etp60 60 +Ethernet240 240,241,242,243 etp61 61 +Ethernet244 244,245,246,247 etp62 62 +Ethernet248 248,249,250,251 etp63 63 +Ethernet252 252,253,254,255 etp64 64 \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index 30d205797642..3a85346de2fd 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -23,8 +23,10 @@ def setUp(self): self.sample_graph_bgp_speaker = os.path.join(self.test_dir, 't0-sample-bgp-speaker.xml') self.sample_device_desc = os.path.join(self.test_dir, 'device.xml') self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini') + self.mlnx_port_config = os.path.join(self.test_dir, 'mellanox-sample-port-config.ini') self.output_file = os.path.join(self.test_dir, 'output') self.output2_file = os.path.join(self.test_dir, 'output2') + self.ecmp_graph = os.path.join(self.test_dir, 'fg-ecmp-sample-minigraph.xml') def tearDown(self): try: @@ -222,6 +224,34 @@ def test_minigraph_vlan_interfaces(self): output = self.run_script(argument) self.assertEqual(output.strip(), "[('Vlan1000', '192.168.0.1/27'), 'Vlan1000']") + def test_minigraph_ecmp_fg_nhg(self): + argument = '-m "' + self.ecmp_graph + '" -p "' + self.mlnx_port_config + '" -v \"FG_NHG.values()|list\"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "[{'bucket_size': 120}, {'bucket_size': 120}]") + + def test_minigraph_ecmp_members(self): + argument = '-m "' + self.ecmp_graph + '" -p "' + self.mlnx_port_config + '" -v "FG_NHG_MEMBER.keys()|list|sort"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "['200.200.200.1', '200.200.200.10', '200.200.200.2', '200.200.200.3', '200.200.200.4', '200.200.200.5'," + " '200.200.200.6', '200.200.200.7', '200.200.200.8', '200.200.200.9', '200:200:200:200::1', '200:200:200:200::10'," + " '200:200:200:200::2', '200:200:200:200::3', '200:200:200:200::4', '200:200:200:200::5', '200:200:200:200::6'," + " '200:200:200:200::7', '200:200:200:200::8', '200:200:200:200::9']") + + def test_minigraph_ecmp_neighbors(self): + argument = '-m "' + self.ecmp_graph + '" -p "' + self.mlnx_port_config + '" -v "NEIGH.keys()|list|sort"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "['Vlan31|200.200.200.1', 'Vlan31|200.200.200.10', 'Vlan31|200.200.200.2', 'Vlan31|200.200.200.3'," + " 'Vlan31|200.200.200.4', 'Vlan31|200.200.200.5', 'Vlan31|200.200.200.6', 'Vlan31|200.200.200.7'," + " 'Vlan31|200.200.200.8', 'Vlan31|200.200.200.9', 'Vlan31|200:200:200:200::1', 'Vlan31|200:200:200:200::10'," + " 'Vlan31|200:200:200:200::2', 'Vlan31|200:200:200:200::3', 'Vlan31|200:200:200:200::4', 'Vlan31|200:200:200:200::5', " + "'Vlan31|200:200:200:200::6', 'Vlan31|200:200:200:200::7', 'Vlan31|200:200:200:200::8', 'Vlan31|200:200:200:200::9']") + + def test_minigraph_ecmp_prefixes(self): + argument = '-m "' + self.ecmp_graph + '" -p "' + self.mlnx_port_config + '" -v "FG_NHG_PREFIX.keys()|list|sort"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "['100.50.25.12/32', 'fc:5::/128']") + + def test_minigraph_portchannels(self): argument = '-m "' + self.sample_graph_simple + '" -p "' + self.port_config + '" -v PORTCHANNEL' output = self.run_script(argument)