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

Parser changes to support parsing of multi-asic device minigraph #4222

Merged
merged 27 commits into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3ce843a
[minigraph.py]: Changes added in minigraph and port_config parser
SuvarnaMeenakshi Mar 4, 2020
ff6aa99
[minigraph.py/portconfig.py]: Minor fixes
SuvarnaMeenakshi Mar 4, 2020
b784c81
[minigraph.py]: Updated to make sure that interfaces names are
SuvarnaMeenakshi Mar 13, 2020
9b4039e
Changes to sonic-cfggen for multi-NPU platforms
arlakshm Mar 24, 2020
d4fe8e0
[sonic-cfggen]: Fix spacing.
SuvarnaMeenakshi Apr 7, 2020
53c6693
Minor fixes and UT tests
arlakshm Apr 11, 2020
a9a06a8
Addressing Review comments
arlakshm Apr 16, 2020
9c9f8c2
Merge remote-tracking branch 'remotes/sumeenak/master' into minigraph…
SuvarnaMeenakshi Apr 16, 2020
5b6892e
addressing review comments
arlakshm Apr 17, 2020
78a2bf7
addressing review comments
arlakshm Apr 19, 2020
ce84879
Change from id to asic_id in the device_metadata
arlakshm Apr 23, 2020
882c251
Fix as per review comments.
SuvarnaMeenakshi Apr 25, 2020
4ae22be
Minor fix as per review comment.
SuvarnaMeenakshi Apr 25, 2020
30024ba
Addressing review comments
arlakshm Apr 27, 2020
9744b85
Addressing review comments
arlakshm Apr 27, 2020
4f4be8c
Addressing review comments
arlakshm Apr 27, 2020
0895629
Addressing review comments
arlakshm Apr 29, 2020
0b90dae
Merge remote-tracking branch 'remotes/sumeenak/master' into minigraph…
SuvarnaMeenakshi Apr 29, 2020
15228c1
Addressing review comments
arlakshm May 1, 2020
0432f92
[minigraph.py]: Updated as per review comments.
SuvarnaMeenakshi May 1, 2020
ebf2e8b
[minigraph.py]: Split parse_asic_png function to make it
SuvarnaMeenakshi May 2, 2020
520c43b
[minigraph.py]: Correction in parse_asic_png function
SuvarnaMeenakshi May 2, 2020
110d1b8
Merge branch 'multiasic_minigraph' of github.com:SuvarnaMeenakshi/son…
SuvarnaMeenakshi May 2, 2020
247370b
Minor fixes.
SuvarnaMeenakshi May 2, 2020
cd3a678
Minor fixes as per review comments.
SuvarnaMeenakshi May 2, 2020
06983e4
Addressing review comments
arlakshm May 3, 2020
17a0d04
Fixes from testing
arlakshm May 4, 2020
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
214 changes: 181 additions & 33 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)


27 changes: 22 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)
asic_role = parse_asic_sub_role(args.minigraph if args.minigraph else '/etc/sonic/minigraph.xml', asic_name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do you need specify /etc/sonic/minigraph.xml, it is in line 196.

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 needed when the sonic-cfggen command is executed without -m option.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we show error in this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

There are a few platform related methods which execute 'sonic-cfggen -H' to get the platform data and other information from device_metadata.
To support these cases for multi-npu platforms, the miigraph from the default location will be used if no minigraph is provided.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think In this case you need to use option --minigraph= and provide the required minigraph

Copy link
Contributor

@arlakshm arlakshm May 3, 2020

Choose a reason for hiding this comment

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

Changed to use the minigraph only when --minigraph option is provided.


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,20 @@ def main():
configdb.connect()
deep_update(data, FormatConverter.db_to_output(configdb.get_config()))


if args.platform_info:
if asic_name is not None and asic_role.lower() == "backend":
mac = get_system_mac(asic_name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

namespace=asic_name

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed in latest commit

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
55 changes: 50 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,7 @@
import yaml
import subprocess
import re

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

def get_machine_info():
if not os.path.isfile('/host/machine.conf'):
return None
Expand All @@ -27,7 +30,45 @@ 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:
lguohan marked this conversation as resolved.
Show resolved Hide resolved
for line in asic_conf_file:
pavel-shirshov marked this conversation as resolved.
Show resolved Hide resolved
tokens = line.split('=')
Copy link
Collaborator

Choose a reason for hiding this comment

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

mixed tab and spaces

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed in latest commit

if len(tokens) < 2:
continue
if tokens[0].lower() == 'num_asic':
Copy link
Collaborator

Choose a reason for hiding this comment

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

mixed tab and spaces, suggest to use retab to correct all of them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed in latest commit

num_npus = tokens[1].strip()
return num_npus

def get_namespaces():
Copy link
Collaborator

Choose a reason for hiding this comment

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

i do not see this function used anywhere else in you code, in the multi npu system, why not use asic.conf to derive the namepsace you have on the system? why use ip netns list to query?

Copy link
Contributor

Choose a reason for hiding this comment

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

The plan is use this function by CLI commands.
Today the namespace are names as "asicX" , it could change in the future so didn't want to generate the name from asic.conf here.

"""
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 = []
try:
proc = subprocess.Popen('ip netns list | cut -d"(" -f1',
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggest to use files to look files under /run/netns

Copy link
Contributor

Choose a reason for hiding this comment

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

Changed in latest commit

stdout=subprocess.PIPE,
shell=True,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
ns_list = [n for n in stdout.split()]
except OSError,e:
raise OSError("Unable to get namespace list")
return natsorted(ns_list)
Copy link
Collaborator

Choose a reason for hiding this comment

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

no need to sort here. you do not know what order the user wants

Copy link
Contributor

Choose a reason for hiding this comment

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

This function is used mainly by CLI, in that case it is better if namespace list is sorted


def get_platform_info(machine_info):
if machine_info != None:
Expand All @@ -51,7 +92,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 +114,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)
Copy link
Collaborator

@lguohan lguohan Apr 23, 2020

Choose a reason for hiding this comment

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

what is the container eth0 mac address? how can make sure it is unique? is it randomly generated? i think it is hard to guarantee the mac is unique across different system in the dc and we could end up with duplicate macs. for frontend chip, you can still use the eth0 mac, only for backend chip you can use for docker generated mac.

Copy link
Contributor

Choose a reason for hiding this comment

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

The container eth0 mac is a dummy mac.
In the current design all the frontend asics will use the eth0 mac whereas the backend asics will use the container's eth0 mac.

Corresponding change in sonic-cfggen

if asic_name is not None and asic_role.lower() == "backend":
            mac = get_system_mac(asic_name)
        else:
            mac = get_system_mac()


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