@@ -185,6 +185,7 @@ def formulate_fine_grained_ecmp(version, dpg_ecmp_content, port_device_map, port
185185 fine_grained_content = {"FG_NHG_MEMBER" : FG_NHG_MEMBER , "FG_NHG" : FG_NHG , "NEIGH" : NEIGH }
186186 return fine_grained_content
187187
188+
188189def parse_png (png , hname , dpg_ecmp_content = None ):
189190 neighbors = {}
190191 devices = {}
@@ -400,9 +401,9 @@ def parse_asic_png(png, asic_name, hostname):
400401 device_data ['lo_addr_v6' ]= lo_prefix_v6
401402 devices [name ] = device_data
402403
403-
404404 return (neighbors , devices , port_speeds )
405405
406+
406407def parse_loopback_intf (child ):
407408 lointfs = child .find (str (QName (ns , "LoopbackIPInterfaces" )))
408409 lo_intfs = {}
@@ -412,6 +413,7 @@ def parse_loopback_intf(child):
412413 lo_intfs [(intfname , ipprefix )] = {}
413414 return lo_intfs
414415
416+
415417def parse_dpg (dpg , hname ):
416418 aclintfs = None
417419 mgmtintfs = None
@@ -455,7 +457,7 @@ def parse_dpg(dpg, hname):
455457 ipprefix = ipintf .find (str (QName (ns , "Prefix" ))).text
456458 intfs [(intfname , ipprefix )] = {}
457459 ip_intfs_map [ipprefix ] = intfalias
458- lo_intfs = parse_loopback_intf (child )
460+ lo_intfs = parse_loopback_intf (child )
459461
460462 subintfs = child .find (str (QName (ns , "SubInterfaces" )))
461463 if subintfs is not None :
@@ -757,7 +759,6 @@ def parse_dpg(dpg, hname):
757759 return None , None , None , None , None , None , None , None , None , None , None , None , None , None , None , None
758760
759761
760-
761762def parse_host_loopback (dpg , hname ):
762763 for child in dpg :
763764 hostname = child .find (str (QName (ns , "Hostname" )))
@@ -766,6 +767,7 @@ def parse_host_loopback(dpg, hname):
766767 lo_intfs = parse_loopback_intf (child )
767768 return lo_intfs
768769
770+
769771def parse_cpg (cpg , hname , local_devices = []):
770772 bgp_sessions = {}
771773 bgp_internal_sessions = {}
@@ -891,6 +893,7 @@ def parse_meta(meta, hname):
891893 max_cores = None
892894 kube_data = {}
893895 macsec_profile = {}
896+ redundancy_type = None
894897 device_metas = meta .find (str (QName (ns , "Devices" )))
895898 for device in device_metas .findall (str (QName (ns1 , "DeviceMetadata" ))):
896899 if device .find (str (QName (ns1 , "Name" ))).text .lower () == hname .lower ():
@@ -933,7 +936,9 @@ def parse_meta(meta, hname):
933936 kube_data ["ip" ] = value
934937 elif name == 'MacSecProfile' :
935938 macsec_profile = parse_macsec_profile (value )
936- return syslog_servers , dhcp_servers , dhcpv6_servers , ntp_servers , tacacs_servers , mgmt_routes , erspan_dst , deployment_id , region , cloudtype , resource_type , downstream_subrole , switch_id , switch_type , max_cores , kube_data , macsec_profile
939+ elif name == "RedundancyType" :
940+ redundancy_type = value
941+ return syslog_servers , dhcp_servers , dhcpv6_servers , ntp_servers , tacacs_servers , mgmt_routes , erspan_dst , deployment_id , region , cloudtype , resource_type , downstream_subrole , switch_id , switch_type , max_cores , kube_data , macsec_profile , redundancy_type
937942
938943
939944def parse_system_defaults (meta ):
@@ -1313,6 +1318,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
13131318 static_routes = {}
13141319 system_defaults = {}
13151320 macsec_profile = {}
1321+ redundancy_type = None
13161322
13171323 hwsku_qn = QName (ns , "HwSku" )
13181324 hostname_qn = QName (ns , "Hostname" )
@@ -1343,7 +1349,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
13431349 elif child .tag == str (QName (ns , "UngDec" )):
13441350 (u_neighbors , u_devices , _ , _ , _ , _ , _ , _ ) = parse_png (child , hostname , None )
13451351 elif child .tag == str (QName (ns , "MetadataDeclaration" )):
1346- (syslog_servers , dhcp_servers , dhcpv6_servers , ntp_servers , tacacs_servers , mgmt_routes , erspan_dst , deployment_id , region , cloudtype , resource_type , downstream_subrole , switch_id , switch_type , max_cores , kube_data , macsec_profile ) = parse_meta (child , hostname )
1352+ (syslog_servers , dhcp_servers , dhcpv6_servers , ntp_servers , tacacs_servers , mgmt_routes , erspan_dst , deployment_id , region , cloudtype , resource_type , downstream_subrole , switch_id , switch_type , max_cores , kube_data , macsec_profile , redundancy_type ) = parse_meta (child , hostname )
13471353 elif child .tag == str (QName (ns , "LinkMetadataDeclaration" )):
13481354 linkmetas = parse_linkmeta (child , hostname )
13491355 elif child .tag == str (QName (ns , "DeviceInfos" )):
@@ -1567,11 +1573,6 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
15671573 if macsec_enabled and 'PrimaryKey' in macsec_profile :
15681574 port ['macsec' ] = macsec_profile ['PrimaryKey' ]
15691575
1570- # If connected to a smart cable, get the connection position
1571- for port_name , port in ports .items ():
1572- if port_name in mux_cable_ports :
1573- port ['mux_cable' ] = "true"
1574-
15751576 # set port description if parsed from deviceinfo
15761577 for port_name in port_descriptions :
15771578 # ignore port not in port_config.ini
@@ -1713,7 +1714,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
17131714 # Add src_ip and qos remapping config into TUNNEL table if tunnel_qos_remap is enabled
17141715 results ['TUNNEL' ] = get_tunnel_entries (tunnel_intfs , tunnel_intfs_qos_remap_config , lo_intfs , system_defaults .get ('tunnel_qos_remap' , {}), mux_tunnel_name , peer_switch_ip )
17151716
1716- results ['MUX_CABLE' ] = get_mux_cable_entries (mux_cable_ports , neighbors , devices )
1717+ active_active_ports = get_ports_in_active_active (root , devices , neighbors )
1718+ results ['MUX_CABLE' ] = get_mux_cable_entries (ports , mux_cable_ports , active_active_ports , neighbors , devices , redundancy_type )
1719+
1720+ # If connected to a smart cable, get the connection position
1721+ for port_name , port in results ['PORT' ].items ():
1722+ if port_name in results ['MUX_CABLE' ]:
1723+ port ['mux_cable' ] = "true"
17171724
17181725 if static_routes :
17191726 results ['STATIC_ROUTE' ] = static_routes
@@ -1826,46 +1833,76 @@ def get_tunnel_entries(tunnel_intfs, tunnel_intfs_qos_remap_config, lo_intfs, tu
18261833
18271834 return tunnels
18281835
1829- def get_mux_cable_entries (mux_cable_ports , neighbors , devices ):
1836+
1837+ def get_ports_in_active_active (root , devices , neighbors ):
1838+ """Parse out ports in active-active cable type."""
1839+ servers = {hostname .lower (): device_data for hostname , device_data in devices .items () if device_data ["type" ] == "Server" }
1840+ ports_in_active_active = {}
1841+ dpg_section = root .find (str (QName (ns , "DpgDec" )))
1842+ neighbor_to_port_mapping = {neighbor ["name" ].lower (): port for port , neighbor in neighbors .items ()}
1843+ if dpg_section is not None :
1844+ for child in dpg_section :
1845+ hostname = child .find (str (QName (ns , "Hostname" )))
1846+ if hostname is None :
1847+ continue
1848+ hostname = hostname .text .lower ()
1849+ if hostname not in servers :
1850+ continue
1851+ lo_intfs = parse_loopback_intf (child )
1852+ soc_intfs = {}
1853+ for intfname , ipprefix in lo_intfs .keys ():
1854+ intfname_lower = intfname .lower ()
1855+ if hostname + "soc" == intfname_lower :
1856+ ipprefix = str (ipaddress .ip_network (UNICODE_TYPE (ipprefix .split ("/" )[0 ])))
1857+ if "." in ipprefix :
1858+ soc_intfs ["soc_ipv4" ] = ipprefix
1859+ elif ":" in ipprefix :
1860+ soc_intfs ["soc_ipv6" ] = ipprefix
1861+ if hostname in neighbor_to_port_mapping and soc_intfs :
1862+ ports_in_active_active [neighbor_to_port_mapping [hostname ]] = soc_intfs
1863+ return ports_in_active_active
1864+
1865+
1866+ def get_mux_cable_entries (ports , mux_cable_ports , active_active_ports , neighbors , devices , redundancy_type ):
18301867 mux_cable_table = {}
1868+ if redundancy_type :
1869+ redundancy_type = redundancy_type .lower ()
1870+
1871+ for port in ports :
1872+ is_active_active = redundancy_type in ("libra" , "mixed" ) and port in active_active_ports
1873+ is_active_standby = port in mux_cable_ports
1874+ if is_active_active and is_active_standby :
1875+ print ("Warning: skip %s as it is defined as active-standby and actie-active" % port , file = sys .stderr )
1876+ continue
1877+ if not (is_active_active or is_active_standby ):
1878+ continue
18311879
1832- for intf , cable_name in mux_cable_ports .items ():
1833- if intf in neighbors :
1834- entry = {}
1835- neighbor = neighbors [intf ]['name' ]
1836- entry ['state' ] = 'auto'
1837-
1838- if devices [neighbor ]['lo_addr' ] is not None :
1839- # Always force a /32 prefix for server IPv4 loopbacks
1840- server_ipv4_lo_addr = devices [neighbor ]['lo_addr' ].split ("/" )[0 ]
1841- server_ipv4_lo_prefix = ipaddress .ip_network (UNICODE_TYPE (server_ipv4_lo_addr ))
1842- entry ['server_ipv4' ] = str (server_ipv4_lo_prefix )
1843-
1844- if 'lo_addr_v6' in devices [neighbor ] and devices [neighbor ]['lo_addr_v6' ] is not None :
1845- server_ipv6_lo_addr = devices [neighbor ]['lo_addr_v6' ].split ('/' )[0 ]
1846- server_ipv6_lo_prefix = ipaddress .ip_network (UNICODE_TYPE (server_ipv6_lo_addr ))
1847- entry ['server_ipv6' ] = str (server_ipv6_lo_prefix )
1848- mux_cable_table [intf ] = entry
1849- else :
1850- print ("Warning: no server IPv4 loopback found for {}, skipping mux cable table entry" .format (neighbor ), file = sys .stderr )
1880+ entry = {}
1881+ neighbor = neighbors [port ]['name' ]
1882+ entry ['state' ] = 'auto'
18511883
1852- if cable_name in devices :
1853- cable_type = devices [cable_name ].get ('subtype' )
1854- if cable_type is None :
1855- continue
1856- if cable_type in dualtor_cable_types :
1857- mux_cable_table [intf ]['cable_type' ] = cable_type
1858- if cable_type == 'active-active' :
1859- soc_ipv4 = devices [cable_name ]['lo_addr' ].split ('/' )[0 ]
1860- soc_ipv4_prefix = ipaddress .ip_network (UNICODE_TYPE (soc_ipv4 ))
1861- mux_cable_table [intf ]['soc_ipv4' ] = str (soc_ipv4_prefix )
1862- else :
1863- print ("Warning: skip parsing device %s for mux cable entry, cable type %s not supported" % (cable_name , cable_type ), file = sys .stderr )
1884+ if devices [neighbor ]['lo_addr' ] is not None :
1885+ # Always force a /32 prefix for server IPv4 loopbacks
1886+ server_ipv4_lo_addr = devices [neighbor ]['lo_addr' ].split ("/" )[0 ]
1887+ server_ipv4_lo_prefix = ipaddress .ip_network (UNICODE_TYPE (server_ipv4_lo_addr ))
1888+ entry ['server_ipv4' ] = str (server_ipv4_lo_prefix )
1889+
1890+ if 'lo_addr_v6' in devices [neighbor ] and devices [neighbor ]['lo_addr_v6' ] is not None :
1891+ server_ipv6_lo_addr = devices [neighbor ]['lo_addr_v6' ].split ('/' )[0 ]
1892+ server_ipv6_lo_prefix = ipaddress .ip_network (UNICODE_TYPE (server_ipv6_lo_addr ))
1893+ entry ['server_ipv6' ] = str (server_ipv6_lo_prefix )
1894+
1895+ if is_active_active :
1896+ entry ['cable_type' ] = 'active-active'
1897+ entry .update (active_active_ports [port ])
1898+
1899+ mux_cable_table [port ] = entry
18641900 else :
1865- print ("Warning: skip parsing device %s for mux cable entry, device definition not found" % cable_name , file = sys .stderr )
1901+ print ("Warning: no server IPv4 loopback found for {}, skipping mux cable table entry" . format ( neighbor ) , file = sys .stderr )
18661902
18671903 return mux_cable_table
18681904
1905+
18691906def parse_device_desc_xml (filename ):
18701907 root = ET .parse (filename ).getroot ()
18711908 (lo_prefix , lo_prefix_v6 , mgmt_prefix , mgmt_prefix_v6 , hostname , hwsku , d_type , _ , _ , _ ) = parse_device (root )
0 commit comments