[config engine] Parser changes to support parsing of multi-asic device minigraph (#4222)

- Changes to minigraph.py to parse minigraph.xml of a multi asic platform 
- Changes to portconfig.py to parse additional column "asic_port_name" in
port_config.ini
- Add a new option -n to sonic-cfggen for multi asic platforms
- Add unit tests for config generation for multi asic platforms

Signed-off-by: SuvarnaMeenakshi <sumeenak@microsoft.com>
Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <arlakshm@microsoft.com>
This commit is contained in:
SuvarnaMeenakshi 2020-05-04 16:15:15 -07:00 committed by GitHub
parent 86e13907b4
commit 8ac1c60b2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1752 additions and 46 deletions

View File

@ -14,6 +14,7 @@ from lxml import etree as ET
from lxml.etree import QName
from portconfig import get_port_config
from sonic_device_util import get_npu_id_from_name
"""minigraph.py
version_added: "1.9"
@ -118,14 +119,13 @@ def parse_png(png, hname):
startport = link.find(str(QName(ns, "StartPort"))).text
bandwidth_node = link.find(str(QName(ns, "Bandwidth")))
bandwidth = bandwidth_node.text if bandwidth_node is not None else None
if enddevice.lower() == hname.lower():
if port_alias_map.has_key(endport):
endport = port_alias_map[endport]
neighbors[endport] = {'name': startdevice, 'port': startport}
if bandwidth:
port_speeds[endport] = bandwidth
else:
elif startdevice.lower() == hname.lower():
if port_alias_map.has_key(startport):
startport = port_alias_map[startport]
neighbors[startport] = {'name': enddevice, 'port': endport}
@ -159,9 +159,103 @@ def parse_png(png, hname):
return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speeds, console_ports)
def parse_asic_external_link(link, asic_name, hostname):
neighbors = {}
port_speeds = {}
enddevice = link.find(str(QName(ns, "EndDevice"))).text
endport = link.find(str(QName(ns, "EndPort"))).text
startdevice = link.find(str(QName(ns, "StartDevice"))).text
startport = link.find(str(QName(ns, "StartPort"))).text
bandwidth_node = link.find(str(QName(ns, "Bandwidth")))
bandwidth = bandwidth_node.text if bandwidth_node is not None else None
# if chassis internal is false, the interface name will be
# interface alias which should be converted to asic port name
if (enddevice.lower() == hostname.lower()):
if ((port_alias_asic_map.has_key(endport)) and
(asic_name.lower() in port_alias_asic_map[endport].lower())):
endport = port_alias_asic_map[endport]
neighbors[port_alias_map[endport]] = {'name': startdevice, 'port': startport}
if bandwidth:
port_speeds[port_alias_map[endport]] = bandwidth
elif (startdevice.lower() == hostname.lower()):
if ((port_alias_asic_map.has_key(startport)) and
(asic_name.lower() in port_alias_asic_map[startport].lower())):
startport = port_alias_asic_map[startport]
neighbors[port_alias_map[startport]] = {'name': enddevice, 'port': endport}
if bandwidth:
port_speeds[port_alias_map[startport]] = bandwidth
return neighbors, port_speeds
def parse_asic_internal_link(link, asic_name, hostname):
neighbors = {}
port_speeds = {}
enddevice = link.find(str(QName(ns, "EndDevice"))).text
endport = link.find(str(QName(ns, "EndPort"))).text
startdevice = link.find(str(QName(ns, "StartDevice"))).text
startport = link.find(str(QName(ns, "StartPort"))).text
bandwidth_node = link.find(str(QName(ns, "Bandwidth")))
bandwidth = bandwidth_node.text if bandwidth_node is not None else None
if ((enddevice.lower() == asic_name.lower()) and
(startdevice.lower() != hostname.lower())):
if port_alias_map.has_key(endport):
endport = port_alias_map[endport]
neighbors[endport] = {'name': startdevice, 'port': startport}
if bandwidth:
port_speeds[endport] = bandwidth
elif ((startdevice.lower() == asic_name.lower()) and
(enddevice.lower() != hostname.lower())):
if port_alias_map.has_key(startport):
startport = port_alias_map[startport]
neighbors[startport] = {'name': enddevice, 'port': endport}
if bandwidth:
port_speeds[startport] = bandwidth
return neighbors, port_speeds
def parse_asic_png(png, asic_name, hostname):
neighbors = {}
devices = {}
port_speeds = {}
for child in png:
if child.tag == str(QName(ns, "DeviceInterfaceLinks")):
for link in child.findall(str(QName(ns, "DeviceLinkBase"))):
# Chassis internal node is used in multi-asic device or chassis minigraph
# where the minigraph will contain the internal asic connectivity and
# external neighbor information. The ChassisInternal node will be used to
# determine if the link is internal to the device or chassis.
chassis_internal_node = link.find(str(QName(ns, "ChassisInternal")))
chassis_internal = chassis_internal_node.text if chassis_internal_node is not None else "false"
# If the link is an external link include the external neighbor
# information in ASIC ports table
if chassis_internal.lower() == "false":
ext_neighbors, ext_port_speeds = parse_asic_external_link(link, asic_name, hostname)
neighbors.update(ext_neighbors)
port_speeds.update(ext_port_speeds)
else:
int_neighbors, int_port_speeds = parse_asic_internal_link(link, asic_name, hostname)
neighbors.update(int_neighbors)
port_speeds.update(int_port_speeds)
if child.tag == str(QName(ns, "Devices")):
for device in child.findall(str(QName(ns, "Device"))):
(lo_prefix, mgmt_prefix, name, hwsku, d_type, deployment_id) = parse_device(device)
device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku }
if deployment_id:
device_data['deployment_id'] = deployment_id
devices[name] = device_data
return (neighbors, devices, port_speeds)
def parse_dpg(dpg, hname):
for child in dpg:
"""In Multi-NPU platforms the acl intfs are defined only for the host not for individual asic.
There is just one aclintf node in the minigraph
Get the aclintfs node first.
"""
if child.find(str(QName(ns, "AclInterfaces"))) is not None:
aclintfs = child.find(str(QName(ns, "AclInterfaces")))
hostname = child.find(str(QName(ns, "Hostname")))
if hostname.text.lower() != hname.lower():
continue
@ -254,7 +348,6 @@ def parse_dpg(dpg, hname):
vlan_attributes['alias'] = vintfname
vlans[sonic_vlan_name] = vlan_attributes
aclintfs = child.find(str(QName(ns, "AclInterfaces")))
acls = {}
for aclintf in aclintfs.findall(str(QName(ns, "AclInterface"))):
if aclintf.find(str(QName(ns, "InAcl"))) is not None:
@ -369,7 +462,7 @@ def parse_cpg(cpg, hname):
'keepalive': keepalive,
'nhopself': nhopself
}
else:
elif start_router.lower() == hname.lower():
bgp_sessions[end_peer.lower()] = {
'name': end_router,
'local_addr': start_peer.lower(),
@ -446,6 +539,19 @@ def parse_meta(meta, hname):
region = value
return syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region
def parse_asic_meta(meta, hname):
sub_role = 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():
properties = device.find(str(QName(ns1, "Properties")))
for device_property in properties.findall(str(QName(ns1, "DeviceProperty"))):
name = device_property.find(str(QName(ns1, "Name"))).text
value = device_property.find(str(QName(ns1, "Value"))).text
if name == "SubRole":
sub_role = value
return sub_role
def parse_deviceinfo(meta, hwsku):
port_speeds = {}
port_descriptions = {}
@ -480,8 +586,7 @@ def parse_spine_chassis_fe(results, vni, lo_intfs, phyport_intfs, pc_intfs, pc_m
lo_network = ipaddress.IPNetwork(lo[1])
if lo_network.version == 4:
lo_addr = str(lo_network.ip)
break
break
results['VXLAN_TUNNEL'] = {chassis_vxlan_tunnel: {
'src_ip': lo_addr
}}
@ -520,7 +625,7 @@ def parse_spine_chassis_fe(results, vni, lo_intfs, phyport_intfs, pc_intfs, pc_m
for pc_member in pc_members:
if pc_member[0] == pc_intf:
intf_name = pc_member[1]
break
break
if intf_name == None:
print >> sys.stderr, 'Warning: cannot find any interfaces that belong to %s' % (pc_intf)
@ -567,8 +672,16 @@ def filter_acl_mirror_table_bindings(acls, neighbors, port_channels):
# Main functions
#
###############################################################################
def parse_xml(filename, platform=None, port_config_file=None, asic_name=None):
""" Parse minigraph xml file.
def parse_xml(filename, platform=None, port_config_file=None):
Keyword arguments:
filename -- minigraph file name
platform -- device platform
port_config_file -- port config file name
asic_name -- asic name; to parse multi-asic device minigraph to
generate asic specific configuration.
"""
root = ET.parse(filename).getroot()
mini_graph_path = filename
@ -588,7 +701,7 @@ def parse_xml(filename, platform=None, port_config_file=None):
lo_intfs = None
neighbors = None
devices = None
hostname = None
sub_role = None
docker_routing_config_mode = "separated"
port_speeds_default = {}
port_speed_png = {}
@ -603,6 +716,13 @@ def parse_xml(filename, platform=None, port_config_file=None):
bgp_peers_with_range = None
deployment_id = None
region = None
hostname = None
#hostname is the asic_name, get the asic_id from the asic_name
if asic_name is not None:
asic_id = get_npu_id_from_name(asic_name)
else:
asic_id = None
hwsku_qn = QName(ns, "HwSku")
hostname_qn = QName(ns, "Hostname")
@ -615,34 +735,59 @@ def parse_xml(filename, platform=None, port_config_file=None):
if child.tag == str(docker_routing_config_mode_qn):
docker_routing_config_mode = child.text
(ports, alias_map) = get_port_config(hwsku, platform, port_config_file)
(ports, alias_map, alias_asic_map) = get_port_config(hwsku=hwsku, platform=platform, port_config_file=port_config_file, asic=asic_id)
port_alias_map.update(alias_map)
for child in root:
if child.tag == str(QName(ns, "DpgDec")):
(intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_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) = parse_png(child, hostname)
elif child.tag == str(QName(ns, "UngDec")):
(u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region) = parse_meta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
port_alias_asic_map.update(alias_asic_map)
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) = parse_dpg(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_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) = parse_png(child, hostname)
elif child.tag == str(QName(ns, "UngDec")):
(u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, device_hostname)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region) = parse_meta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
(port_speeds_default, port_descriptions) = 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) = parse_dpg(child, asic_name)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name)
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)
elif child.tag == str(QName(ns, "DeviceInfos")):
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
if asic_name is None:
current_device = [devices[key] for key in devices if key.lower() == hostname.lower()][0]
name = hostname
else:
current_device = [devices[key] for key in devices if key.lower() == asic_name.lower()][0]
name = asic_name
current_device = [devices[key] for key in devices if key.lower() == hostname.lower()][0]
results = {}
results['DEVICE_METADATA'] = {'localhost': {
'bgp_asn': bgp_asn,
'deployment_id': deployment_id,
'region': region,
'docker_routing_config_mode': docker_routing_config_mode,
'hostname': hostname,
'hostname': name,
'hwsku': hwsku,
'type': current_device['type']
}
}
# for this hostname, if sub_role is defined, add sub_role in
# device_metadata
if sub_role is not None:
current_device['sub_role'] = sub_role
results['DEVICE_METADATA']['localhost']['sub_role'] = sub_role
results['BGP_NEIGHBOR'] = bgp_sessions
results['BGP_MONITORS'] = bgp_monitors
results['BGP_PEER_RANGE'] = bgp_peers_with_range
@ -703,9 +848,11 @@ def parse_xml(filename, platform=None, port_config_file=None):
for port_name in port_speed_png:
# not consider port not in port_config.ini
if port_name not in ports:
print >> sys.stderr, "Warning: ignore interface '%s' as it is not in the port_config.ini" % port_name
continue
#If no port_config_file is found ports is empty so ignore this error
if port_config_file is not None:
if port_name not in ports:
print >> sys.stderr, "Warning: ignore interface '%s' as it is not in the port_config.ini" % port_name
continue
ports.setdefault(port_name, {})['speed'] = port_speed_png[port_name]
@ -809,11 +956,14 @@ def parse_xml(filename, platform=None, port_config_file=None):
for nghbr in neighbors.keys():
# remove port not in port_config.ini
if nghbr not in ports:
print >> sys.stderr, "Warning: ignore interface '%s' in DEVICE_NEIGHBOR as it is not in the port_config.ini" % nghbr
if port_config_file is not None:
print >> sys.stderr, "Warning: ignore interface '%s' in DEVICE_NEIGHBOR as it is not in the port_config.ini" % nghbr
del neighbors[nghbr]
results['DEVICE_NEIGHBOR'] = neighbors
results['DEVICE_NEIGHBOR_METADATA'] = { key:devices[key] for key in devices if key.lower() != hostname.lower() }
if asic_name is None:
results['DEVICE_NEIGHBOR_METADATA'] = { key:devices[key] for key in devices if key.lower() != hostname.lower() }
else:
results['DEVICE_NEIGHBOR_METADATA'] = { key:devices[key] for key in devices if key in {device['name'] for device in neighbors.values()} }
results['SYSLOG_SERVER'] = dict((item, {}) for item in syslog_servers)
results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers)
results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers)
@ -890,8 +1040,17 @@ def parse_device_desc_xml(filename):
return results
def parse_asic_sub_role(filename, asic_name):
if not os.path.isfile(filename):
return None
root = ET.parse(filename).getroot()
for child in root:
if child.tag == str(QName(ns, "MetadataDeclaration")):
sub_role = parse_asic_meta(child, asic_name)
return sub_role
port_alias_map = {}
port_alias_asic_map = {}
def print_parse_xml(filename):

View File

@ -3,11 +3,13 @@ import os
import sys
def get_port_config_file_name(hwsku=None, platform=None):
def get_port_config_file_name(hwsku=None, platform=None, asic=None):
port_config_candidates = []
port_config_candidates.append('/usr/share/sonic/hwsku/port_config.ini')
if hwsku:
if platform:
if asic:
port_config_candidates.append(os.path.join('/usr/share/sonic/device', platform, hwsku, asic,'port_config.ini'))
port_config_candidates.append(os.path.join('/usr/share/sonic/device', platform, hwsku, 'port_config.ini'))
port_config_candidates.append(os.path.join('/usr/share/sonic/platform', hwsku, 'port_config.ini'))
port_config_candidates.append(os.path.join('/usr/share/sonic', hwsku, 'port_config.ini'))
@ -17,17 +19,19 @@ def get_port_config_file_name(hwsku=None, platform=None):
return None
def get_port_config(hwsku=None, platform=None, port_config_file=None):
def get_port_config(hwsku=None, platform=None, port_config_file=None, asic=None):
if not port_config_file:
port_config_file = get_port_config_file_name(hwsku, platform)
port_config_file = get_port_config_file_name(hwsku, platform, asic)
if not port_config_file:
return ({}, {})
return ({}, {}, {})
return parse_port_config_file(port_config_file)
def parse_port_config_file(port_config_file):
ports = {}
port_alias_map = {}
port_alias_asic_map = {}
# Default column definition
titles = ['name', 'lanes', 'alias', 'index']
with open(port_config_file) as data:
@ -49,6 +53,14 @@ def parse_port_config_file(port_config_file):
data.setdefault('alias', name)
ports[name] = data
port_alias_map[data['alias']] = name
return (ports, port_alias_map)
# asic_port_name to sonic_name mapping also included in
# port_alias_map
if (('asic_port_name' in data) and
(data['asic_port_name'] != name)):
port_alias_map[data['asic_port_name']] = name
# alias to asic_port_name mapping
if 'asic_port_name' in data:
port_alias_asic_map[data['alias']] = data['asic_port_name'].strip()
return (ports, port_alias_map, port_alias_asic_map)

View File

@ -37,10 +37,12 @@ from functools import partial
from minigraph import minigraph_encoder
from minigraph import parse_xml
from minigraph import parse_device_desc_xml
from minigraph import parse_asic_sub_role
from portconfig import get_port_config
from sonic_device_util import get_machine_info
from sonic_device_util import get_platform_info
from sonic_device_util import get_system_mac
from sonic_device_util import get_npu_id_from_name
from config_samples import generate_sample_config
from config_samples import get_available_config
from swsssdk import SonicV2Connector, ConfigDBConnector
@ -195,6 +197,7 @@ def main():
group.add_argument("-m", "--minigraph", help="minigraph xml file", nargs='?', const='/etc/sonic/minigraph.xml')
group.add_argument("-M", "--device-description", help="device description xml file")
group.add_argument("-k", "--hwsku", help="HwSKU")
parser.add_argument("-n", "--namespace", help="namespace name, used with -m or -k", nargs='?', const=None)
parser.add_argument("-p", "--port-config", help="port config file, used with -m or -k", nargs='?', const=None)
parser.add_argument("-y", "--yaml", help="yaml file that contains additional variables", action='append', default=[])
parser.add_argument("-j", "--json", help="json file that contains additional variables", action='append', default=[])
@ -222,13 +225,18 @@ def main():
data = {}
hwsku = args.hwsku
asic_name = args.namespace
asic_id = None
if asic_name is not None:
asic_id = get_npu_id_from_name(asic_name)
if hwsku is not None:
hardware_data = {'DEVICE_METADATA': {'localhost': {
'hwsku': hwsku
}}}
deep_update(data, hardware_data)
(ports, _) = get_port_config(hwsku, platform, args.port_config)
(ports, _, _) = get_port_config(hwsku, platform, args.port_config, asic_id)
if not ports:
print('Failed to get port config', file=sys.stderr)
sys.exit(1)
@ -242,11 +250,11 @@ def main():
minigraph = args.minigraph
if platform:
if args.port_config != None:
deep_update(data, parse_xml(minigraph, platform, args.port_config))
deep_update(data, parse_xml(minigraph, platform, args.port_config, asic_name=asic_name))
else:
deep_update(data, parse_xml(minigraph, platform))
deep_update(data, parse_xml(minigraph, platform, asic_name=asic_name))
else:
deep_update(data, parse_xml(minigraph, port_config_file=args.port_config))
deep_update(data, parse_xml(minigraph, port_config_file=args.port_config, asic_name=asic_name))
if args.device_description != None:
deep_update(data, parse_device_desc_xml(args.device_description))
@ -267,11 +275,28 @@ def main():
configdb.connect()
deep_update(data, FormatConverter.db_to_output(configdb.get_config()))
# the minigraph file must be provided to get the mac address for backend asics
if args.platform_info:
asic_role = None
if asic_name is not None:
if args.minigraph is not None:
asic_role = parse_asic_sub_role(args.minigraph, asic_name)
if asic_role is not None and asic_role.lower() == "backend":
mac = get_system_mac(namespace=asic_name)
else:
mac = get_system_mac()
else:
mac = get_system_mac()
hardware_data = {'DEVICE_METADATA': {'localhost': {
'platform': platform,
'mac': get_system_mac()
'mac': mac,
}}}
# The ID needs to be passed to the SAI to identify the asic.
if asic_name is not None:
hardware_data['DEVICE_METADATA']['localhost'].update(asic_id=asic_id)
deep_update(data, hardware_data)
if args.template is not None:

View File

@ -3,7 +3,8 @@ import os
import yaml
import subprocess
import re
from natsort import natsorted
import glob
DOCUMENTATION = '''
---
module: sonic_device_util
@ -17,6 +18,9 @@ description:
TODO: this file shall be renamed and moved to other places in future
to have it shared with multiple applications.
'''
SONIC_DEVICE_PATH = '/usr/share/sonic/device'
NPU_NAME_PREFIX = 'asic'
NAMESPACE_PATH_GLOB = '/run/netns/*'
def get_machine_info():
if not os.path.isfile('/host/machine.conf'):
return None
@ -27,7 +31,38 @@ def get_machine_info():
if len(tokens) < 2:
continue
machine_vars[tokens[0]] = tokens[1].strip()
return machine_vars
return machine_vars
def get_npu_id_from_name(npu_name):
if npu_name.startswith(NPU_NAME_PREFIX):
return npu_name[len(NPU_NAME_PREFIX):]
else:
return None
def get_num_npus():
platform = get_platform_info(get_machine_info())
asic_conf_file_path = os.path.join(SONIC_DEVICE_PATH, platform, 'asic.conf')
if not os.path.isfile(asic_conf_file_path):
return 1
with open(asic_conf_file_path) as asic_conf_file:
for line in asic_conf_file:
tokens = line.split('=')
if len(tokens) < 2:
continue
if tokens[0].lower() == 'num_asic':
num_npus = tokens[1].strip()
return num_npus
def get_namespaces():
"""
In a multi NPU platform, each NPU is in a Linux Namespace.
This method returns list of all the Namespace present on the device
"""
ns_list = []
for path in glob.glob(NAMESPACE_PATH_GLOB):
ns = os.path.basename(path)
ns_list.append(ns)
return natsorted(ns_list)
def get_platform_info(machine_info):
if machine_info != None:
@ -51,7 +86,7 @@ def get_sonic_version_info():
def valid_mac_address(mac):
return bool(re.match("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", mac))
def get_system_mac():
def get_system_mac(namespace=None):
version_info = get_sonic_version_info()
if (version_info['asic_type'] == 'mellanox'):
@ -73,10 +108,14 @@ def get_system_mac():
# Try valid mac in eeprom, else fetch it from eth0
platform = get_platform_info(get_machine_info())
hwsku = get_machine_info()['onie_machine']
profile_cmd = 'cat /usr/share/sonic/device/' + platform +'/'+ hwsku +'/profile.ini | cut -f2 -d='
profile_cmd = 'cat' + SONIC_DEVICE_PATH + '/' + platform +'/'+ hwsku +'/profile.ini | cut -f2 -d='
hw_mac_entry_cmds = [ profile_cmd, "sudo decode-syseeprom -m", "ip link show eth0 | grep ether | awk '{print $2}'" ]
else:
hw_mac_entry_cmds = [ "ip link show eth0 | grep ether | awk '{print $2}'" ]
mac_address_cmd = "cat /sys/class/net/eth0/address"
if namespace is not None:
mac_address_cmd = "sudo ip netns exec {} {}".format(namespace, mac_address_cmd)
hw_mac_entry_cmds = [mac_address_cmd]
for get_mac_cmd in hw_mac_entry_cmds:
proc = subprocess.Popen(get_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
# name lanes alias asic_port_name
Ethernet0 33,34,35,36 Ethernet1/1 Eth0-ASIC0
Ethernet4 29,30,31,32 Ethernet1/2 Eth1-ASIC0
Ethernet8 41,42,43,44 Ethernet1/3 Eth2-ASIC0
Ethernet12 37,38,39,40 Ethernet1/4 Eth3-ASIC0
Ethernet-BP0 13,14,15,16 Ethernet-BP0 Eth4-ASIC0
Ethernet-BP4 17,18,19,20 Ethernet-BP4 Eth5-ASIC0
Ethernet-BP8 21,22,23,24 Ethernet-BP8 Eth6-ASIC0
Ethernet-BP12 25,26,27,28 Ethernet-BP12 Eth7-ASIC0

View File

@ -0,0 +1,9 @@
# name lanes alias asic_port_name
Ethernet16 33,34,35,36 Ethernet1/5 Eth0-ASIC1
Ethernet20 29,30,31,32 Ethernet1/6 Eth1-ASIC1
Ethernet24 41,42,43,44 Ethernet1/7 Eth2-ASIC1
Ethernet28 37,38,39,40 Ethernet1/8 Eth3-ASIC1
Ethernet-BP16 13,14,15,16 Ethernet-BP16 Eth4-ASIC1
Ethernet-BP20 17,18,19,20 Ethernet-BP20 Eth5-ASIC1
Ethernet-BP24 21,22,23,24 Ethernet-BP24 Eth6-ASIC1
Ethernet-BP28 25,26,27,28 Ethernet-BP28 Eth7-ASIC1

View File

@ -0,0 +1,9 @@
# name lanes alias asic_port_name
Ethernet-BP256 61,62,63,64 Ethernet-BP256 Eth0-ASIC2
Ethernet-BP260 57,58,59,60 Ethernet-BP260 Eth1-ASIC2
Ethernet-BP264 53,54,55,56 Ethernet-BP264 Eth2-ASIC2
Ethernet-BP268 49,50,51,52 Ethernet-BP268 Eth3-ASIC2
Ethernet-BP272 45,46,47,48 Ethernet-BP272 Eth4-ASIC2
Ethernet-BP276 41,42,43,44 Ethernet-BP276 Eth5-ASIC2
Ethernet-BP280 37,38,39,40 Ethernet-BP280 Eth6-ASIC2
Ethernet-BP284 33,34,35,36 Ethernet-BP284 Eth7-ASIC2

View File

@ -0,0 +1,9 @@
# name lanes alias asic_port_name
Ethernet-BP384 29,30,31,32 Ethernet-BP384 Eth0-ASIC3
Ethernet-BP388 25,26,27,28 Ethernet-BP388 Eth1-ASIC3
Ethernet-BP392 21,22,23,24 Ethernet-BP392 Eth2-ASIC3
Ethernet-BP396 17,18,19,20 Ethernet-BP396 Eth3-ASIC3
Ethernet-BP400 13,14,15,16 Ethernet-BP400 Eth4-ASIC3
Ethernet-BP404 9,10,11,12 Ethernet-BP404 Eth5-ASIC3
Ethernet-BP408 5,6,7,8 Ethernet-BP408 Eth6-ASIC3
Ethernet-BP412 1,2,3,4 Ethernet-BP412 Eth7-ASIC3

View File

@ -0,0 +1,221 @@
import unittest
from unittest import TestCase
import subprocess
import os
import json
import yaml
SKU = 'multi-npu-01'
ASIC_SKU = 'multi-npu-asic'
NUM_ASIC = 4
HOSTNAME = 'multi_npu_platform_01'
class TestMultiNpuCfgGen(TestCase):
def setUp(self):
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.test_data_dir = os.path.join(self.test_dir, 'multi_npu_data')
self.script_file = os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.sample_graph = os.path.join(self.test_data_dir, 'sample-minigraph.xml')
self.port_config = []
for asic in range(NUM_ASIC):
self.port_config.append(os.path.join(self.test_data_dir, "sample_port_config-{}.ini".format(asic)))
def run_script(self, argument, check_stderr=False):
print '\n Running sonic-cfggen ' + argument
if check_stderr:
output = subprocess.check_output(self.script_file + ' ' + argument, stderr=subprocess.STDOUT, shell=True)
else:
output = subprocess.check_output(self.script_file + ' ' + argument, shell=True)
linecount = output.strip().count('\n')
if linecount <= 0:
print ' Output: ' + output.strip()
else:
print ' Output: ({0} lines, {1} bytes)'.format(linecount + 1, len(output))
return output
def run_diff(self, file1, file2):
return subprocess.check_output('diff -u {} {} || true'.format(file1, file2), shell=True)
def run_script_for_asic(self,argument,asic, port_config=None):
argument = "{} -n asic{} ".format(argument, asic)
if port_config:
argument += "-p {}".format(port_config)
output = self.run_script(argument)
return output
def test_dummy_run(self):
argument = ''
output = self.run_script(argument)
self.assertEqual(output, '')
def test_hwsku(self):
argument = "-v \"DEVICE_METADATA[\'localhost\'][\'hwsku\']\" -m \"{}\"".format(self.sample_graph)
output = self.run_script(argument)
self.assertEqual(output.strip(), SKU)
for asic in range(NUM_ASIC):
output = self.run_script_for_asic(argument, asic)
self.assertEqual(output.strip(), SKU)
def test_print_data(self):
argument = "-m \"{}\" --print-data".format(self.sample_graph)
output = self.run_script(argument)
self.assertGreater(len(output.strip()) , 0)
for asic in range(NUM_ASIC):
output = self.run_script_for_asic(argument, asic)
self.assertGreater(len(output.strip()) , 0)
def test_additional_json_data(self):
argument = '-a \'{"key1":"value1"}\' -v key1'
output = self.run_script(argument)
self.assertEqual(output.strip(), 'value1')
for asic in range(NUM_ASIC):
output = self.run_script_for_asic(argument, asic)
self.assertEqual(output.strip(), 'value1')
def test_read_yaml(self):
argument = '-v yml_item -y ' + os.path.join(self.test_dir, 'test.yml')
output = yaml.load(self.run_script(argument))
self.assertListEqual(output, ['value1', 'value2'])
for asic in range(NUM_ASIC):
output = yaml.load(self.run_script_for_asic(argument, asic))
self.assertListEqual(output, ['value1', 'value2'])
def test_render_template(self):
argument = '-y ' + os.path.join(self.test_dir, 'test.yml') + ' -t ' + os.path.join(self.test_dir, 'test.j2')
output = self.run_script(argument)
self.assertEqual(output.strip(), 'value1\nvalue2')
for asic in range(NUM_ASIC):
output = self.run_script_for_asic(argument, asic)
self.assertEqual(output.strip(), 'value1\nvalue2')
def test_metadata_tacacs(self):
argument = '-m "' + self.sample_graph + '" --var-json "TACPLUS_SERVER"'
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, {'123.46.98.21': {'priority': '1', 'tcp_port': '49'}})
#TACPLUS_SERVER not present in the asic configuration.
for asic in range(NUM_ASIC):
output = json.loads(self.run_script_for_asic(argument, asic, self.port_config[asic]))
self.assertDictEqual(output, {})
def test_metadata_ntp(self):
argument = '-m "' + self.sample_graph + '" --var-json "NTP_SERVER"'
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, {'17.39.1.130': {}, '17.39.1.129': {}})
#NTP data is present only in the host config
for asic in range(NUM_ASIC):
output = json.loads(self.run_script_for_asic(argument, asic, self.port_config[asic]))
print "Log:asic{} sku {}".format(asic,output)
self.assertDictEqual(output, {})
def test_mgmt_port(self):
argument = '-m "' + self.sample_graph + '" --var-json "MGMT_PORT"'
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, {'eth0': {'alias': 'eth0', 'admin_status': 'up'}})
for asic in range(NUM_ASIC):
output = json.loads(self.run_script_for_asic(argument, asic, self.port_config[asic]))
self.assertDictEqual(output, {})
def test_frontend_asic_portchannels(self):
argument = "-m {} -p {} -n asic0 --var-json \"PORTCHANNEL\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'PortChannel0002': {'admin_status': 'up', 'min_links': '2', 'members': ['Ethernet0', 'Ethernet4'], 'mtu': '9100'},
'PortChannel4001': {'admin_status': 'up', 'min_links': '2', 'members': ['Ethernet-BP0', 'Ethernet-BP4'], 'mtu': '9100'},
'PortChannel4002': {'admin_status': 'up', 'min_links': '2', 'members': ['Ethernet-BP8', 'Ethernet-BP12'], 'mtu': '9100'}})
def test_backend_asic_portchannels(self):
argument = "-m {} -p {} -n asic3 --var-json \"PORTCHANNEL\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'PortChannel4013': {'admin_status': 'up', 'min_links': '2', 'members': ['Ethernet-BP384', 'Ethernet-BP388'], 'mtu': '9100'},
'PortChannel4014': {'admin_status': 'up', 'min_links': '2', 'members': ['Ethernet-BP392', 'Ethernet-BP396'], 'mtu': '9100'}})
def test_frontend_asic_portchannel_mem(self):
argument = "-m {} -p {} -n asic0 --var-json \"PORTCHANNEL_MEMBER\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertListEqual(output.keys(), \
['PortChannel4002|Ethernet-BP8', 'PortChannel0002|Ethernet0', 'PortChannel0002|Ethernet4', 'PortChannel4002|Ethernet-BP12', 'PortChannel4001|Ethernet-BP0', 'PortChannel4001|Ethernet-BP4'])
def test_backend_asic_portchannels_mem(self):
argument = "-m {} -p {} -n asic3 --var-json \"PORTCHANNEL_MEMBER\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertListEqual(output.keys(), \
['PortChannel4013|Ethernet-BP384', 'PortChannel4014|Ethernet-BP392', 'PortChannel4014|Ethernet-BP396', 'PortChannel4013|Ethernet-BP388'])
def test_frontend_asic_portchannel_intf(self):
argument = "-m {} -p {} -n asic0 --var-json \"PORTCHANNEL_INTERFACE\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertListEqual(output.keys(), \
['PortChannel4001|10.1.0.1/31', 'PortChannel0002|FC00::1/126', 'PortChannel4002|10.1.0.3/31', 'PortChannel0002', 'PortChannel0002|10.0.0.0/31', 'PortChannel4001', 'PortChannel4002'])
def test_backend_asic_portchannel_intf(self):
argument = "-m {} -p {} -n asic3 --var-json \"PORTCHANNEL_INTERFACE\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertListEqual(output.keys(), \
['PortChannel4013', 'PortChannel4013|10.1.0.2/31', 'PortChannel4014', 'PortChannel4014|10.1.0.6/31'])
def test_frontend_asic_device_neigh(self):
argument = "-m {} -p {} -n asic0 --var-json \"DEVICE_NEIGHBOR\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'Ethernet0': {'name': '01T2', 'port': 'Ethernet1'},
'Ethernet4': {'name': '01T2', 'port': 'Ethernet2'},
'Ethernet-BP4': {'name': 'ASIC2', 'port': 'Eth1-ASIC2'},
'Ethernet-BP12': {'name': 'ASIC3', 'port': 'Eth1-ASIC3'},
'Ethernet-BP0': {'name': 'ASIC2', 'port': 'Eth0-ASIC2'},
'Ethernet-BP8': {'name': 'ASIC3', 'port': 'Eth0-ASIC3'}})
def test_frontend_asic_device_neigh_metadata(self):
argument = "-m {} -p {} -n asic0 --var-json \"DEVICE_NEIGHBOR_METADATA\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'01T2': {'lo_addr': None, 'mgmt_addr': '89.139.132.40', 'hwsku': 'VM', 'type': 'SpineRouter'},
'ASIC3': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'},
'ASIC2': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}})
def test_backend_asic_device_neigh(self):
argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'Ethernet-BP396': {'name': 'ASIC1', 'port': 'Eth7-ASIC1'},
'Ethernet-BP384': {'name': 'ASIC0', 'port': 'Eth6-ASIC0'},
'Ethernet-BP392': {'name': 'ASIC1', 'port': 'Eth6-ASIC1'},
'Ethernet-BP388': {'name': 'ASIC0', 'port': 'Eth7-ASIC0'}})
def test_backend_device_neigh_metadata(self):
argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR_METADATA\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'ASIC1': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'},
'ASIC0': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}})
def test_frontend_bgp_neighbor(self):
argument = "-m {} -p {} -n asic0 --var-json \"BGP_NEIGHBOR\"".format(self.sample_graph, self.port_config[0])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'10.0.0.1': {'rrclient': 0, 'name': '01T2', 'local_addr': '10.0.0.0', 'nhopself': 0, 'holdtime': '10', 'asn': '65200', 'keepalive': '3'},
'10.1.0.0': {'rrclient': 0, 'name': 'ASIC2', 'local_addr': '10.1.0.1', 'nhopself': 0, 'holdtime': '0', 'asn': '65100', 'keepalive': '0'},
'fc00::2': {'rrclient': 0, 'name': '01T2', 'local_addr': 'fc00::1', 'nhopself': 0, 'holdtime': '10', 'asn': '65200', 'keepalive': '3'},
'10.1.0.2': {'rrclient': 0, 'name': 'ASIC3', 'local_addr': '10.1.0.3', 'nhopself': 0, 'holdtime': '0', 'asn': '65100', 'keepalive': '0'}})
def test_backend_asic_bgp_neighbor(self):
argument = "-m {} -p {} -n asic3 --var-json \"BGP_NEIGHBOR\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \
{'10.1.0.7': {'rrclient': 0, 'name': 'ASIC1', 'local_addr': '10.1.0.6', 'nhopself': 0, 'holdtime': '0', 'asn': '65100', 'keepalive': '0'},
'10.1.0.3': {'rrclient': 0, 'name': 'ASIC0', 'local_addr': '10.1.0.2', 'nhopself': 0, 'holdtime': '0', 'asn': '65100', 'keepalive': '0'}})
def test_device_asic_metadata(self):
argument = "-m {} --var-json DEVICE_METADATA".format(self.sample_graph)
for asic in range(NUM_ASIC):
output = json.loads(self.run_script_for_asic(argument, asic,self.port_config[asic]))
asic_name = "asic{}".format(asic)
self.assertEqual(output['localhost']['hostname'], asic_name)
self.assertEqual(output['localhost']['type'], 'Asic')
if asic == 0 or asic == 1:
self.assertEqual(output['localhost']['sub_role'], 'FrontEnd')
else:
self.assertEqual(output['localhost']['sub_role'], 'BackEnd')