10
10
from lxml import etree as ET
11
11
from lxml .etree import QName
12
12
13
+
13
14
from portconfig import get_port_config
14
15
from sonic_py_common .multi_asic import get_asic_id_from_name
15
16
from sonic_py_common .interface import backplane_prefix
@@ -105,7 +106,73 @@ def parse_device(device):
105
106
106
107
return (lo_prefix , lo_prefix_v6 , mgmt_prefix , name , hwsku , d_type , deployment_id )
107
108
108
- def parse_png (png , hname ):
109
+ def calculate_lcm_for_ecmp (nhdevices_bank_map , nhip_bank_map ):
110
+ banks_enumerated = {}
111
+ lcm_array = []
112
+ for value in nhdevices_bank_map .values ():
113
+ for key in nhip_bank_map .keys ():
114
+ if nhip_bank_map [key ] == value :
115
+ if value not in banks_enumerated :
116
+ banks_enumerated [value ] = 1
117
+ else :
118
+ banks_enumerated [value ] = banks_enumerated [value ] + 1
119
+ for bank_enumeration in banks_enumerated .values ():
120
+ lcm_list = range (1 , bank_enumeration + 1 )
121
+ lcm_comp = lcm_list [0 ]
122
+ for i in lcm_list [1 :]:
123
+ lcm_comp = lcm_comp * i / calculate_gcd (lcm_comp , i )
124
+ lcm_array .append (lcm_comp )
125
+
126
+ LCM = sum (lcm_array )
127
+ return int (LCM )
128
+
129
+ def calculate_gcd (x , y ):
130
+ while y != 0 :
131
+ (x , y ) = (y , x % y )
132
+ return int (x )
133
+
134
+ def formulate_fine_grained_ecmp (version , dpg_ecmp_content , port_device_map , port_alias_map ):
135
+ family = ""
136
+ tag = ""
137
+ neigh_key = []
138
+ if version == "ipv4" :
139
+ family = "IPV4"
140
+ tag = "fgnhg_v4"
141
+ elif version == "ipv6" :
142
+ family = "IPV6"
143
+ tag = "fgnhg_v6"
144
+
145
+ port_nhip_map = dpg_ecmp_content ['port_nhip_map' ]
146
+ nhgaddr = dpg_ecmp_content ['nhgaddr' ]
147
+ nhg_int = dpg_ecmp_content ['nhg_int' ]
148
+
149
+ nhip_device_map = {port_nhip_map [x ]: port_device_map [x ] for x in port_device_map
150
+ if x in port_nhip_map }
151
+ nhip_devices = sorted (list (set (nhip_device_map .values ())))
152
+ nhdevices_ip_bank_map = {device : bank for bank , device in enumerate (nhip_devices )}
153
+ nhip_bank_map = {ip : nhdevices_ip_bank_map [device ] for ip , device in nhip_device_map .items ()}
154
+ LCM = calculate_lcm_for_ecmp (nhdevices_ip_bank_map , nhip_bank_map )
155
+
156
+ FG_NHG_MEMBER = {ip : {"FG_NHG" : tag , "bank" : bank } for ip , bank in nhip_bank_map .items ()}
157
+ nhip_port_map = dict (zip (port_nhip_map .values (), port_nhip_map .keys ()))
158
+
159
+
160
+
161
+ for nhip , memberinfo in FG_NHG_MEMBER .items ():
162
+ if nhip in nhip_port_map :
163
+ memberinfo ["link" ] = port_alias_map [nhip_port_map [nhip ]]
164
+ FG_NHG_MEMBER [nhip ] = memberinfo
165
+
166
+ FG_NHG_PREFIX = {nhgaddr : {"FG_NHG" : tag }}
167
+ FG_NHG = {tag : {"bucket_size" : LCM }}
168
+ for ip in nhip_bank_map :
169
+ neigh_key .append (str (nhg_int + "|" + ip ))
170
+ NEIGH = {neigh_key : {"family" : family } for neigh_key in neigh_key }
171
+
172
+ fine_grained_content = {"FG_NHG_MEMBER" : FG_NHG_MEMBER , "FG_NHG" : FG_NHG , "FG_NHG_PREFIX" : FG_NHG_PREFIX , "NEIGH" : NEIGH }
173
+ return fine_grained_content
174
+
175
+ def parse_png (png , hname , dpg_ecmp_content = None ):
109
176
neighbors = {}
110
177
devices = {}
111
178
console_dev = ''
@@ -116,6 +183,13 @@ def parse_png(png, hname):
116
183
console_ports = {}
117
184
mux_cable_ports = {}
118
185
is_storage_device = False
186
+ port_device_map = {}
187
+ png_ecmp_content = {}
188
+ FG_NHG_MEMBER = {}
189
+ FG_NHG_PREFIX = {}
190
+ FG_NHG = {}
191
+ NEIGH = {}
192
+
119
193
for child in png :
120
194
if child .tag == str (QName (ns , "DeviceInterfaceLinks" )):
121
195
for link in child .findall (str (QName (ns , "DeviceLinkBase" ))):
@@ -141,6 +215,11 @@ def parse_png(png, hname):
141
215
}
142
216
continue
143
217
218
+ if linktype == "DeviceInterfaceLink" :
219
+ endport = link .find (str (QName (ns , "EndPort" ))).text
220
+ startdevice = link .find (str (QName (ns , "StartDevice" ))).text
221
+ port_device_map [endport ] = startdevice
222
+
144
223
if linktype != "DeviceInterfaceLink" and linktype != "UnderlayInterfaceLink" :
145
224
continue
146
225
@@ -196,6 +275,7 @@ def parse_png(png, hname):
196
275
elif node .tag == str (QName (ns , "EndDevice" )):
197
276
mgmt_dev = node .text
198
277
278
+
199
279
if child .tag == str (QName (ns , "DeviceInterfaceLinks" )):
200
280
for link in child .findall (str (QName (ns , 'DeviceLinkBase' ))):
201
281
if link .find (str (QName (ns , "ElementType" ))).text == "LogicalLink" :
@@ -205,7 +285,19 @@ def parse_png(png, hname):
205
285
206
286
mux_cable_ports [intf_name ] = "true"
207
287
208
- return (neighbors , devices , console_dev , console_port , mgmt_dev , mgmt_port , port_speeds , console_ports , mux_cable_ports , is_storage_device )
288
+ if (len (dpg_ecmp_content )):
289
+ for version , content in dpg_ecmp_content .items (): # version is ipv4 or ipv6
290
+ fine_grained_content = formulate_fine_grained_ecmp (version , content , port_device_map , port_alias_map ) # port_alias_map
291
+ FG_NHG_MEMBER .update (fine_grained_content ['FG_NHG_MEMBER' ])
292
+ FG_NHG_PREFIX .update (fine_grained_content ['FG_NHG_PREFIX' ])
293
+ FG_NHG .update (fine_grained_content ['FG_NHG' ])
294
+ NEIGH .update (fine_grained_content ['NEIGH' ])
295
+
296
+ png_ecmp_content = {"FG_NHG_PREFIX" : FG_NHG_PREFIX , "FG_NHG_MEMBER" : FG_NHG_MEMBER , "FG_NHG" : FG_NHG ,
297
+ "NEIGH" : NEIGH }
298
+
299
+ return (neighbors , devices , console_dev , console_port , mgmt_dev , mgmt_port , port_speeds , console_ports , mux_cable_ports , is_storage_device , png_ecmp_content )
300
+
209
301
210
302
def parse_asic_external_link (link , asic_name , hostname ):
211
303
neighbors = {}
@@ -295,6 +387,8 @@ def parse_asic_png(png, asic_name, hostname):
295
387
if lo_prefix_v6 :
296
388
device_data ['lo_addr_v6' ]= lo_prefix_v6
297
389
devices [name ] = device_data
390
+
391
+
298
392
return (neighbors , devices , port_speeds )
299
393
300
394
def parse_loopback_intf (child ):
@@ -339,12 +433,13 @@ def parse_dpg(dpg, hname):
339
433
340
434
ipintfs = child .find (str (QName (ns , "IPInterfaces" )))
341
435
intfs = {}
436
+ ip_intfs_map = {}
342
437
for ipintf in ipintfs .findall (str (QName (ns , "IPInterface" ))):
343
438
intfalias = ipintf .find (str (QName (ns , "AttachTo" ))).text
344
439
intfname = port_alias_map .get (intfalias , intfalias )
345
440
ipprefix = ipintf .find (str (QName (ns , "Prefix" ))).text
346
441
intfs [(intfname , ipprefix )] = {}
347
-
442
+ ip_intfs_map [ ipprefix ] = intfalias
348
443
lo_intfs = parse_loopback_intf (child )
349
444
350
445
mvrfConfigs = child .find (str (QName (ns , "MgmtVrfConfigs" )))
@@ -381,7 +476,46 @@ def parse_dpg(dpg, hname):
381
476
pcs [pcintfname ] = {'members' : pcmbr_list , 'fallback' : pcintf .find (str (QName (ns , "Fallback" ))).text , 'min_links' : str (int (math .ceil (len () * 0.75 )))}
382
477
else :
383
478
pcs [pcintfname ] = {'members' : pcmbr_list , 'min_links' : str (int (math .ceil (len (pcmbr_list ) * 0.75 )))}
384
-
479
+ port_nhipv4_map = {}
480
+ port_nhipv6_map = {}
481
+ nhgaddr = ["" , "" ]
482
+ nhg_int = ""
483
+ nhportlist = []
484
+ dpg_ecmp_content = {}
485
+ ipnhs = child .find (str (QName (ns , "IPNextHops" )))
486
+ if ipnhs is not None :
487
+ for ipnh in ipnhs .findall (str (QName (ns , "IPNextHop" ))):
488
+ if ipnh .find (str (QName (ns , "Type" ))).text == 'FineGrainedECMPGroupMember' :
489
+ ipnhfmbr = ipnh .find (str (QName (ns , "AttachTo" ))).text
490
+ ipnhaddr = ipnh .find (str (QName (ns , "Address" ))).text
491
+ nhportlist .append (ipnhfmbr )
492
+ if "." in ipnhaddr :
493
+ port_nhipv4_map [ipnhfmbr ] = ipnhaddr
494
+ elif ":" in ipnhaddr :
495
+ port_nhipv6_map [ipnhfmbr ] = ipnhaddr
496
+
497
+ if port_nhipv4_map is not None and port_nhipv6_map is not None :
498
+ subnet_check_ip = list (port_nhipv4_map .values ())[0 ]
499
+ for subnet_range in ip_intfs_map :
500
+ if ("." in subnet_range ):
501
+ a = ipaddress .ip_address (UNICODE_TYPE (subnet_check_ip ))
502
+ n = list (ipaddress .ip_network (UNICODE_TYPE (subnet_range ), False ).hosts ())
503
+ if a in n :
504
+ nhg_int = ip_intfs_map [subnet_range ]
505
+ dwnstrms = child .find (str (QName (ns , "DownstreamSummarySet" )))
506
+ for dwnstrm in dwnstrms .findall (str (QName (ns , "DownstreamSummary" ))):
507
+ dwnstrmentry = str (ET .tostring (dwnstrm ))
508
+ if ("FineGrainedECMPGroupDestination" in dwnstrmentry ):
509
+ subnet_ip = dwnstrm .find (str (QName (ns1 , "Subnet" ))).text
510
+ truncsubnet_ip = subnet_ip .split ("/" )[0 ]
511
+ if "." in (truncsubnet_ip ):
512
+ nhgaddr [0 ] = subnet_ip
513
+ elif ":" in (truncsubnet_ip ):
514
+ nhgaddr [1 ] = subnet_ip
515
+ ipv4_content = {"port_nhip_map" : port_nhipv4_map , "nhgaddr" : nhgaddr [0 ], "nhg_int" : nhg_int }
516
+ ipv6_content = {"port_nhip_map" : port_nhipv6_map , "nhgaddr" : nhgaddr [1 ], "nhg_int" : nhg_int }
517
+ dpg_ecmp_content ['ipv4' ] = ipv4_content
518
+ dpg_ecmp_content ['ipv6' ] = ipv6_content
385
519
vlanintfs = child .find (str (QName (ns , "VlanInterfaces" )))
386
520
vlan_intfs = []
387
521
vlans = {}
@@ -503,6 +637,7 @@ def parse_dpg(dpg, hname):
503
637
except :
504
638
print ("Warning: Ignoring Control Plane ACL %s without type" % aclname , file = sys .stderr )
505
639
640
+
506
641
mg_tunnels = child .find (str (QName (ns , "TunnelInterfaces" )))
507
642
if mg_tunnels is not None :
508
643
table_key_to_mg_key_map = {"encap_ecn_mode" : "EcnEncapsulationMode" ,
@@ -521,7 +656,7 @@ def parse_dpg(dpg, hname):
521
656
if mg_key in mg_tunnel .attrib :
522
657
tunnelintfs [tunnel_type ][tunnel_name ][table_key ] = mg_tunnel .attrib [mg_key ]
523
658
524
- return intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnelintfs
659
+ return intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnelintfs , dpg_ecmp_content
525
660
return None , None , None , None , None , None , None , None , None , None
526
661
527
662
def parse_host_loopback (dpg , hname ):
@@ -927,6 +1062,8 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
927
1062
bgp_monitors = []
928
1063
bgp_asn = None
929
1064
intfs = None
1065
+ dpg_ecmp_content = {}
1066
+ png_ecmp_content = {}
930
1067
vlan_intfs = None
931
1068
pc_intfs = None
932
1069
tunnel_intfs = None
@@ -988,13 +1125,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
988
1125
for child in root :
989
1126
if asic_name is None :
990
1127
if child .tag == str (QName (ns , "DpgDec" )):
991
- (intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnel_intfs ) = parse_dpg (child , hostname )
1128
+ (intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnel_intfs , dpg_ecmp_content ) = parse_dpg (child , hostname )
992
1129
elif child .tag == str (QName (ns , "CpgDec" )):
993
1130
(bgp_sessions , bgp_internal_sessions , bgp_asn , bgp_peers_with_range , bgp_monitors ) = parse_cpg (child , hostname )
994
1131
elif child .tag == str (QName (ns , "PngDec" )):
995
- (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 )
1132
+ (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 )
996
1133
elif child .tag == str (QName (ns , "UngDec" )):
997
- (u_neighbors , u_devices , _ , _ , _ , _ , _ , _ ) = parse_png (child , hostname )
1134
+ (u_neighbors , u_devices , _ , _ , _ , _ , _ , _ ) = parse_png (child , hostname , None )
998
1135
elif child .tag == str (QName (ns , "MetadataDeclaration" )):
999
1136
(syslog_servers , dhcp_servers , ntp_servers , tacacs_servers , mgmt_routes , erspan_dst , deployment_id , region , cloudtype , resource_type ) = parse_meta (child , hostname )
1000
1137
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
1003
1140
(port_speeds_default , port_descriptions ) = parse_deviceinfo (child , hwsku )
1004
1141
else :
1005
1142
if child .tag == str (QName (ns , "DpgDec" )):
1006
- (intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnel_intfs ) = parse_dpg (child , asic_name )
1143
+ (intfs , lo_intfs , mvrf , mgmt_intf , vlans , vlan_members , pcs , pc_members , acls , vni , tunnel_intfs , dpg_ecmp_content ) = parse_dpg (child , asic_name )
1007
1144
host_lo_intfs = parse_host_loopback (child , hostname )
1008
1145
elif child .tag == str (QName (ns , "CpgDec" )):
1009
1146
(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
1305
1442
'client_crt_cname' : 'client.restapi.sonic'
1306
1443
}
1307
1444
}
1445
+
1446
+ if len (png_ecmp_content ):
1447
+ results ['FG_NHG_MEMBER' ] = png_ecmp_content ['FG_NHG_MEMBER' ]
1448
+ results ['FG_NHG_PREFIX' ] = png_ecmp_content ['FG_NHG_PREFIX' ]
1449
+ results ['FG_NHG' ] = png_ecmp_content ['FG_NHG' ]
1450
+ results ['NEIGH' ] = png_ecmp_content ['NEIGH' ]
1451
+
1308
1452
# Do not configure the minigraph's mirror session, which is currently unused
1309
1453
# mirror_sessions = {}
1310
1454
# if erspan_dst:
0 commit comments