Skip to content

Commit

Permalink
[chassis] VoQ configuration using minigraph.xml file (#5991)
Browse files Browse the repository at this point in the history
This commit contains the following changes to support for configuring a VoQ switch using a minigraph.xml file.:
- Add support for system ports configuration to minigraph
- Add support for SwitchId, SwitchType and MaxCores to minigraph
- Add support for inband vlan configuration in minigraph
- `asic_name` is now a mandatory attribute in CONFIG_DB on VoQ switches

Co-authored-by: Maxime Lorrillere <mlorrillere@arista.com>
  • Loading branch information
mlorrillere and mlorrillere committed Apr 27, 2021
1 parent 5f435f2 commit a92da83
Show file tree
Hide file tree
Showing 3 changed files with 470 additions and 12 deletions.
102 changes: 90 additions & 12 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,17 @@ def parse_dpg(dpg, hname):
gwaddr = ipaddress.ip_address(next(mgmtipn.hosts()))
mgmt_intf[(intfname, ipprefix)] = {'gwaddr': gwaddr}

voqinbandintfs = child.find(str(QName(ns, "VoqInbandInterfaces")))
voq_inband_intfs = {}
if voqinbandintfs:
for voqintf in voqinbandintfs.findall(str(QName(ns1, "VoqInbandInterface"))):
intfname = voqintf.find(str(QName(ns, "Name"))).text
intftype = voqintf.find(str(QName(ns, "Type"))).text
ipprefix = voqintf.find(str(QName(ns1, "PrefixStr"))).text
if intfname not in voq_inband_intfs:
voq_inband_intfs[intfname] = {'inband_type': intftype}
voq_inband_intfs["%s|%s" % (intfname, ipprefix)] = {}

pcintfs = child.find(str(QName(ns, "PortChannelInterfaces")))
pc_intfs = []
pcs = {}
Expand Down Expand Up @@ -667,8 +678,8 @@ 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, dpg_ecmp_content
return None, None, None, None, None, None, None, None, None, None
return intfs, lo_intfs, mvrf, mgmt_intf, voq_inband_intfs, vlans, vlan_members, pcs, pc_members, acls, vni, tunnelintfs, dpg_ecmp_content
return None, None, None, None, None, None, None, None, None, None, None, None, None

def parse_host_loopback(dpg, hname):
for child in dpg:
Expand Down Expand Up @@ -794,6 +805,9 @@ def parse_meta(meta, hname):
cloudtype = None
resource_type = None
downstream_subrole = None
switch_id = None
switch_type = None
max_cores = None
kube_data = {}
device_metas = meta.find(str(QName(ns, "Devices")))
for device in device_metas.findall(str(QName(ns1, "DeviceMetadata"))):
Expand Down Expand Up @@ -825,11 +839,17 @@ def parse_meta(meta, hname):
resource_type = value
elif name == "DownStreamSubRole":
downstream_subrole = value
elif name == "SwitchId":
switch_id = value
elif name == "SwitchType":
switch_type = value
elif name == "MaxCores":
max_cores = value
elif name == "KubernetesEnabled":
kube_data["enable"] = value
elif name == "KubernetesServerIp":
kube_data["ip"] = value
return syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, kube_data
return syslog_servers, dhcp_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


def parse_linkmeta(meta, hname):
Expand Down Expand Up @@ -886,6 +906,9 @@ def parse_linkmeta(meta, hname):

def parse_asic_meta(meta, hname):
sub_role = None
switch_id = None
switch_type = None
max_cores = None
device_metas = meta.find(str(QName(ns, "Devices")))
for device in device_metas.findall(str(QName(ns1, "DeviceMetadata"))):
if device.find(str(QName(ns1, "Name"))).text.lower() == hname.lower():
Expand All @@ -895,7 +918,13 @@ def parse_asic_meta(meta, hname):
value = device_property.find(str(QName(ns1, "Value"))).text
if name == "SubRole":
sub_role = value
return sub_role
elif name == "SwitchId":
switch_id = value
elif name == "SwitchType":
switch_type = value
elif name == "MaxCores":
max_cores = value
return sub_role, switch_id, switch_type, max_cores

def parse_deviceinfo(meta, hwsku):
port_speeds = {}
Expand All @@ -912,7 +941,28 @@ def parse_deviceinfo(meta, hwsku):
if desc != None:
port_descriptions[port_alias_map.get(alias, alias)] = desc.text
port_speeds[port_alias_map.get(alias, alias)] = speed
return port_speeds, port_descriptions

sysports = device_info.find(str(QName(ns, "SystemPorts")))
sys_ports = {}
if sysports is not None:
for sysport in sysports.findall(str(QName(ns, "SystemPort"))):
portname = sysport.find(str(QName(ns, "Name"))).text
hostname = sysport.find(str(QName(ns, "Hostname")))
asic_name = sysport.find(str(QName(ns, "AsicName")))
system_port_id = sysport.find(str(QName(ns, "SystemPortId"))).text
switch_id = sysport.find(str(QName(ns, "SwitchId"))).text
core_id = sysport.find(str(QName(ns, "CoreId"))).text
core_port_id = sysport.find(str(QName(ns, "CorePortId"))).text
speed = sysport.find(str(QName(ns, "Speed"))).text
num_voq = sysport.find(str(QName(ns, "NumVoq"))).text
key = portname
if asic_name is not None:
key = "%s|%s" % (asic_name.text, key)
if hostname is not None:
key = "%s|%s" % (hostname.text, key)
sys_ports[key] = {"system_port_id": system_port_id, "switch_id": switch_id, "core_index": core_id, "core_port_index": core_port_id, "speed": speed, "num_voq": num_voq}

return port_speeds, port_descriptions, sys_ports

# Function to check if IP address is present in the key.
# If it is present, then the key would be a tuple.
Expand Down Expand Up @@ -1097,6 +1147,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
vlan_members = None
pcs = None
mgmt_intf = None
voq_inband_intfs = None
lo_intfs = None
neighbors = None
devices = None
Expand All @@ -1107,6 +1158,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
port_speeds_default = {}
port_speed_png = {}
port_descriptions = {}
sys_ports = {}
console_ports = {}
mux_cable_ports = {}
syslog_servers = []
Expand All @@ -1119,6 +1171,9 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
deployment_id = None
region = None
cloudtype = None
switch_id = None
switch_type = None
max_cores = None
hostname = None
linkmetas = {}
host_lo_intfs = None
Expand Down Expand Up @@ -1153,33 +1208,33 @@ 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, dpg_ecmp_content) = parse_dpg(child, hostname)
(intfs, lo_intfs, mvrf, mgmt_intf, voq_inband_intfs, 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_voq_chassis_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, 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, 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, downstream_subrole, kube_data) = parse_meta(child, hostname)
(syslog_servers, dhcp_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) = parse_meta(child, hostname)
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
linkmetas = parse_linkmeta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
(port_speeds_default, port_descriptions, sys_ports) = 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, dpg_ecmp_content) = parse_dpg(child, asic_name)
(intfs, lo_intfs, mvrf, mgmt_intf, voq_inband_intfs, 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_voq_chassis_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices)
elif child.tag == str(QName(ns, "PngDec")):
(neighbors, devices, port_speed_png) = parse_asic_png(child, asic_name, hostname)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(sub_role) = parse_asic_meta(child, asic_name)
(sub_role, switch_id, switch_type, max_cores ) = parse_asic_meta(child, asic_name)
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
linkmetas = parse_linkmeta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
(port_speeds_default, port_descriptions, sys_ports) = parse_deviceinfo(child, hwsku)

# set the host device type in asic metadata also
device_type = [devices[key]['type'] for key in devices if key.lower() == hostname.lower()][0]
Expand Down Expand Up @@ -1232,6 +1287,26 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
current_device['sub_role'] = sub_role
results['DEVICE_METADATA']['localhost']['sub_role'] = sub_role
results['DEVICE_METADATA']['localhost']['asic_name'] = asic_name
elif switch_type == "voq":
# On Voq switches asic_name is mandatory even on single-asic devices
results['DEVICE_METADATA']['localhost']['asic_name'] = 'Asic0'

# on Voq system each asic has a switch_id
if switch_id is not None:
results['DEVICE_METADATA']['localhost']['switch_id'] = switch_id
# on Voq system each asic has a switch_type
if switch_type is not None:
results['DEVICE_METADATA']['localhost']['switch_type'] = switch_type
# on Voq system each asic has a max_cores
if max_cores is not None:
results['DEVICE_METADATA']['localhost']['max_cores'] = max_cores

# Voq systems have an inband interface
if voq_inband_intfs is not None:
results['VOQ_INBAND_INTERFACE'] = {}
for key in voq_inband_intfs:
results['VOQ_INBAND_INTERFACE'][key] = voq_inband_intfs[key]


if resource_type is not None:
results['DEVICE_METADATA']['localhost']['resource_type'] = resource_type
Expand Down Expand Up @@ -1312,6 +1387,9 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
results['INTERFACE'] = phyport_intfs
results['VLAN_INTERFACE'] = vlan_intfs

if sys_ports:
results['SYSTEM_PORT'] = sys_ports

for port_name in port_speeds_default:
# ignore port not in port_config.ini
if port_name not in ports:
Expand Down Expand Up @@ -1597,7 +1675,7 @@ def parse_asic_sub_role(filename, asic_name):
root = ET.parse(filename).getroot()
for child in root:
if child.tag == str(QName(ns, "MetadataDeclaration")):
sub_role = parse_asic_meta(child, asic_name)
sub_role, _, _, _ = parse_asic_meta(child, asic_name)
return sub_role

def parse_asic_meta_get_devices(root):
Expand Down
Loading

0 comments on commit a92da83

Please sign in to comment.