Skip to content

Commit

Permalink
[config engine] Parser changes to support parsing of multi-asic devic…
Browse files Browse the repository at this point in the history
…e minigraph (sonic-net#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>
  • Loading branch information
SuvarnaMeenakshi authored and abdosi committed May 5, 2020
1 parent cb22310 commit 3bf2bf9
Show file tree
Hide file tree
Showing 10 changed files with 1,753 additions and 47 deletions.
223 changes: 191 additions & 32 deletions src/sonic-config-engine/minigraph.py

Large diffs are not rendered by default.

22 changes: 17 additions & 5 deletions src/sonic-config-engine/portconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
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'))
Expand All @@ -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:
Expand All @@ -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)


35 changes: 30 additions & 5 deletions src/sonic-config-engine/sonic-cfggen
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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=[])
Expand Down Expand Up @@ -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)
Expand All @@ -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))
Expand All @@ -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:
Expand Down
49 changes: 44 additions & 5 deletions src/sonic-config-engine/sonic_device_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import yaml
import subprocess
import re

from natsort import natsorted
import glob
DOCUMENTATION = '''
---
module: sonic_device_util
Expand All @@ -17,6 +18,9 @@
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
Expand All @@ -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:
Expand All @@ -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'):
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 3bf2bf9

Please sign in to comment.