Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VoQ configuration using minigraph.xml file #5991

Merged
merged 1 commit into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is configuration is for the Line card host not asic, do we need these attributes ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are required in the case of single asic linecards. In this case the linecard is a single host device. For multi-asic linecards or multi-asic fixed systems these attributes needs to be parsed for each asic in parse_asic_meta.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the minigraph.xml this is defined under each asic.. We don't need this here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For single-asic linecards this is defined under the host's DpgDec section.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sysports are also for each asic. I dont think we need it here.

Copy link
Contributor Author

@mlorrillere mlorrillere Feb 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sysports a defined globally but needs to be programmed by each asic.


# 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
Copy link
Contributor

@vganesan-nokia vganesan-nokia Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VoQ system needs "asic_id" also. Setting of asic_id in ['DEVICE_METADATA']['localhost']['asic_id''] is done in sonic-cfggen. But this asic_id will be in simple number format like 0, 1, etc. Please make sure that this is over-written by asic_id (in either PCI device id format or simple number) from default_config.json before orchagent starts as discussed in sonic-net/sonic-utilities#1228.

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a unit tests for this ?

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