diff --git a/config/main.py b/config/main.py index 38863d9149..9079fdbb12 100755 --- a/config/main.py +++ b/config/main.py @@ -13,7 +13,8 @@ import time from minigraph import parse_device_desc_xml -from sonic_py_common import device_info, logger +from sonic_py_common import device_info, multi_asic, logger +from sonic_py_common.interface import get_interface_table_name, get_port_table_name from swsssdk import ConfigDBConnector, SonicV2Connector, SonicDBConfig import aaa @@ -99,7 +100,7 @@ def execute_systemctl_per_asic_instance(inst, event, service, action): # Execute action on list of systemd services def execute_systemctl(list_of_services, action): - num_asic = device_info.get_num_npus() + num_asic = multi_asic.get_num_asics() generated_services_list, generated_multi_instance_services = _get_sonic_generated_services(num_asic) if ((generated_services_list == []) and (generated_multi_instance_services == [])): @@ -117,7 +118,7 @@ def execute_systemctl(list_of_services, action): if (service + '.service' in generated_multi_instance_services): # With Multi NPU, Start a thread per instance to do the "action" on multi instance services. - if device_info.is_multi_npu(): + if multi_asic.is_multi_asic(): threads = [] # Use this event object to co-ordinate if any threads raised exception e = threading.Event() @@ -151,24 +152,9 @@ def run_command(command, display_cmd=False, ignore_error=False): if proc.returncode != 0 and not ignore_error: sys.exit(proc.returncode) -# Validate whether a given namespace name is valid in the device. -def validate_namespace(namespace): - if not device_info.is_multi_npu(): - return True - - namespaces = device_info.get_all_namespaces() - if namespace in namespaces['front_ns'] + namespaces['back_ns']: - return True - else: - return False - -def interface_alias_to_name(interface_alias): +def interface_alias_to_name(config_db, interface_alias): """Return default interface name if alias name is given as argument """ - config_db = ConfigDBConnector() - config_db.connect() - port_dict = config_db.get_table('PORT') - vlan_id = "" sub_intf_sep_idx = -1 if interface_alias is not None: @@ -178,6 +164,17 @@ def interface_alias_to_name(interface_alias): # interface_alias holds the parent port name so the subsequent logic still applies interface_alias = interface_alias[:sub_intf_sep_idx] + # If the input parameter config_db is None, derive it from interface. + # In single ASIC platform, get_port_namespace() returns DEFAULT_NAMESPACE. + if config_db is None: + namespace = get_port_namespace(interface_alias) + if namespace is None: + return None + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + + config_db.connect() + port_dict = config_db.get_table('PORT') + if interface_alias is not None: if not port_dict: click.echo("port_dict is None!") @@ -190,18 +187,24 @@ def interface_alias_to_name(interface_alias): # portchannel is passed in as argument, which does not have an alias return interface_alias if sub_intf_sep_idx == -1 else interface_alias + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - -def interface_name_is_valid(interface_name): +def interface_name_is_valid(config_db, interface_name): """Check if the interface name is valid """ - config_db = ConfigDBConnector() + # If the input parameter config_db is None, derive it from interface. + # In single ASIC platform, get_port_namespace() returns DEFAULT_NAMESPACE. + if config_db is None: + namespace = get_port_namespace(interface_name) + if namespace is None: + return False + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() port_dict = config_db.get_table('PORT') port_channel_dict = config_db.get_table('PORTCHANNEL') sub_port_intf_dict = config_db.get_table('VLAN_SUB_INTERFACE') if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is not None: if not port_dict: @@ -220,10 +223,17 @@ def interface_name_is_valid(interface_name): return True return False -def interface_name_to_alias(interface_name): +def interface_name_to_alias(config_db, interface_name): """Return alias interface name if default name is given as argument """ - config_db = ConfigDBConnector() + # If the input parameter config_db is None, derive it from interface. + # In single ASIC platform, get_port_namespace() returns DEFAULT_NAMESPACE. + if config_db is None: + namespace = get_port_namespace(interface_name) + if namespace is None: + return None + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() port_dict = config_db.get_table('PORT') @@ -237,24 +247,6 @@ def interface_name_to_alias(interface_name): return None -def get_interface_table_name(interface_name): - """Get table name by interface_name prefix - """ - if interface_name.startswith("Ethernet"): - if VLAN_SUB_INTERFACE_SEPARATOR in interface_name: - return "VLAN_SUB_INTERFACE" - return "INTERFACE" - elif interface_name.startswith("PortChannel"): - if VLAN_SUB_INTERFACE_SEPARATOR in interface_name: - return "VLAN_SUB_INTERFACE" - return "PORTCHANNEL_INTERFACE" - elif interface_name.startswith("Vlan"): - return "VLAN_INTERFACE" - elif interface_name.startswith("Loopback"): - return "LOOPBACK_INTERFACE" - else: - return "" - def interface_ipaddr_dependent_on_interface(config_db, interface_name): """Get table keys including ipaddress """ @@ -279,6 +271,39 @@ def is_interface_bind_to_vrf(config_db, interface_name): return True return False +# Return the namespace where an interface belongs +# The port name input could be in default mode or in alias mode. +def get_port_namespace(port): + # If it is a non multi-asic platform, or if the interface is management interface + # return DEFAULT_NAMESPACE + if not multi_asic.is_multi_asic() or port == 'eth0': + return DEFAULT_NAMESPACE + + # Get the table to check for interface presence + table_name = get_port_table_name(port) + if table_name == "": + return None + + ns_list = multi_asic.get_all_namespaces() + namespaces = ns_list['front_ns'] + ns_list['back_ns'] + for namespace in namespaces: + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() + + # If the interface naming mode is alias, search the tables for alias_name. + if get_interface_naming_mode() == "alias": + port_dict = config_db.get_table(table_name) + if port_dict: + for port_name in port_dict.keys(): + if port == port_dict[port_name]['alias']: + return namespace + else: + entry = config_db.get_entry(table_name, port) + if entry: + return namespace + + return None + def del_interface_bind_to_vrf(config_db, vrf_name): """del interface bind to vrf """ @@ -299,8 +324,18 @@ def set_interface_naming_mode(mode): user = os.getenv('SUDO_USER') bashrc_ifacemode_line = "export SONIC_CLI_IFACE_MODE={}".format(mode) + # In case of multi-asic, we can check for the alias mode support in any of + # the namespaces as this setting of alias mode should be identical everywhere. + # Here by default we set the namespaces to be a list just having '' which + # represents the linux host. In case of multi-asic, we take the first namespace + # created for the front facing ASIC. + + namespaces = [DEFAULT_NAMESPACE] + if multi_asic.is_multi_asic(): + namespaces = multi_asic.get_all_namespaces()['front_ns'] + # Ensure all interfaces have an 'alias' key in PORT dict - config_db = ConfigDBConnector() + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespaces[0]) config_db.connect() port_dict = config_db.get_table('PORT') @@ -652,11 +687,11 @@ def save(filename): """Export current config DB to a file on disk.\n : Names of configuration file(s) to save, separated by comma with no spaces in between """ - num_asic = device_info.get_num_npus() + num_asic = multi_asic.get_num_asics() cfg_files = [] num_cfg_file = 1 - if device_info.is_multi_npu(): + if multi_asic.is_multi_asic(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -667,12 +702,11 @@ def save(filename): click.echo("Input {} config file(s) separated by comma for multiple files ".format(num_cfg_file)) return - """In case of multi-asic mode we have additional config_db{NS}.json files for - various namespaces created per ASIC. {NS} is the namespace index. - """ + # In case of multi-asic mode we have additional config_db{NS}.json files for + # various namespaces created per ASIC. {NS} is the namespace index. for inst in range(-1, num_cfg_file-1): #inst = -1, refers to the linux host where there is no namespace. - if inst is -1: + if inst == -1: namespace = None else: namespace = "{}{}".format(NAMESPACE_PREFIX, inst) @@ -708,11 +742,11 @@ def load(filename, yes): if not yes: click.confirm(message, abort=True) - num_asic = device_info.get_num_npus() + num_asic = multi_asic.get_num_asics() cfg_files = [] num_cfg_file = 1 - if device_info.is_multi_npu(): + if multi_asic.is_multi_asic(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -723,12 +757,11 @@ def load(filename, yes): click.echo("Input {} config file(s) separated by comma for multiple files ".format(num_cfg_file)) return - """In case of multi-asic mode we have additional config_db{NS}.json files for - various namespaces created per ASIC. {NS} is the namespace index. - """ + # In case of multi-asic mode we have additional config_db{NS}.json files for + # various namespaces created per ASIC. {NS} is the namespace index. for inst in range(-1, num_cfg_file-1): #inst = -1, refers to the linux host where there is no namespace. - if inst is -1: + if inst == -1: namespace = None else: namespace = "{}{}".format(NAMESPACE_PREFIX, inst) @@ -774,11 +807,11 @@ def reload(filename, yes, load_sysinfo, no_service_restart): log.log_info("'reload' executing...") - num_asic = device_info.get_num_npus() + num_asic = multi_asic.get_num_asics() cfg_files = [] num_cfg_file = 1 - if device_info.is_multi_npu(): + if multi_asic.is_multi_asic(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -804,14 +837,13 @@ def reload(filename, yes, load_sysinfo, no_service_restart): log.log_info("'reload' stopping services...") _stop_services() - """ In Single AISC platforms we have single DB service. In multi-ASIC platforms we have a global DB - service running in the host + DB services running in each ASIC namespace created per ASIC. - In the below logic, we get all namespaces in this platform and add an empty namespace '' - denoting the current namespace which we are in ( the linux host ) - """ + # In Single AISC platforms we have single DB service. In multi-ASIC platforms we have a global DB + # service running in the host + DB services running in each ASIC namespace created per ASIC. + # In the below logic, we get all namespaces in this platform and add an empty namespace '' + # denoting the current namespace which we are in ( the linux host ) for inst in range(-1, num_cfg_file-1): # Get the namespace name, for linux host it is None - if inst is -1: + if inst == -1: namespace = None else: namespace = "{}{}".format(NAMESPACE_PREFIX, inst) @@ -825,7 +857,7 @@ def reload(filename, yes, load_sysinfo, no_service_restart): else: file = "/etc/sonic/config_db{}.json".format(inst) - #Check the file exists before proceeding. + # Check the file exists before proceeding. if not os.path.isfile(file): click.echo("The config_db file {} doesn't exist".format(file)) continue @@ -930,9 +962,9 @@ def load_minigraph(no_service_restart): # for mulit Asic platform the empty string to generate the config # for host namespace_list = [DEFAULT_NAMESPACE] - num_npus = device_info.get_num_npus() + num_npus = multi_asic.get_num_asics() if num_npus > 1: - namespace_list += device_info.get_namespaces() + namespace_list += multi_asic.get_namespaces_from_linux() for namespace in namespace_list: if namespace is DEFAULT_NAMESPACE: @@ -1007,11 +1039,18 @@ def hostname(new_hostname): # 'portchannel' group ('config portchannel ...') # @config.group(cls=AbbreviationGroup) +# TODO add "hidden=True if this is a single ASIC platform, once we have click 7.0 in all branches. +@click.option('-n', '--namespace', help='Namespace name', + required=True if multi_asic.is_multi_asic() else False, type=click.Choice(multi_asic.get_namespace_list())) @click.pass_context -def portchannel(ctx): - config_db = ConfigDBConnector() +def portchannel(ctx, namespace): + # Set namespace to default_namespace if it is None. + if namespace is None: + namespace = DEFAULT_NAMESPACE + + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=str(namespace)) config_db.connect() - ctx.obj = {'db': config_db} + ctx.obj = {'db': config_db, 'namespace': str(namespace)} pass @portchannel.command('add') @@ -1050,6 +1089,11 @@ def portchannel_member(ctx): def add_portchannel_member(ctx, portchannel_name, port_name): """Add member to port channel""" db = ctx.obj['db'] + + # Check if the member interface given by user is valid in the namespace. + if interface_name_is_valid(db, port_name) is False: + ctx.fail("Interface name is invalid. Please enter a valid interface name!!") + db.set_entry('PORTCHANNEL_MEMBER', (portchannel_name, port_name), {'NULL': 'NULL'}) @@ -1060,6 +1104,11 @@ def add_portchannel_member(ctx, portchannel_name, port_name): def del_portchannel_member(ctx, portchannel_name, port_name): """Remove member from portchannel""" db = ctx.obj['db'] + + # Check if the member interface given by user is valid in the namespace. + if interface_name_is_valid(db, port_name) is False: + ctx.fail("Interface name is invalid. Please enter a valid interface name!!") + db.set_entry('PORTCHANNEL_MEMBER', (portchannel_name, port_name), None) db.set_entry('PORTCHANNEL_MEMBER', portchannel_name + '|' + port_name, None) @@ -1103,7 +1152,7 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): """ For multi-npu platforms we need to program all front asic namespaces """ - namespaces = device_info.get_all_namespaces() + namespaces = multi_asic.get_all_namespaces() if not namespaces['front_ns']: config_db = ConfigDBConnector() config_db.connect() @@ -1125,7 +1174,7 @@ def remove(session_name): """ For multi-npu platforms we need to program all front asic namespaces """ - namespaces = device_info.get_all_namespaces() + namespaces = multi_asic.get_all_namespaces() if not namespaces['front_ns']: config_db = ConfigDBConnector() config_db.connect() @@ -1451,7 +1500,7 @@ def add_vlan_member(ctx, vid, interface_name, untagged): interface_table = db.get_table('INTERFACE') if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -1460,7 +1509,7 @@ def add_vlan_member(ctx, vid, interface_name, untagged): members = vlan.get('members', []) if interface_name in members: if get_interface_naming_mode() == "alias": - interface_name = interface_name_to_alias(interface_name) + interface_name = interface_name_to_alias(db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") ctx.fail("{} is already a member of {}".format(interface_name, @@ -1488,7 +1537,7 @@ def del_vlan_member(ctx, vid, interface_name): vlan = db.get_entry('VLAN', vlan_name) if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -1497,7 +1546,7 @@ def del_vlan_member(ctx, vid, interface_name): members = vlan.get('members', []) if interface_name not in members: if get_interface_naming_mode() == "alias": - interface_name = interface_name_to_alias(interface_name) + interface_name = interface_name_to_alias(db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") ctx.fail("{} is not a member of {}".format(interface_name, vlan_name)) @@ -1740,8 +1789,8 @@ def all(verbose): namespaces = [DEFAULT_NAMESPACE] ignore_local_hosts = False - if device_info.is_multi_npu(): - ns_list = device_info.get_all_namespaces() + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_all_namespaces() namespaces = ns_list['front_ns'] ignore_local_hosts = True @@ -1766,8 +1815,8 @@ def neighbor(ipaddr_or_hostname, verbose): namespaces = [DEFAULT_NAMESPACE] found_neighbor = False - if device_info.is_multi_npu(): - ns_list = device_info.get_all_namespaces() + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_all_namespaces() namespaces = ns_list['front_ns'] + ns_list['back_ns'] # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the @@ -1797,8 +1846,8 @@ def all(verbose): namespaces = [DEFAULT_NAMESPACE] ignore_local_hosts = False - if device_info.is_multi_npu(): - ns_list = device_info.get_all_namespaces() + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_all_namespaces() namespaces = ns_list['front_ns'] ignore_local_hosts = True @@ -1823,8 +1872,8 @@ def neighbor(ipaddr_or_hostname, verbose): namespaces = [DEFAULT_NAMESPACE] found_neighbor = False - if device_info.is_multi_npu(): - ns_list = device_info.get_all_namespaces() + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_all_namespaces() namespaces = ns_list['front_ns'] + ns_list['back_ns'] # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the @@ -1856,8 +1905,8 @@ def remove_neighbor(neighbor_ip_or_hostname): namespaces = [DEFAULT_NAMESPACE] removed_neighbor = False - if device_info.is_multi_npu(): - ns_list = device_info.get_all_namespaces() + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_all_namespaces() namespaces = ns_list['front_ns'] + ns_list['back_ns'] # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the @@ -1876,14 +1925,18 @@ def remove_neighbor(neighbor_ip_or_hostname): # @config.group(cls=AbbreviationGroup) +# TODO add "hidden=True if this is a single ASIC platform, once we have click 7.0 in all branches. +@click.option('-n', '--namespace', help='Namespace name', + required=True if multi_asic.is_multi_asic() else False, type=click.Choice(multi_asic.get_namespace_list())) @click.pass_context -def interface(ctx): +def interface(ctx, namespace): """Interface-related configuration tasks""" - config_db = ConfigDBConnector() + # Set namespace to default_namespace if it is None. + if namespace is None: + namespace = DEFAULT_NAMESPACE + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=str(namespace)) config_db.connect() - ctx.obj = {} - ctx.obj['config_db'] = config_db - + ctx.obj = {'config_db': config_db, 'namespace': str(namespace)} # # 'startup' subcommand # @@ -1893,14 +1946,16 @@ def interface(ctx): @click.pass_context def startup(ctx, interface_name): """Start up interface""" + # Get the config_db connector config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - if interface_name_is_valid(interface_name) is False: - ctx.fail("Interface name is invalid. Please enter a valid interface name!!") + if interface_name_is_valid(config_db, interface_name) is False: + ctx.fail("Interface name is invalid. Please enter a valid interface name!!") if interface_name.startswith("Ethernet"): if VLAN_SUB_INTERFACE_SEPARATOR in interface_name: @@ -1921,13 +1976,15 @@ def startup(ctx, interface_name): @click.pass_context def shutdown(ctx, interface_name): """Shut down interface""" + # Get the config_db connector config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - if interface_name_is_valid(interface_name) is False: + if interface_name_is_valid(config_db, interface_name) is False: ctx.fail("Interface name is invalid. Please enter a valid interface name!!") if interface_name.startswith("Ethernet"): @@ -1952,12 +2009,19 @@ def shutdown(ctx, interface_name): @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def speed(ctx, interface_name, interface_speed, verbose): """Set interface speed""" + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - command = "portconfig -p {} -s {}".format(interface_name, interface_speed) + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -s {}".format(interface_name, interface_speed) + else: + command = "portconfig -p {} -s {} -n {}".format(interface_name, interface_speed, ctx.obj['namespace']) + if verbose: command += " -vv" run_command(command, display_cmd=verbose) @@ -1995,12 +2059,18 @@ def mgmt_ip_restart_services(): @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def mtu(ctx, interface_name, interface_mtu, verbose): """Set interface mtu""" + # Get the config_db connector + config_db = ctx.obj['config_db'] if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - command = "portconfig -p {} -m {}".format(interface_name, interface_mtu) + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -m {}".format(interface_name, interface_mtu) + else: + command = "portconfig -p {} -m {} -n {}".format(interface_name, interface_mtu, ctx.obj['namespace']) + if verbose: command += " -vv" run_command(command, display_cmd=verbose) @@ -2012,14 +2082,21 @@ def mtu(ctx, interface_name, interface_mtu, verbose): @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def fec(ctx, interface_name, interface_fec, verbose): """Set interface fec""" + # Get the config_db connector + config_db = ctx.obj['config_db'] + if interface_fec not in ["rs", "fc", "none"]: ctx.fail("'fec not in ['rs', 'fc', 'none']!") if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - command = "portconfig -p {} -f {}".format(interface_name, interface_fec) + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -f {}".format(interface_name, interface_fec) + else: + command = "portconfig -p {} -f {} -n {}".format(interface_name, interface_fec, ctx.obj['namespace']) + if verbose: command += " -vv" run_command(command, display_cmd=verbose) @@ -2045,9 +2122,11 @@ def ip(ctx): @click.pass_context def add(ctx, interface_name, ip_addr, gw): """Add an IP address towards the interface""" - config_db = ctx.obj["config_db"] + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2102,9 +2181,11 @@ def add(ctx, interface_name, ip_addr, gw): @click.pass_context def remove(ctx, interface_name, ip_addr): """Remove an IP address from the interface""" - config_db = ctx.obj["config_db"] + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2124,7 +2205,10 @@ def remove(ctx, interface_name, ip_addr): if len(interface_dependent) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False: config_db.set_entry(table_name, interface_name, None) - command = "ip neigh flush dev {} {}".format(interface_name, ip_addr) + if multi_asic.is_multi_asic(): + command = "sudo ip netns exec {} ip neigh flush dev {} {}".format(ctx.obj['namespace'], interface_name, ip_addr) + else: + command = "ip neigh flush dev {} {}".format(interface_name, ip_addr) run_command(command) except ValueError: ctx.fail("'ip_addr' is not valid.") @@ -2149,12 +2233,15 @@ def transceiver(ctx): @click.pass_context def lpmode(ctx, interface_name, state): """Enable/disable low-power mode for SFP transceiver module""" + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - if interface_name_is_valid(interface_name) is False: + if interface_name_is_valid(config_db, interface_name) is False: ctx.fail("Interface name is invalid. Please enter a valid interface name!!") cmd = "sudo sfputil lpmode {} {}".format("on" if state == "enable" else "off", interface_name) @@ -2169,12 +2256,15 @@ def lpmode(ctx, interface_name, state): @click.pass_context def reset(ctx, interface_name): """Reset SFP transceiver module""" + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - if interface_name_is_valid(interface_name) is False: + if interface_name_is_valid(config_db, interface_name) is False: ctx.fail("Interface name is invalid. Please enter a valid interface name!!") cmd = "sudo sfputil reset {}".format(interface_name) @@ -2200,9 +2290,11 @@ def vrf(ctx): @click.pass_context def bind(ctx, interface_name, vrf_name): """Bind the interface to VRF""" - config_db = ctx.obj["config_db"] + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2218,7 +2310,10 @@ def bind(ctx, interface_name, vrf_name): config_db.set_entry(table_name, interface_del, None) config_db.set_entry(table_name, interface_name, None) # When config_db del entry and then add entry with same key, the DEL will lost. - state_db = SonicV2Connector(host='127.0.0.1') + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + state_db = SonicV2Connector(use_unix_socket_path=True) + else: + state_db = SonicV2Connector(use_unix_socket_path=True, namespace=ctx.obj['namespace']) state_db.connect(state_db.STATE_DB, False) _hash = '{}{}'.format('INTERFACE_TABLE|', interface_name) while state_db.get(state_db.STATE_DB, _hash, "state") == "ok": @@ -2235,9 +2330,11 @@ def bind(ctx, interface_name, vrf_name): @click.pass_context def unbind(ctx, interface_name): """Unbind the interface to VRF""" - config_db = ctx.obj["config_db"] + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("interface is None!") @@ -2663,8 +2760,11 @@ def pfc(ctx): @click.pass_context def asymmetric(ctx, interface_name, status): """Set asymmetric PFC configuration.""" + # Get the config_db connector + config_db = ctx.obj['config_db'] + if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) + interface_name = interface_alias_to_name(config_db, interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -3000,11 +3100,11 @@ def interface(ctx): @click.argument('ifname', metavar='', required=True, type=str) @click.pass_context def enable(ctx, ifname): - if not interface_name_is_valid(ifname) and ifname != 'all': + config_db = ctx.obj['db'] + if not interface_name_is_valid(config_db, ifname) and ifname != 'all': click.echo("Invalid interface name") return - config_db = ctx.obj['db'] intf_dict = config_db.get_table('SFLOW_SESSION') if intf_dict and ifname in intf_dict.keys(): @@ -3020,11 +3120,11 @@ def enable(ctx, ifname): @click.argument('ifname', metavar='', required=True, type=str) @click.pass_context def disable(ctx, ifname): - if not interface_name_is_valid(ifname) and ifname != 'all': + config_db = ctx.obj['db'] + if not interface_name_is_valid(config_db, ifname) and ifname != 'all': click.echo("Invalid interface name") return - config_db = ctx.obj['db'] intf_dict = config_db.get_table('SFLOW_SESSION') if intf_dict and ifname in intf_dict.keys(): @@ -3042,14 +3142,14 @@ def disable(ctx, ifname): @click.argument('rate', metavar='', required=True, type=int) @click.pass_context def sample_rate(ctx, ifname, rate): - if not interface_name_is_valid(ifname) and ifname != 'all': + config_db = ctx.obj['db'] + if not interface_name_is_valid(config_db, ifname) and ifname != 'all': click.echo('Invalid interface name') return if not is_valid_sample_rate(rate): click.echo('Error: Sample rate must be between 256 and 8388608') return - config_db = ctx.obj['db'] sess_dict = config_db.get_table('SFLOW_SESSION') if sess_dict and ifname in sess_dict.keys(): diff --git a/scripts/portconfig b/scripts/portconfig index 07b4828a29..95e4b6006b 100755 --- a/scripts/portconfig +++ b/scripts/portconfig @@ -4,7 +4,7 @@ portconfig is the utility to show and change ECN configuration usage: portconfig [-h] [-v] [-s] [-f] [-m] [-p PROFILE] [-gmin GREEN_MIN] [-gmax GREEN_MAX] [-ymin YELLOW_MIN] [-ymax YELLOW_MAX] - [-rmin RED_MIN] [-rmax RED_MAX] [-vv] + [-rmin RED_MIN] [-rmax RED_MAX] [-vv] [-n namespace] optional arguments: -h --help show this help message and exit @@ -14,6 +14,7 @@ optional arguments: -s --speed port speed in Mbits -f --fec port fec mode -m --mtu port mtu in bytes + -n --namesapce Namespace name """ from __future__ import print_function @@ -32,12 +33,16 @@ class portconfig(object): """ Process aclstat """ - def __init__(self, verbose, port): + def __init__(self, verbose, port, namespace): self.verbose = verbose # Set up db connections - self.db = swsssdk.ConfigDBConnector() + if namespace is None: + self.db = swsssdk.ConfigDBConnector() + else: + self.db = swsssdk.ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) self.db.connect() + # check whether table for this port exists port_tables = self.db.get_table(PORT_TABLE_NAME) if not port_tables.has_key(port): @@ -74,10 +79,15 @@ def main(): parser.add_argument('-f', '--fec', type=str, help='port fec mode value in (none, rs, fc)', default=None) parser.add_argument('-m', '--mtu', type=int, help='port mtu value in bytes', default=None) parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False) + parser.add_argument('-n', '--namespace', metavar='namespace details', type = str, required = False, + help = 'The asic namespace whose DB instance we need to connect', default=None) args = parser.parse_args() + if args.namespace is not None: + swsssdk.SonicDBConfig.load_sonic_global_db_config(namespace=args.namespace) + try: - port = portconfig(args.verbose, args.port) + port = portconfig(args.verbose, args.port, args.namespace) if args.list: port.list_params(args.port) elif args.speed or args.fec or args.mtu: diff --git a/scripts/sfpshow b/scripts/sfpshow index 04b1ec2b46..a098f05551 100755 --- a/scripts/sfpshow +++ b/scripts/sfpshow @@ -12,9 +12,12 @@ import operator import os from natsort import natsorted -from swsssdk import SonicV2Connector, port_util from tabulate import tabulate +from utilities_common import multi_asic as multi_asic_util +from sonic_py_common.interface import front_panel_prefix, backplane_prefix +from sonic_py_common import multi_asic + # Mock the redis for unit test purposes # try: if os.environ["UTILITIES_UNIT_TESTING"] == "1": @@ -23,6 +26,9 @@ try: sys.path.insert(0, modules_path) sys.path.insert(0, test_path) import mock_tables.dbconnector + if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": + import mock_tables.mock_multi_asic + mock_tables.dbconnector.load_namespace_config() except KeyError: pass @@ -128,18 +134,27 @@ dom_value_unit_map = {'rx1power': 'dBm', 'rx2power': 'dBm', 'tx3power': 'dBm', 'tx4power': 'dBm', 'temperature': 'C', 'voltage': 'Volts'} +def display_invalid_intf_eeprom(intf_name): + output = intf_name + ': ' + 'SFP EEPROM Not detected' + '\n' + click.echo(output) +def display_invalid_intf_presence(intf_name): + header = ['Port', 'Presence'] + port_table = [] + port_table.append((intf_name, 'Not present')) + click.echo(tabulate(port_table, header)) class SFPShow(object): - def __init__(self): + def __init__(self, intf_name, namespace_option, dump_dom=False): super(SFPShow,self).__init__() - self.adb = SonicV2Connector(host="127.0.0.1") - self.adb.connect(self.adb.APPL_DB) - - self.sdb = SonicV2Connector(host="127.0.0.1") - self.sdb.connect(self.sdb.STATE_DB) - return + self.db = None + self.config_db = None + self.intf_name = intf_name + self.dump_dom = dump_dom + self.table = [] + self.output = '' + self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace_option) # Convert dict values to cli output string def format_dict_value_to_string(self, sorted_key_table, @@ -293,53 +308,61 @@ class SFPShow(object): return out_put - def display_eeprom(self, interfacename, dump_dom): + @multi_asic_util.run_on_multi_asic + def get_eeprom(self): out_put = '' - if interfacename is not None: - presence = self.sdb.exists(self.sdb.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interfacename)) + if self.intf_name is not None: + presence = self.db.exists(self.db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(self.intf_name)) if presence: - out_put = self.convert_interface_sfp_info_to_cli_output_string(self.sdb, interfacename, dump_dom) + out_put = self.convert_interface_sfp_info_to_cli_output_string(self.db, self.intf_name, self.dump_dom) else: - out_put = out_put + interfacename + ': ' + 'SFP EEPROM Not detected' + '\n' + out_put = out_put + self.intf_name + ': ' + 'SFP EEPROM Not detected' + '\n' else: - port_table_keys = self.adb.keys(self.adb.APPL_DB, "PORT_TABLE:*") + port_table_keys = self.db.keys(self.db.APPL_DB, "PORT_TABLE:*") sorted_table_keys = natsorted(port_table_keys) for i in sorted_table_keys: interface = re.split(':', i, maxsplit=1)[-1].strip() - if interface and interface.startswith('Ethernet'): - presence = self.sdb.exists(self.sdb.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interface)) + if interface and interface.startswith(front_panel_prefix()) and not interface.startswith(backplane_prefix()): + presence = self.db.exists(self.db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interface)) if presence: - out_put = out_put + self.convert_interface_sfp_info_to_cli_output_string(self.sdb, interface, dump_dom) + out_put = out_put + self.convert_interface_sfp_info_to_cli_output_string(self.db, interface, self.dump_dom) else: out_put = out_put + interface + ': ' + 'SFP EEPROM Not detected' + '\n' - out_put = out_put + '\n' + out_put = out_put + '\n' - print out_put + self.output += out_put - def display_presence(self, interfacename): + @multi_asic_util.run_on_multi_asic + def get_presence(self): port_table = [] - header = ['Port', 'Presence'] - if interfacename is not None: - presence = self.sdb.exists(self.sdb.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interfacename)) + if self.intf_name is not None: + presence = self.db.exists(self.db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(self.intf_name)) if presence: - port_table.append((interfacename, 'Present')) + port_table.append((self.intf_name, 'Present')) else: - port_table.append((interfacename, 'Not present')) + port_table.append((self.intf_name, 'Not present')) else: - port_table_keys = self.adb.keys(self.adb.APPL_DB, "PORT_TABLE:*") + port_table_keys = self.db.keys(self.db.APPL_DB, "PORT_TABLE:*") for i in port_table_keys: key = re.split(':', i, maxsplit=1)[-1].strip() - if key and key.startswith('Ethernet'): - presence = self.sdb.exists(self.sdb.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(key)) + if key and key.startswith(front_panel_prefix()) and not key.startswith(backplane_prefix()): + presence = self.db.exists(self.db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(key)) if presence: port_table.append((key,'Present')) else: port_table.append((key,'Not present')) - sorted_port_table = natsorted(port_table) + self.table += port_table + + def display_eeprom(self): + click.echo(self.output) + + def display_presence(self): + header = ['Port', 'Presence'] + sorted_port_table = natsorted(self.table) click.echo(tabulate(sorted_port_table, header)) # This is our main entrypoint - the main 'sfpshow' command @@ -352,16 +375,36 @@ def cli(): @cli.command() @click.option('-p', '--port', metavar='', help="Display SFP EEPROM data for port only") @click.option('-d', '--dom', 'dump_dom', is_flag=True, help="Also display Digital Optical Monitoring (DOM) data") -def eeprom(port, dump_dom): - sfp = SFPShow() - sfp.display_eeprom(port, dump_dom) +@click.option('-n', '--namespace', default=None, help="Display interfaces for specific namespace") +def eeprom(port, dump_dom, namespace): + if port and multi_asic.is_multi_asic() and namespace is None: + try: + ns = multi_asic.get_namespace_for_port(port) + namespace=ns + except Exception: + display_invalid_intf_eeprom(port) + sys.exit(1) + + sfp = SFPShow(port, namespace, dump_dom) + sfp.get_eeprom() + sfp.display_eeprom() # 'presence' subcommand @cli.command() @click.option('-p', '--port', metavar='', help="Display SFP presence for port only") -def presence(port): - sfp = SFPShow() - sfp.display_presence(port) +@click.option('-n', '--namespace', default=None, help="Display interfaces for specific namespace") +def presence(port, namespace): + if port and multi_asic.is_multi_asic() and namespace is None: + try: + ns = multi_asic.get_namespace_for_port(port) + namespace=ns + except Exception: + display_invalid_intf_presence(port) + sys.exit(1) + + sfp = SFPShow(port, namespace) + sfp.get_presence() + sfp.display_presence() if __name__ == "__main__": cli() diff --git a/sfputil/main.py b/sfputil/main.py index 89cd5fcfdf..b90d0ca59f 100644 --- a/sfputil/main.py +++ b/sfputil/main.py @@ -14,6 +14,7 @@ import syslog import types import traceback + from sonic_py_common import device_info, multi_asic from tabulate import tabulate except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -384,9 +385,17 @@ def cli(): # Load port info try: - port_config_file_path = get_path_to_port_config_file() - platform_sfputil.read_porttab_mappings(port_config_file_path) - except Exception, e: + if multi_asic.is_multi_asic(): + # For multi ASIC platforms we pass DIR of port_config_file_path and the number of asics + (platform_path, hwsku_path) = device_info.get_paths_to_platform_and_hwsku_dirs() + + # Load platform module from source + platform_sfputil.read_all_porttab_mappings(hwsku_path, multi_asic.get_num_asics()) + else: + # For single ASIC platforms we pass port_config_file_path and the asic_inst as 0 + port_config_file_path = device_info.get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path, 0) + except Exception as e: log_error("Error reading port info (%s)" % str(e), True) sys.exit(3) diff --git a/show/main.py b/show/main.py index 677f4cc0b4..eb14eac9db 100755 --- a/show/main.py +++ b/show/main.py @@ -735,8 +735,10 @@ def transceiver(): @transceiver.command() @click.argument('interfacename', required=False) @click.option('-d', '--dom', 'dump_dom', is_flag=True, help="Also display Digital Optical Monitoring (DOM) data") +@click.option('--namespace', '-n', 'namespace', default=None, show_default=True, + type=click.Choice(multi_asic_util.multi_asic_ns_choices()), help='Namespace name or all') @click.option('--verbose', is_flag=True, help="Enable verbose output") -def eeprom(interfacename, dump_dom, verbose): +def eeprom(interfacename, dump_dom, namespace, verbose): """Show interface transceiver EEPROM information""" cmd = "sfpshow eeprom" @@ -750,8 +752,10 @@ def eeprom(interfacename, dump_dom, verbose): cmd += " -p {}".format(interfacename) - run_command(cmd, display_cmd=verbose) + if namespace is not None: + cmd += " -n {}".format(namespace) + run_command(cmd, display_cmd=verbose) @transceiver.command() @click.argument('interfacename', required=False) @@ -771,8 +775,10 @@ def lpmode(interfacename, verbose): @transceiver.command() @click.argument('interfacename', required=False) +@click.option('--namespace', '-n', 'namespace', default=None, show_default=True, + type=click.Choice(multi_asic_util.multi_asic_ns_choices()), help='Namespace name or all') @click.option('--verbose', is_flag=True, help="Enable verbose output") -def presence(interfacename, verbose): +def presence(interfacename, namespace, verbose): """Show interface transceiver presence""" cmd = "sfpshow presence" @@ -783,6 +789,9 @@ def presence(interfacename, verbose): cmd += " -p {}".format(interfacename) + if namespace is not None: + cmd += " -n {}".format(namespace) + run_command(cmd, display_cmd=verbose) diff --git a/sonic-utilities-tests/mock_tables/asic0/state_db.json b/sonic-utilities-tests/mock_tables/asic0/state_db.json new file mode 100644 index 0000000000..07939bc007 --- /dev/null +++ b/sonic-utilities-tests/mock_tables/asic0/state_db.json @@ -0,0 +1,207 @@ +{ + "TRANSCEIVER_INFO|Ethernet0": { + "type": "QSFP28 or later", + "hardware_rev": "AC", + "serial": "MT1706FT02064", + "manufacturer": "Mellanox", + "model": "MFA1A00-C003", + "vendor_oui": "00-02-c9", + "vendor_date": "2017-01-13 ", + "connector": "No separable connector", + "encoding": "64B66B", + "ext_identifier": "Power Class 3(2.5W max), CDR present in Rx Tx", + "ext_rateselect_compliance": "QSFP+ Rate Select Version 1", + "cable_type": "Length Cable Assembly(m)", + "cable_length": "3", + "specification_compliance": "{'10/40G Ethernet Compliance Code': '40G Active Cable (XLPPI)'}", + "nominal_bit_rate": "255", + "application_advertisement": "N/A" + }, + "TRANSCEIVER_DOM_SENSOR|Ethernet0": { + "temperature": "30.9258", + "voltage": "3.2824", + "rx1power": "0.3802", + "rx2power": "-0.4871", + "rx3power": "-0.0860", + "rx4power": "0.3830", + "tx1bias": "6.7500", + "tx2bias": "6.7500", + "tx3bias": "6.7500", + "tx4bias": "6.7500", + "tx1power": "N/A", + "tx2power": "N/A", + "tx3power": "N/A", + "tx4power": "N/A", + "rxpowerhighalarm": "3.4001", + "rxpowerhighwarning": "2.4000", + "rxpowerlowalarm": "-13.5067", + "rxpowerlowwarning": "-9.5001", + "txbiashighalarm": "10.0000", + "txbiashighwarning": "9.5000", + "txbiaslowalarm": "0.5000", + "txbiaslowwarning": "1.0000", + "temphighalarm": "75.0000", + "temphighwarning": "70.0000", + "templowalarm": "-5.0000", + "templowwarning": "0.0000", + "vcchighalarm": "3.6300", + "vcchighwarning": "3.4650", + "vcclowalarm": "2.9700", + "vcclowwarning": "3.1349" + }, + "CHASSIS_INFO|chassis 1": { + "psu_num": "2" + }, + "PSU_INFO|PSU 1": { + "presence": "true", + "status": "true", + "led_status": "green" + }, + "PSU_INFO|PSU 2": { + "presence": "true", + "status": "true", + "led_status": "green" + }, + "SWITCH_CAPABILITY|switch": { + "MIRROR": "true", + "MIRRORV6": "true", + "ACL_ACTIONS|INGRESS": "PACKET_ACTION,REDIRECT_ACTION,MIRROR_INGRESS_ACTION", + "ACL_ACTIONS|EGRESS": "PACKET_ACTION,MIRROR_EGRESS_ACTION", + "ACL_ACTION|PACKET_ACTION": "FORWARD" + }, + "DEBUG_COUNTER_CAPABILITIES|PORT_INGRESS_DROPS": { + "reasons": "[IP_HEADER_ERROR,NO_L3_HEADER]", + "count": "4" + }, + "DEBUG_COUNTER_CAPABILITIES|SWITCH_EGRESS_DROPS": { + "reasons": "[ACL_ANY,L2_ANY,L3_ANY]", + "count": "2" + }, + "LAG_MEMBER_TABLE|PortChannel0001|Ethernet112": { + "runner.actor_lacpdu_info.state": "5", + "runner.state": "disabled", + "runner.partner_lacpdu_info.port": "0", + "runner.actor_lacpdu_info.port": "113", + "runner.selected": "false", + "runner.partner_lacpdu_info.state": "0", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "00:00:00:00:00:00", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "false", + "runner.aggregator.id": "0", + "link.up": "false", + "ifinfo.ifindex": "98" + }, + "LAG_MEMBER_TABLE|PortChannel0002|Ethernet116": { + "runner.actor_lacpdu_info.state": "61", + "runner.state": "current", + "runner.partner_lacpdu_info.port": "1", + "runner.actor_lacpdu_info.port": "117", + "runner.selected": "true", + "runner.partner_lacpdu_info.state": "61", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "1e:af:77:fc:79:ee", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "true", + "runner.aggregator.id": "97", + "link.up": "true", + "ifinfo.ifindex": "97" + }, + "LAG_MEMBER_TABLE|PortChannel0003|Ethernet120": { + "runner.actor_lacpdu_info.state": "61", + "runner.state": "current", + "runner.partner_lacpdu_info.port": "1", + "runner.actor_lacpdu_info.port": "121", + "runner.selected": "true", + "runner.partner_lacpdu_info.state": "61", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "16:0e:58:6f:3c:dd", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "true", + "runner.aggregator.id": "100", + "link.up": "true", + "ifinfo.ifindex": "100" + }, + "LAG_TABLE|PortChannel0001": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "71", + "setup.pid": "32", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0002": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "72", + "setup.pid": "40", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0003": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "73", + "setup.pid": "48", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0004": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "74", + "setup.pid": "56", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "FAN_INFO|fan1": { + "drawer_name": "drawer1", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "True", + "direction": "intake", + "speed": "30", + "speed_tolerance": "50", + "speed_target": "20", + "led_status": "red", + "timestamp": "20200813 01:32:30" + }, + "FAN_INFO|fan2": { + "drawer_name": "drawer2", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "False", + "direction": "intake", + "speed": "50", + "speed_tolerance": "50", + "speed_target": "50", + "led_status": "green", + "timestamp": "20200813 01:32:30" + }, + "FAN_INFO|fan3": { + "drawer_name": "drawer3", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "Updating", + "direction": "intake", + "speed": "50", + "speed_tolerance": "50", + "speed_target": "50", + "led_status": "green", + "timestamp": "20200813 01:32:30" + } +} diff --git a/sonic-utilities-tests/mock_tables/asic1/appl_db.json b/sonic-utilities-tests/mock_tables/asic1/appl_db.json index f5f67b26ce..3ac977cb02 100644 --- a/sonic-utilities-tests/mock_tables/asic1/appl_db.json +++ b/sonic-utilities-tests/mock_tables/asic1/appl_db.json @@ -1,4 +1,16 @@ { + "PORT_TABLE:Ethernet64": { + "oper_status": "up", + "lanes": "29,30,31,32", + "description": "ARISTA01T2:Ethernet3/2/1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet1/17", + "admin_status": "up", + "role": "Ext", + "speed": "40000", + "asic_port_name": "Eth0-ASIC1" + }, "PORT_TABLE:Ethernet-BP256": { "oper_status": "up", "lanes": "61,62,63,64", @@ -32,4 +44,4 @@ "LAG_MEMBER_TABLE:PortChannel4009:Ethernet-BP260": { "status": "enabled" } -} \ No newline at end of file +} diff --git a/sonic-utilities-tests/mock_tables/asic1/config_db.json b/sonic-utilities-tests/mock_tables/asic1/config_db.json index c8f97c5df1..bdcca2dcfe 100644 --- a/sonic-utilities-tests/mock_tables/asic1/config_db.json +++ b/sonic-utilities-tests/mock_tables/asic1/config_db.json @@ -16,6 +16,17 @@ "sub_role": "BackEnd", "type": "LeafRouter" }, + "PORT|Ethernet64": { + "admin_status": "up", + "alias": "Ethernet1/17", + "asic_port_name": "Eth0-ASIC1", + "description": "ARISTA01T2:Ethernet3/1/1", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "role": "Ext", + "speed": "40000" + }, "PORT|Ethernet-BP256": { "admin_status": "up", "alias": "Ethernet-BP256", @@ -52,4 +63,4 @@ "PORTCHANNEL_MEMBER|PortChannel4009|Ethernet-BP260" : { "NULL": "NULL" } -} \ No newline at end of file +} diff --git a/sonic-utilities-tests/mock_tables/asic1/state_db.json b/sonic-utilities-tests/mock_tables/asic1/state_db.json new file mode 100644 index 0000000000..1e748a539d --- /dev/null +++ b/sonic-utilities-tests/mock_tables/asic1/state_db.json @@ -0,0 +1,207 @@ +{ + "TRANSCEIVER_INFO|Ethernet64": { + "type": "QSFP28 or later", + "hardware_rev": "AC", + "serial": "MT1706FT02064", + "manufacturer": "Mellanox", + "model": "MFA1A00-C003", + "vendor_oui": "00-02-c9", + "vendor_date": "2017-01-13 ", + "connector": "No separable connector", + "encoding": "64B66B", + "ext_identifier": "Power Class 3(2.5W max), CDR present in Rx Tx", + "ext_rateselect_compliance": "QSFP+ Rate Select Version 1", + "cable_type": "Length Cable Assembly(m)", + "cable_length": "3", + "specification_compliance": "{'10/40G Ethernet Compliance Code': '40G Active Cable (XLPPI)'}", + "nominal_bit_rate": "255", + "application_advertisement": "N/A" + }, + "TRANSCEIVER_DOM_SENSOR|Ethernet64": { + "temperature": "30.9258", + "voltage": "3.2824", + "rx1power": "0.3802", + "rx2power": "-0.4871", + "rx3power": "-0.0860", + "rx4power": "0.3830", + "tx1bias": "6.7500", + "tx2bias": "6.7500", + "tx3bias": "6.7500", + "tx4bias": "6.7500", + "tx1power": "N/A", + "tx2power": "N/A", + "tx3power": "N/A", + "tx4power": "N/A", + "rxpowerhighalarm": "3.4001", + "rxpowerhighwarning": "2.4000", + "rxpowerlowalarm": "-13.5067", + "rxpowerlowwarning": "-9.5001", + "txbiashighalarm": "10.0000", + "txbiashighwarning": "9.5000", + "txbiaslowalarm": "0.5000", + "txbiaslowwarning": "1.0000", + "temphighalarm": "75.0000", + "temphighwarning": "70.0000", + "templowalarm": "-5.0000", + "templowwarning": "0.0000", + "vcchighalarm": "3.6300", + "vcchighwarning": "3.4650", + "vcclowalarm": "2.9700", + "vcclowwarning": "3.1349" + }, + "CHASSIS_INFO|chassis 1": { + "psu_num": "2" + }, + "PSU_INFO|PSU 1": { + "presence": "true", + "status": "true", + "led_status": "green" + }, + "PSU_INFO|PSU 2": { + "presence": "true", + "status": "true", + "led_status": "green" + }, + "SWITCH_CAPABILITY|switch": { + "MIRROR": "true", + "MIRRORV6": "true", + "ACL_ACTIONS|INGRESS": "PACKET_ACTION,REDIRECT_ACTION,MIRROR_INGRESS_ACTION", + "ACL_ACTIONS|EGRESS": "PACKET_ACTION,MIRROR_EGRESS_ACTION", + "ACL_ACTION|PACKET_ACTION": "FORWARD" + }, + "DEBUG_COUNTER_CAPABILITIES|PORT_INGRESS_DROPS": { + "reasons": "[IP_HEADER_ERROR,NO_L3_HEADER]", + "count": "4" + }, + "DEBUG_COUNTER_CAPABILITIES|SWITCH_EGRESS_DROPS": { + "reasons": "[ACL_ANY,L2_ANY,L3_ANY]", + "count": "2" + }, + "LAG_MEMBER_TABLE|PortChannel0001|Ethernet112": { + "runner.actor_lacpdu_info.state": "5", + "runner.state": "disabled", + "runner.partner_lacpdu_info.port": "0", + "runner.actor_lacpdu_info.port": "113", + "runner.selected": "false", + "runner.partner_lacpdu_info.state": "0", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "00:00:00:00:00:00", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "false", + "runner.aggregator.id": "0", + "link.up": "false", + "ifinfo.ifindex": "98" + }, + "LAG_MEMBER_TABLE|PortChannel0002|Ethernet116": { + "runner.actor_lacpdu_info.state": "61", + "runner.state": "current", + "runner.partner_lacpdu_info.port": "1", + "runner.actor_lacpdu_info.port": "117", + "runner.selected": "true", + "runner.partner_lacpdu_info.state": "61", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "1e:af:77:fc:79:ee", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "true", + "runner.aggregator.id": "97", + "link.up": "true", + "ifinfo.ifindex": "97" + }, + "LAG_MEMBER_TABLE|PortChannel0003|Ethernet120": { + "runner.actor_lacpdu_info.state": "61", + "runner.state": "current", + "runner.partner_lacpdu_info.port": "1", + "runner.actor_lacpdu_info.port": "121", + "runner.selected": "true", + "runner.partner_lacpdu_info.state": "61", + "ifinfo.dev_addr": "52:54:00:f2:e1:23", + "runner.partner_lacpdu_info.system": "16:0e:58:6f:3c:dd", + "link_watches.list.link_watch_0.up": "false", + "runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23", + "runner.aggregator.selected": "true", + "runner.aggregator.id": "100", + "link.up": "true", + "ifinfo.ifindex": "100" + }, + "LAG_TABLE|PortChannel0001": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "71", + "setup.pid": "32", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0002": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "72", + "setup.pid": "40", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0003": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "73", + "setup.pid": "48", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "LAG_TABLE|PortChannel0004": { + "runner.fallback": "false", + "team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23", + "team_device.ifinfo.ifindex": "74", + "setup.pid": "56", + "state": "ok", + "runner.fast_rate": "false", + "setup.kernel_team_mode_name": "loadbalance", + "runner.active": "true" + }, + "FAN_INFO|fan1": { + "drawer_name": "drawer1", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "True", + "direction": "intake", + "speed": "30", + "speed_tolerance": "50", + "speed_target": "20", + "led_status": "red", + "timestamp": "20200813 01:32:30" + }, + "FAN_INFO|fan2": { + "drawer_name": "drawer2", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "False", + "direction": "intake", + "speed": "50", + "speed_tolerance": "50", + "speed_target": "50", + "led_status": "green", + "timestamp": "20200813 01:32:30" + }, + "FAN_INFO|fan3": { + "drawer_name": "drawer3", + "presence": "True", + "model": "N/A", + "serial": "N/A", + "status": "Updating", + "direction": "intake", + "speed": "50", + "speed_tolerance": "50", + "speed_target": "50", + "led_status": "green", + "timestamp": "20200813 01:32:30" + } +} diff --git a/sonic-utilities-tests/multi_asic_intfutil_test.py b/sonic-utilities-tests/multi_asic_intfutil_test.py index 621828dd4d..8a5d260f74 100644 --- a/sonic-utilities-tests/multi_asic_intfutil_test.py +++ b/sonic-utilities-tests/multi_asic_intfutil_test.py @@ -8,43 +8,44 @@ scripts_path = os.path.join(modules_path, "scripts") intf_status_all = """\ - Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC ---------------- ------------ ------- ----- ----- -------------- --------------- ------ ------- ------ ---------- - Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up N/A off - Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off - Ethernet-BP0 93,94,95,96 40G 9100 N/A Ethernet-BP0 PortChannel4001 up up N/A off - Ethernet-BP4 97,98,99,100 40G 9100 N/A Ethernet-BP4 PortChannel4001 up up N/A off - Ethernet-BP256 61,62,63,64 40G 9100 N/A Ethernet-BP256 PortChannel4009 up up N/A off - Ethernet-BP260 57,58,59,60 40G 9100 N/A Ethernet-BP260 PortChannel4009 up up N/A off -PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A -PortChannel4001 N/A 80G 9100 N/A N/A routed up up N/A N/A -PortChannel4009 N/A 80G 9100 N/A N/A routed up up N/A N/A + Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC +--------------- ------------ ------- ----- ----- -------------- --------------- ------ ------- --------------- ---------- + Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up QSFP28 or later off + Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off + Ethernet64 29,30,31,32 40G 9100 N/A Ethernet1/17 routed up up QSFP28 or later off + Ethernet-BP0 93,94,95,96 40G 9100 N/A Ethernet-BP0 PortChannel4001 up up N/A off + Ethernet-BP4 97,98,99,100 40G 9100 N/A Ethernet-BP4 PortChannel4001 up up N/A off + Ethernet-BP256 61,62,63,64 40G 9100 N/A Ethernet-BP256 PortChannel4009 up up N/A off + Ethernet-BP260 57,58,59,60 40G 9100 N/A Ethernet-BP260 PortChannel4009 up up N/A off +PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A +PortChannel4001 N/A 80G 9100 N/A N/A routed up up N/A N/A +PortChannel4009 N/A 80G 9100 N/A N/A routed up up N/A N/A """ intf_status = """\ - Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC ---------------- ----------- ------- ----- ----- ----------- --------------- ------ ------- ------ ---------- - Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up N/A off - Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off -PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A + Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC +--------------- ----------- ------- ----- ----- ----------- --------------- ------ ------- --------------- ---------- + Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up QSFP28 or later off + Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off +PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A """ intf_status_asic0 = """\ - Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC ---------------- ----------- ------- ----- ----- ----------- --------------- ------ ------- ------ ---------- - Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up N/A off - Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off -PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A + Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC +--------------- ----------- ------- ----- ----- ----------- --------------- ------ ------- --------------- ---------- + Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up QSFP28 or later off + Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off +PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A """ intf_status_asic0_all = """\ - Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC ---------------- ------------ ------- ----- ----- ------------ --------------- ------ ------- ------ ---------- - Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up N/A off - Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off - Ethernet-BP0 93,94,95,96 40G 9100 N/A Ethernet-BP0 PortChannel4001 up up N/A off - Ethernet-BP4 97,98,99,100 40G 9100 N/A Ethernet-BP4 PortChannel4001 up up N/A off -PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A -PortChannel4001 N/A 80G 9100 N/A N/A routed up up N/A N/A + Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC +--------------- ------------ ------- ----- ----- ------------ --------------- ------ ------- --------------- ---------- + Ethernet0 33,34,35,36 40G 9100 N/A Ethernet1/1 PortChannel1002 up up QSFP28 or later off + Ethernet4 29,30,31,32 40G 9100 N/A Ethernet1/2 PortChannel1002 up up N/A off + Ethernet-BP0 93,94,95,96 40G 9100 N/A Ethernet-BP0 PortChannel4001 up up N/A off + Ethernet-BP4 97,98,99,100 40G 9100 N/A Ethernet-BP4 PortChannel4001 up up N/A off +PortChannel1002 N/A 80G 9100 N/A N/A routed up up N/A N/A +PortChannel4001 N/A 80G 9100 N/A N/A routed up up N/A N/A """ intf_description = """\ Interface Oper Admin Alias Description @@ -58,6 +59,7 @@ -------------- ------ ------- -------------- ------------------------ Ethernet0 up up Ethernet1/1 ARISTA01T2:Ethernet3/1/1 Ethernet4 up up Ethernet1/2 ARISTA01T2:Ethernet3/2/1 + Ethernet64 up up Ethernet1/17 ARISTA01T2:Ethernet3/2/1 Ethernet-BP0 up up Ethernet-BP0 ASIC1:Eth0-ASIC1 Ethernet-BP4 up up Ethernet-BP4 ASIC1:Eth1-ASIC1 Ethernet-BP256 up up Ethernet-BP256 ASIC0:Eth16-ASIC0 diff --git a/sonic-utilities-tests/sfp_test.py b/sonic-utilities-tests/sfp_test.py index 250e6fa92f..81e00c5b41 100644 --- a/sonic-utilities-tests/sfp_test.py +++ b/sonic-utilities-tests/sfp_test.py @@ -10,6 +10,230 @@ import show.main as show +test_sfp_eeprom_with_dom_output = """\ +Ethernet0: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + ChannelMonitorValues: + RX1Power: 0.3802dBm + RX2Power: -0.4871dBm + RX3Power: -0.0860dBm + RX4Power: 0.3830dBm + TX1Bias: 6.7500mA + TX2Bias: 6.7500mA + TX3Bias: 6.7500mA + TX4Bias: 6.7500mA + ChannelThresholdValues: + RxPowerHighAlarm : 3.4001dBm + RxPowerHighWarning: 2.4000dBm + RxPowerLowAlarm : -13.5067dBm + RxPowerLowWarning : -9.5001dBm + TxBiasHighAlarm : 10.0000mA + TxBiasHighWarning : 9.5000mA + TxBiasLowAlarm : 0.5000mA + TxBiasLowWarning : 1.0000mA + ModuleMonitorValues: + Temperature: 30.9258C + Vcc: 3.2824Volts + ModuleThresholdValues: + TempHighAlarm : 75.0000C + TempHighWarning: 70.0000C + TempLowAlarm : -5.0000C + TempLowWarning : 0.0000C + VccHighAlarm : 3.6300Volts + VccHighWarning : 3.4650Volts + VccLowAlarm : 2.9700Volts + VccLowWarning : 3.1349Volts + +""" + +test_sfp_eeprom_output = """\ +Ethernet0: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + +""" + +test_sfp_eeprom_dom_all_output = """\ +Ethernet0: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + ChannelMonitorValues: + RX1Power: 0.3802dBm + RX2Power: -0.4871dBm + RX3Power: -0.0860dBm + RX4Power: 0.3830dBm + TX1Bias: 6.7500mA + TX2Bias: 6.7500mA + TX3Bias: 6.7500mA + TX4Bias: 6.7500mA + ChannelThresholdValues: + RxPowerHighAlarm : 3.4001dBm + RxPowerHighWarning: 2.4000dBm + RxPowerLowAlarm : -13.5067dBm + RxPowerLowWarning : -9.5001dBm + TxBiasHighAlarm : 10.0000mA + TxBiasHighWarning : 9.5000mA + TxBiasLowAlarm : 0.5000mA + TxBiasLowWarning : 1.0000mA + ModuleMonitorValues: + Temperature: 30.9258C + Vcc: 3.2824Volts + ModuleThresholdValues: + TempHighAlarm : 75.0000C + TempHighWarning: 70.0000C + TempLowAlarm : -5.0000C + TempLowWarning : 0.0000C + VccHighAlarm : 3.6300Volts + VccHighWarning : 3.4650Volts + VccLowAlarm : 2.9700Volts + VccLowWarning : 3.1349Volts + +Ethernet4: SFP EEPROM Not detected + +Ethernet64: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + ChannelMonitorValues: + RX1Power: 0.3802dBm + RX2Power: -0.4871dBm + RX3Power: -0.0860dBm + RX4Power: 0.3830dBm + TX1Bias: 6.7500mA + TX2Bias: 6.7500mA + TX3Bias: 6.7500mA + TX4Bias: 6.7500mA + ChannelThresholdValues: + RxPowerHighAlarm : 3.4001dBm + RxPowerHighWarning: 2.4000dBm + RxPowerLowAlarm : -13.5067dBm + RxPowerLowWarning : -9.5001dBm + TxBiasHighAlarm : 10.0000mA + TxBiasHighWarning : 9.5000mA + TxBiasLowAlarm : 0.5000mA + TxBiasLowWarning : 1.0000mA + ModuleMonitorValues: + Temperature: 30.9258C + Vcc: 3.2824Volts + ModuleThresholdValues: + TempHighAlarm : 75.0000C + TempHighWarning: 70.0000C + TempLowAlarm : -5.0000C + TempLowWarning : 0.0000C + VccHighAlarm : 3.6300Volts + VccHighWarning : 3.4650Volts + VccLowAlarm : 2.9700Volts + VccLowWarning : 3.1349Volts + + +""" + +test_sfp_eeprom_all_output = """\ +Ethernet0: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + +Ethernet4: SFP EEPROM Not detected + +Ethernet64: SFP EEPROM detected + Application Advertisement: N/A + Connector: No separable connector + Encoding: 64B66B + Extended Identifier: Power Class 3(2.5W max), CDR present in Rx Tx + Extended RateSelect Compliance: QSFP+ Rate Select Version 1 + Identifier: QSFP28 or later + Length Cable Assembly(m): 3 + Nominal Bit Rate(100Mbs): 255 + Specification compliance: + 10/40G Ethernet Compliance Code: 40G Active Cable (XLPPI) + Vendor Date Code(YYYY-MM-DD Lot): 2017-01-13 + Vendor Name: Mellanox + Vendor OUI: 00-02-c9 + Vendor PN: MFA1A00-C003 + Vendor Rev: AC + Vendor SN: MT1706FT02064 + + +""" + +test_sfp_presence_all_output = """\ +Port Presence +---------- ----------- +Ethernet0 Present +Ethernet4 Not present +Ethernet64 Present +""" + class TestSFP(object): @classmethod def setup_class(cls): @@ -25,7 +249,6 @@ def test_sfp_presence(self): Ethernet0 Present """ assert result.output == expected - result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet200"]) expected = """Port Presence ----------- ----------- @@ -110,14 +333,82 @@ def test_sfp_eeprom(self): """ assert result.output == expected - result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet200"]) result_lines = result.output.strip('\n') expected = "Ethernet200: SFP EEPROM Not detected" assert result_lines == expected + @classmethod def teardown_class(cls): print("TEARDOWN") os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) os.environ["UTILITIES_UNIT_TESTING"] = "0" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "" + +class Test_multiAsic_SFP(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "1" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + def test_sfp_presence_with_ns(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet0 -n asic0"]) + expected = """Port Presence +--------- ---------- +Ethernet0 Present +""" + assert result.exit_code == 0 + assert result.output == expected + + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet200 -n asic0"]) + expected = """Port Presence +----------- ----------- +Ethernet200 Not present +""" + assert result.exit_code == 0 + assert result.output == expected + + def test_sfp_presence_all(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"]) + assert result.exit_code == 0 + assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_sfp_presence_all_output + + def test_sfp_eeprom_with_dom_with_ns(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet0 -d -n asic0"]) + assert result.exit_code == 0 + assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_sfp_eeprom_with_dom_output + + def test_sfp_eeprom_with_ns(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet0 -n asic0"]) + assert result.exit_code == 0 + assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_sfp_eeprom_output + + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet200 -n asic0"]) + result_lines = result.output.strip('\n') + expected = "Ethernet200: SFP EEPROM Not detected" + assert result_lines == expected + + def test_sfp_eeprom_all(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"]) + assert result.exit_code == 0 + assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_sfp_eeprom_all_output + + def test_sfp_eeprom_dom_all(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["-d"]) + assert result.exit_code == 0 + assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_sfp_eeprom_dom_all_output + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""