Skip to content

Commit 940dd94

Browse files
committed
Minigraph ECMP parsing support (cleaner format)
1 parent df13245 commit 940dd94

File tree

3 files changed

+255
-9
lines changed

3 files changed

+255
-9
lines changed

src/sonic-config-engine/minigraph.py

+153-9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from lxml import etree as ET
1111
from lxml.etree import QName
1212

13+
1314
from portconfig import get_port_config
1415
from sonic_py_common.multi_asic import get_asic_id_from_name
1516
from sonic_py_common.interface import backplane_prefix
@@ -105,7 +106,73 @@ def parse_device(device):
105106

106107
return (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id)
107108

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):
109176
neighbors = {}
110177
devices = {}
111178
console_dev = ''
@@ -116,6 +183,13 @@ def parse_png(png, hname):
116183
console_ports = {}
117184
mux_cable_ports = {}
118185
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+
119193
for child in png:
120194
if child.tag == str(QName(ns, "DeviceInterfaceLinks")):
121195
for link in child.findall(str(QName(ns, "DeviceLinkBase"))):
@@ -141,6 +215,11 @@ def parse_png(png, hname):
141215
}
142216
continue
143217

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+
144223
if linktype != "DeviceInterfaceLink" and linktype != "UnderlayInterfaceLink":
145224
continue
146225

@@ -196,6 +275,7 @@ def parse_png(png, hname):
196275
elif node.tag == str(QName(ns, "EndDevice")):
197276
mgmt_dev = node.text
198277

278+
199279
if child.tag == str(QName(ns, "DeviceInterfaceLinks")):
200280
for link in child.findall(str(QName(ns, 'DeviceLinkBase'))):
201281
if link.find(str(QName(ns, "ElementType"))).text == "LogicalLink":
@@ -205,7 +285,19 @@ def parse_png(png, hname):
205285

206286
mux_cable_ports[intf_name] = "true"
207287

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+
209301

210302
def parse_asic_external_link(link, asic_name, hostname):
211303
neighbors = {}
@@ -295,6 +387,8 @@ def parse_asic_png(png, asic_name, hostname):
295387
if lo_prefix_v6:
296388
device_data['lo_addr_v6']= lo_prefix_v6
297389
devices[name] = device_data
390+
391+
298392
return (neighbors, devices, port_speeds)
299393

300394
def parse_loopback_intf(child):
@@ -339,12 +433,13 @@ def parse_dpg(dpg, hname):
339433

340434
ipintfs = child.find(str(QName(ns, "IPInterfaces")))
341435
intfs = {}
436+
ip_intfs_map = {}
342437
for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))):
343438
intfalias = ipintf.find(str(QName(ns, "AttachTo"))).text
344439
intfname = port_alias_map.get(intfalias, intfalias)
345440
ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text
346441
intfs[(intfname, ipprefix)] = {}
347-
442+
ip_intfs_map[ipprefix] = intfalias
348443
lo_intfs = parse_loopback_intf(child)
349444

350445
mvrfConfigs = child.find(str(QName(ns, "MgmtVrfConfigs")))
@@ -381,7 +476,46 @@ def parse_dpg(dpg, hname):
381476
pcs[pcintfname] = {'members': pcmbr_list, 'fallback': pcintf.find(str(QName(ns, "Fallback"))).text, 'min_links': str(int(math.ceil(len() * 0.75)))}
382477
else:
383478
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
385519
vlanintfs = child.find(str(QName(ns, "VlanInterfaces")))
386520
vlan_intfs = []
387521
vlans = {}
@@ -503,6 +637,7 @@ def parse_dpg(dpg, hname):
503637
except:
504638
print("Warning: Ignoring Control Plane ACL %s without type" % aclname, file=sys.stderr)
505639

640+
506641
mg_tunnels = child.find(str(QName(ns, "TunnelInterfaces")))
507642
if mg_tunnels is not None:
508643
table_key_to_mg_key_map = {"encap_ecn_mode": "EcnEncapsulationMode",
@@ -521,7 +656,7 @@ def parse_dpg(dpg, hname):
521656
if mg_key in mg_tunnel.attrib:
522657
tunnelintfs[tunnel_type][tunnel_name][table_key] = mg_tunnel.attrib[mg_key]
523658

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
525660
return None, None, None, None, None, None, None, None, None, None
526661

527662
def parse_host_loopback(dpg, hname):
@@ -927,6 +1062,8 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
9271062
bgp_monitors = []
9281063
bgp_asn = None
9291064
intfs = None
1065+
dpg_ecmp_content = {}
1066+
png_ecmp_content = {}
9301067
vlan_intfs = None
9311068
pc_intfs = None
9321069
tunnel_intfs = None
@@ -988,13 +1125,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
9881125
for child in root:
9891126
if asic_name is None:
9901127
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)
9921129
elif child.tag == str(QName(ns, "CpgDec")):
9931130
(bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname)
9941131
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)
9961133
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)
9981135
elif child.tag == str(QName(ns, "MetadataDeclaration")):
9991136
(syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type) = parse_meta(child, hostname)
10001137
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
10031140
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
10041141
else:
10051142
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)
10071144
host_lo_intfs = parse_host_loopback(child, hostname)
10081145
elif child.tag == str(QName(ns, "CpgDec")):
10091146
(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
13051442
'client_crt_cname': 'client.restapi.sonic'
13061443
}
13071444
}
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+
13081452
# Do not configure the minigraph's mirror session, which is currently unused
13091453
# mirror_sessions = {}
13101454
# if erspan_dst:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# name lanes alias index
2+
Ethernet0 0,1,2,3 etp1 1
3+
Ethernet4 4,5,6,7 etp2 2
4+
Ethernet8 8,9,10,11 etp3 3
5+
Ethernet12 12,13,14,15 etp4 4
6+
Ethernet16 16,17,18,19 etp5 5
7+
Ethernet20 20,21,22,23 etp6 6
8+
Ethernet24 24,25,26,27 etp7 7
9+
Ethernet28 28,29,30,31 etp8 8
10+
Ethernet32 32,33,34,35 etp9 9
11+
Ethernet36 36,37,38,39 etp10 10
12+
Ethernet40 40,41,42,43 etp11 11
13+
Ethernet44 44,45,46,47 etp12 12
14+
Ethernet48 48,49,50,51 etp13 13
15+
Ethernet52 52,53,54,55 etp14 14
16+
Ethernet56 56,57,58,59 etp15 15
17+
Ethernet60 60,61,62,63 etp16 16
18+
Ethernet64 64,65,66,67 etp17 17
19+
Ethernet68 68,69,70,71 etp18 18
20+
Ethernet72 72,73,74,75 etp19 19
21+
Ethernet76 76,77,78,79 etp20 20
22+
Ethernet80 80,81,82,83 etp21 21
23+
Ethernet84 84,85,86,87 etp22 22
24+
Ethernet88 88,89,90,91 etp23 23
25+
Ethernet92 92,93,94,95 etp24 24
26+
Ethernet96 96,97,98,99 etp25 25
27+
Ethernet100 100,101,102,103 etp26 26
28+
Ethernet104 104,105,106,107 etp27 27
29+
Ethernet108 108,109,110,111 etp28 28
30+
Ethernet112 112,113,114,115 etp29 29
31+
Ethernet116 116,117,118,119 etp30 30
32+
Ethernet120 120,121,122,123 etp31 31
33+
Ethernet124 124,125,126,127 etp32 32
34+
Ethernet128 128,129,130,131 etp33 33
35+
Ethernet132 132,133,134,135 etp34 34
36+
Ethernet136 136,137,138,139 etp35 35
37+
Ethernet140 140,141,142,143 etp36 36
38+
Ethernet144 144,145,146,147 etp37 37
39+
Ethernet148 148,149,150,151 etp38 38
40+
Ethernet152 152,153,154,155 etp39 39
41+
Ethernet156 156,157,158,159 etp40 40
42+
Ethernet160 160,161,162,163 etp41 41
43+
Ethernet164 164,165,166,167 etp42 42
44+
Ethernet168 168,169,170,171 etp43 43
45+
Ethernet172 172,173,174,175 etp44 44
46+
Ethernet176 176,177,178,179 etp45 45
47+
Ethernet180 180,181,182,183 etp46 46
48+
Ethernet184 184,185,186,187 etp47 47
49+
Ethernet188 188,189,190,191 etp48 48
50+
Ethernet192 192,193,194,195 etp49 49
51+
Ethernet196 196,197,198,199 etp50 50
52+
Ethernet200 200,201,202,203 etp51 51
53+
Ethernet204 204,205,206,207 etp52 52
54+
Ethernet208 208,209,210,211 etp53 53
55+
Ethernet212 212,213,214,215 etp54 54
56+
Ethernet216 216,217,218,219 etp55 55
57+
Ethernet220 220,221,222,223 etp56 56
58+
Ethernet224 224,225,226,227 etp57 57
59+
Ethernet228 228,229,230,231 etp58 58
60+
Ethernet232 232,233,234,235 etp59 59
61+
Ethernet236 236,237,238,239 etp60 60
62+
Ethernet240 240,241,242,243 etp61 61
63+
Ethernet244 244,245,246,247 etp62 62
64+
Ethernet248 248,249,250,251 etp63 63
65+
Ethernet252 252,253,254,255 etp64 64

0 commit comments

Comments
 (0)