From 2c0ff9280e2890b336626b4f867af0b46604db1e Mon Sep 17 00:00:00 2001 From: arlakshm <55814491+arlakshm@users.noreply.github.com> Date: Fri, 21 Aug 2020 06:33:23 -0700 Subject: [PATCH] support show interface commands for multi ASIC platforms (#1006) Changes to support the show interface status/description for multi ASIC - Add argparse intfutil script - Change the IntfDescription and IntfStatus classes to get the information from all namespaces. - Add changes to filter out the internal ports from display - Add support for -n and -d click options - Add unit test to test mulit asic commands Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan --- scripts/intfutil | 308 +++++++++---------- setup.py | 7 +- show/interfaces/__init__.py | 63 ++-- show/main.py | 6 +- tests/intfutil_test.py | 16 +- tests/mock_tables/asic0/__init__.py | 0 tests/mock_tables/asic0/appl_db.json | 72 +++++ tests/mock_tables/asic0/asic_db.json | 6 + tests/mock_tables/asic0/config_db.json | 87 ++++++ tests/mock_tables/asic0/counters_db.json | 161 ++++++++++ tests/mock_tables/asic0/database_config.json | 57 ++++ tests/mock_tables/asic1/__init__.py | 0 tests/mock_tables/asic1/appl_db.json | 35 +++ tests/mock_tables/asic1/config_db.json | 55 ++++ tests/mock_tables/asic1/database_config.json | 57 ++++ tests/mock_tables/database_config.json | 57 ++++ tests/mock_tables/database_global.json | 16 + tests/mock_tables/dbconnector.py | 73 +++-- tests/mock_tables/mock_multi_asic.py | 20 ++ tests/multi_asic_intfutil_test.py | 182 +++++++++++ utilities_common/cli.py | 10 +- 21 files changed, 1064 insertions(+), 224 deletions(-) create mode 100644 tests/mock_tables/asic0/__init__.py create mode 100644 tests/mock_tables/asic0/appl_db.json create mode 100644 tests/mock_tables/asic0/asic_db.json create mode 100644 tests/mock_tables/asic0/config_db.json create mode 100644 tests/mock_tables/asic0/counters_db.json create mode 100644 tests/mock_tables/asic0/database_config.json create mode 100644 tests/mock_tables/asic1/__init__.py create mode 100644 tests/mock_tables/asic1/appl_db.json create mode 100644 tests/mock_tables/asic1/config_db.json create mode 100644 tests/mock_tables/asic1/database_config.json create mode 100644 tests/mock_tables/database_config.json create mode 100644 tests/mock_tables/database_global.json create mode 100644 tests/mock_tables/mock_multi_asic.py create mode 100644 tests/multi_asic_intfutil_test.py diff --git a/scripts/intfutil b/scripts/intfutil index 1cedf8d65b..8ba3a9bb6b 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -1,17 +1,17 @@ #! /usr/bin/python -import swsssdk -import sys +import argparse +import os import re +import sys import types -from tabulate import tabulate + from natsort import natsorted -from swsssdk import ConfigDBConnector -from pprint import pprint +from tabulate import tabulate +from utilities_common import constants +from utilities_common import multi_asic as multi_asic_util from utilities_common.intf_filter import parse_interface_in_filter -import os - # mock the redis for unit test purposes # try: if os.environ["UTILITIES_UNIT_TESTING"] == "2": @@ -20,6 +20,10 @@ try: sys.path.insert(0, modules_path) sys.path.insert(0, tests_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 @@ -44,16 +48,6 @@ VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation" SUB_PORT = "subport" -def db_connect_configdb(): - """ - Connect to configdb - """ - config_db = ConfigDBConnector() - if config_db is None: - return None - config_db.connect() - return config_db - def get_frontpanel_port_list(config_db): ports_dict = config_db.get_table('PORT') front_panel_ports_list = [] @@ -100,14 +94,6 @@ def config_db_vlan_port_keys_get(int_to_vlan_dict, front_panel_ports_list, intf_ return vlan -def db_connect_appl(): - appl_db = swsssdk.SonicV2Connector(host='127.0.0.1') - if appl_db is None: - return None - appl_db.connect(appl_db.APPL_DB) - return appl_db - - def appl_db_keys_get(appl_db, front_panel_ports_list, intf_name): """ Get APPL_DB Keys @@ -151,18 +137,6 @@ def appl_db_port_status_get(appl_db, intf_name, status_type): status = '{}G'.format(status[:-3]) return status - -def db_connect_state(): - """ - Connect to REDIS STATE DB and get optics info - """ - state_db = swsssdk.SonicV2Connector(host='127.0.0.1') - if state_db is None: - return None - state_db.connect(state_db.STATE_DB, False) # Make one attempt only - return state_db - - def state_db_port_optics_get(state_db, intf_name, type): """ Get optic type info for port @@ -348,9 +322,44 @@ def appl_db_sub_intf_status_get(appl_db, config_db, front_panel_ports_list, port header_stat = ['Interface', 'Lanes', 'Speed', 'MTU', 'FEC', 'Alias', 'Vlan', 'Oper', 'Admin', 'Type', 'Asym PFC'] header_stat_sub_intf = ['Sub port interface', 'Speed', 'MTU', 'Vlan', 'Admin', 'Type'] + class IntfStatus(object): - def display_intf_status(self, intf_name, appl_db_keys, front_panel_ports_list, portchannel_speed_dict, appl_db_sub_intf_keys, sub_intf_list, sub_intf_only): + def __init__(self, intf_name, namespace_option, display_option): + """ + Class constructor method + :param self: + :param intf_name: string of interface + :return: + """ + self.db = None + self.config_db = None + self.sub_intf_only = False + self.intf_name = intf_name + self.sub_intf_name = intf_name + self.table = [] + self.multi_asic = multi_asic_util.MultiAsic( + display_option, namespace_option) + if intf_name is not None: + if intf_name == SUB_PORT: + self.intf_name = None + self.sub_intf_name = None + self.sub_intf_only = True + else: + sub_intf_sep_idx = intf_name.find(VLAN_SUB_INTERFACE_SEPARATOR) + if sub_intf_sep_idx != -1: + self.sub_intf_only = True + self.intf_name = intf_name[:sub_intf_sep_idx] + + def display_intf_status(self): + self.get_intf_status() + sorted_table = natsorted(self.table) + print tabulate(sorted_table, + header_stat if not self.sub_intf_only else header_stat_sub_intf, + tablefmt="simple", + stralign='right') + + def generate_intf_status(self): """ Generate interface-status output """ @@ -359,91 +368,64 @@ class IntfStatus(object): table = [] key = [] - intf_fs = parse_interface_in_filter(intf_name) - + intf_fs = parse_interface_in_filter(self.intf_name) # # Iterate through all the keys and append port's associated state to # the result table. # - if not sub_intf_only: - for i in appl_db_keys: + if not self.sub_intf_only: + for i in self.appl_db_keys: key = re.split(':', i, maxsplit=1)[-1].strip() - if key in front_panel_ports_list: - if intf_name is None or key in intf_fs: + if key in self.front_panel_ports_list: + if self.multi_asic.skip_display(constants.PORT_OBJ, key): + continue + + if self.intf_name is None or key in intf_fs: table.append((key, - appl_db_port_status_get(self.appl_db, key, PORT_LANES_STATUS), - appl_db_port_status_get(self.appl_db, key, PORT_SPEED), - appl_db_port_status_get(self.appl_db, key, PORT_MTU_STATUS), - appl_db_port_status_get(self.appl_db, key, PORT_FEC), - appl_db_port_status_get(self.appl_db, key, PORT_ALIAS), + appl_db_port_status_get(self.db, key, PORT_LANES_STATUS), + appl_db_port_status_get(self.db, key, PORT_SPEED), + appl_db_port_status_get(self.db, key, PORT_MTU_STATUS), + appl_db_port_status_get(self.db, key, PORT_FEC), + appl_db_port_status_get(self.db, key, PORT_ALIAS), config_db_vlan_port_keys_get(self.combined_int_to_vlan_po_dict, self.front_panel_ports_list, key), - appl_db_port_status_get(self.appl_db, key, PORT_OPER_STATUS), - appl_db_port_status_get(self.appl_db, key, PORT_ADMIN_STATUS), - state_db_port_optics_get(self.state_db, key, PORT_OPTICS_TYPE), - appl_db_port_status_get(self.appl_db, key, PORT_PFC_ASYM_STATUS))) + appl_db_port_status_get(self.db, key, PORT_OPER_STATUS), + appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS), + state_db_port_optics_get(self.db, key, PORT_OPTICS_TYPE), + appl_db_port_status_get(self.db, key, PORT_PFC_ASYM_STATUS))) - for po, value in portchannel_speed_dict.iteritems(): + for po, value in self.portchannel_speed_dict.iteritems(): if po: - if intf_name is None or po in intf_fs: + if self.multi_asic.skip_display(constants.PORT_CHANNEL_OBJ, po): + continue + if self.intf_name is None or po in intf_fs: table.append((po, - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_LANES_STATUS, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_SPEED, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_MTU_STATUS, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_FEC, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_ALIAS, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, "vlan", self.portchannel_speed_dict, self.combined_int_to_vlan_po_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_OPER_STATUS, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_ADMIN_STATUS, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_OPTICS_TYPE, self.portchannel_speed_dict), - appl_db_portchannel_status_get(self.appl_db, self.config_db, po, PORT_PFC_ASYM_STATUS, self.portchannel_speed_dict))) + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_LANES_STATUS, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_SPEED, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_MTU_STATUS, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_FEC, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_ALIAS, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, "vlan", self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_OPER_STATUS, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_ADMIN_STATUS, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_OPTICS_TYPE, self.portchannel_speed_dict), + appl_db_portchannel_status_get(self.db, self.config_db, po, PORT_PFC_ASYM_STATUS, self.portchannel_speed_dict))) else: - for key in appl_db_sub_intf_keys: + for key in self.appl_db_sub_intf_keys: sub_intf = re.split(':', key, maxsplit=1)[-1].strip() - if sub_intf in sub_intf_list: + if sub_intf in self.sub_intf_list: table.append((sub_intf, - appl_db_sub_intf_status_get(self.appl_db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_SPEED), - appl_db_sub_intf_status_get(self.appl_db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_MTU_STATUS), - appl_db_sub_intf_status_get(self.appl_db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, "vlan"), - appl_db_sub_intf_status_get(self.appl_db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_ADMIN_STATUS), - appl_db_sub_intf_status_get(self.appl_db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_OPTICS_TYPE))) - - # Sorting and tabulating the result table. - sorted_table = natsorted(table) - print(tabulate(sorted_table, header_stat if not sub_intf_only else header_stat_sub_intf, tablefmt="simple", stralign='right')) - + appl_db_sub_intf_status_get(self.db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_SPEED), + appl_db_sub_intf_status_get(self.db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_MTU_STATUS), + appl_db_sub_intf_status_get(self.db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, "vlan"), + appl_db_sub_intf_status_get(self.db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_ADMIN_STATUS), + appl_db_sub_intf_status_get(self.db, self.config_db, self.front_panel_ports_list, self.portchannel_speed_dict, sub_intf, PORT_OPTICS_TYPE))) + return table - def __init__(self, intf_name): - """ - Class constructor method - :param self: - :param intf_name: string of interface - :return: - """ - self.appl_db = db_connect_appl() - self.state_db = db_connect_state() - self.config_db = db_connect_configdb() - if self.appl_db is None: - return - if self.state_db is None: - return - if self.config_db is None: - return - - sub_intf_only = False - sub_intf_name = intf_name - if intf_name is not None: - if intf_name == SUB_PORT: - intf_name = None - sub_intf_name = None - sub_intf_only = True - else: - sub_intf_sep_idx = intf_name.find(VLAN_SUB_INTERFACE_SEPARATOR) - if sub_intf_sep_idx != -1: - sub_intf_only = True - intf_name = intf_name[:sub_intf_sep_idx] + @multi_asic_util.run_on_multi_asic + def get_intf_status(self): self.front_panel_ports_list = get_frontpanel_port_list(self.config_db) - appl_db_keys = appl_db_keys_get(self.appl_db, self.front_panel_ports_list, None) + self.appl_db_keys = appl_db_keys_get(self.db, self.front_panel_ports_list, None) self.int_to_vlan_dict = get_interface_vlan_dict(self.config_db) self.get_raw_po_int_configdb_info = get_raw_portchannel_info(self.config_db) self.portchannel_list = get_portchannel_list(self.get_raw_po_int_configdb_info) @@ -451,16 +433,13 @@ class IntfStatus(object): self.po_int_dict = create_po_int_dict(self.po_int_tuple_list) self.int_po_dict = create_int_to_portchannel_dict(self.po_int_tuple_list) self.combined_int_to_vlan_po_dict = merge_dicts(self.int_to_vlan_dict, self.int_po_dict) - self.portchannel_speed_dict = po_speed_dict(self.po_int_dict, self.appl_db) + self.portchannel_speed_dict = po_speed_dict(self.po_int_dict, self.db) self.portchannel_keys = self.portchannel_speed_dict.keys() self.sub_intf_list = get_sub_port_intf_list(self.config_db) - appl_db_sub_intf_keys = appl_db_sub_intf_keys_get(self.appl_db, self.sub_intf_list, sub_intf_name) - if appl_db_keys is None: - return - self.display_intf_status(intf_name, appl_db_keys, self.front_panel_ports_list, self.portchannel_speed_dict, appl_db_sub_intf_keys, self.sub_intf_list, sub_intf_only) - - + self.appl_db_sub_intf_keys = appl_db_sub_intf_keys_get(self.db, self.sub_intf_list, self.sub_intf_name) + if self.appl_db_keys: + self.table += self.generate_intf_status() # ========================== interface-description logic ========================== @@ -470,7 +449,27 @@ header_desc = ['Interface', 'Oper', 'Admin', 'Alias', 'Description'] class IntfDescription(object): - def display_intf_description(self, appl_db_keys, front_panel_ports_list): + def __init__(self, intf_name, namespace_option, display_option): + self.db = None + self.config_db = None + self.table = [] + self.multi_asic = multi_asic_util.MultiAsic( + display_option, namespace_option) + + if intf_name is not None and intf_name == SUB_PORT: + self.intf_name = None + else: + self.intf_name = intf_name + + def display_intf_description(self): + + self.get_intf_description() + + # Sorting and tabulating the result table. + sorted_table = natsorted(self.table) + print tabulate(sorted_table, header_desc, tablefmt="simple", stralign='right') + + def generate_intf_description(self): """ Generate interface-description output """ @@ -483,58 +482,41 @@ class IntfDescription(object): # Iterate through all the keys and append port's associated state to # the result table. # - for i in appl_db_keys: + for i in self.appl_db_keys: key = re.split(':', i, maxsplit=1)[-1].strip() - if key in front_panel_ports_list: + if key in self.front_panel_ports_list: + if self.multi_asic.skip_display(constants.PORT_OBJ, key): + continue table.append((key, - appl_db_port_status_get(self.appl_db, key, PORT_OPER_STATUS), - appl_db_port_status_get(self.appl_db, key, PORT_ADMIN_STATUS), - appl_db_port_status_get(self.appl_db, key, PORT_ALIAS), - appl_db_port_status_get(self.appl_db, key, PORT_DESCRIPTION))) - - # Sorting and tabulating the result table. - sorted_table = natsorted(table) - print(tabulate(sorted_table, header_desc, tablefmt="simple", stralign='right')) - - def __init__(self, intf_name): - - self.config_db = db_connect_configdb() - self.appl_db = db_connect_appl() - if self.appl_db is None: - return - if self.config_db is None: - return - - if intf_name is not None and intf_name == SUB_PORT: - intf_name = None - + appl_db_port_status_get(self.db, key, PORT_OPER_STATUS), + appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS), + appl_db_port_status_get(self.db, key, PORT_ALIAS), + appl_db_port_status_get(self.db, key, PORT_DESCRIPTION))) + return table + + @multi_asic_util.run_on_multi_asic + def get_intf_description(self): self.front_panel_ports_list = get_frontpanel_port_list(self.config_db) - appl_db_keys = appl_db_keys_get(self.appl_db, self.front_panel_ports_list, intf_name) - if appl_db_keys is None: - return - - self.display_intf_description(appl_db_keys, self.front_panel_ports_list) - - - -def main(args): - if len(args) == 0: - print("No valid arguments provided") - return - - command = args[0] - if command != "status" and command != "description": - print("No valid command provided") - return - - intf_name = args[1] if len(args) == 2 else None - - if command == "status": - interface_stat = IntfStatus(intf_name) - elif command == "description": - interface_desc = IntfDescription(intf_name) + self.appl_db_keys = appl_db_keys_get(self.db, self.front_panel_ports_list, self.intf_name) + if self.appl_db_keys: + self.table += self.generate_intf_description() + +def main(): + parser = argparse.ArgumentParser(description='Display Interface information', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('-c', '--command', type=str, help='get interface status or description', default=None) + parser.add_argument('-i', '--interface', type=str, help='interface information for specific port: Ethernet0', default=None) + parser = multi_asic_util.multi_asic_args(parser) + args = parser.parse_args() + + if args.command == "status": + interface_stat = IntfStatus(args.interface, args.namespace, args.display) + interface_stat.display_intf_status() + elif args.command == "description": + interface_desc = IntfDescription(args.interface, args.namespace, args.display) + interface_desc.display_intf_description() sys.exit(0) if __name__ == "__main__": - main(sys.argv[1:]) + main() diff --git a/setup.py b/setup.py index 37e55cba1e..434ece3f3d 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,12 @@ ], package_data={ 'show': ['aliases.ini'], - 'tests': ['acl_input/*', 'mock_tables/*.py', 'mock_tables/*.json', 'filter_fdb_input/*'] + 'tests': ['acl_input/*', + 'mock_tables/*.py', + 'mock_tables/*.json', + 'mock_tables/asic0/*.json', + 'mock_tables/asic1/*.json', + 'filter_fdb_input/*'] }, scripts=[ 'scripts/aclshow', diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index b2d1c17936..06c74b9b2a 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -4,15 +4,17 @@ from natsort import natsorted from tabulate import tabulate +from sonic_py_common import multi_asic import utilities_common.cli as clicommon +import utilities_common.multi_asic as multi_asic_util import portchannel -def try_convert_interfacename_from_alias(ctx, db, interfacename): +def try_convert_interfacename_from_alias(ctx, interfacename): """try to convert interface name from alias""" if clicommon.get_interface_naming_mode() == "alias": alias = interfacename - interfacename = clicommon.InterfaceAliasConverter(db).alias_to_name(alias) + interfacename = clicommon.InterfaceAliasConverter().alias_to_name(alias) # TODO: ideally alias_to_name should return None when it cannot find # the port name for the alias if interfacename == alias: @@ -31,19 +33,19 @@ def interfaces(): # 'alias' subcommand ("show interfaces alias") @interfaces.command() @click.argument('interfacename', required=False) -@clicommon.pass_db -def alias(db, interfacename): +@multi_asic_util.multi_asic_click_options +def alias(interfacename, namespace, display): """Show Interface Name/Alias Mapping""" ctx = click.get_current_context() - port_dict = db.cfgdb.get_table("PORT") + port_dict = multi_asic.get_port_table(namespace=namespace) header = ['Name', 'Alias'] body = [] if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) # If we're given an interface name, output name and alias for that interface only if interfacename in port_dict.keys(): @@ -56,6 +58,10 @@ def alias(db, interfacename): else: # Output name and alias for all interfaces for port_name in natsorted(port_dict.keys()): + if ((display == multi_asic_util.constants.DISPLAY_EXTERNAL) and + ('role' in port_dict[port_name]) and + (port_dict[port_name]['role'] is multi_asic.INTERNAL_PORT)): + continue if 'alias' in port_dict[port_name]: body.append([port_name, port_dict[port_name]['alias']]) else: @@ -65,19 +71,25 @@ def alias(db, interfacename): @interfaces.command() @click.argument('interfacename', required=False) +@multi_asic_util.multi_asic_click_options @click.option('--verbose', is_flag=True, help="Enable verbose output") -@clicommon.pass_db -def description(db, interfacename, verbose): +def description(interfacename, namespace, display, verbose): """Show interface status, protocol and description""" ctx = click.get_current_context() - cmd = "intfutil description" + cmd = "intfutil -c description" + #ignore the display option when interface name is passed if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) - cmd += " {}".format(interfacename) + cmd += " -i {}".format(interfacename) + else: + cmd += " -d {}".format(display) + + if namespace is not None: + cmd += " -n {}".format(namespace) clicommon.run_command(cmd, display_cmd=verbose) @@ -91,19 +103,24 @@ def naming_mode(verbose): @interfaces.command() @click.argument('interfacename', required=False) +@multi_asic_util.multi_asic_click_options @click.option('--verbose', is_flag=True, help="Enable verbose output") -@clicommon.pass_db -def status(db, interfacename, verbose): +def status(interfacename, namespace, display, verbose): """Show Interface status information""" ctx = click.get_current_context() - cmd = "intfutil status" + cmd = "intfutil -c status" if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) - cmd += " {}".format(interfacename) + cmd += " -i {}".format(interfacename) + else: + cmd += " -d {}".format(display) + + if namespace is not None: + cmd += " -n {}".format(namespace) clicommon.run_command(cmd, display_cmd=verbose) @@ -225,7 +242,7 @@ def expected(db, interfacename): for port in natsorted(neighbor_dict.keys()): temp_port = port if clicommon.get_interface_naming_mode() == "alias": - port = clicommon.InterfaceAliasConverter(db).name_to_alias(port) + port = clicommon.InterfaceAliasConverter().name_to_alias(port) neighbor_dict[port] = neighbor_dict.pop(temp_port) device2interface_dict[neighbor_dict[port]['name']] = {'localPort': port, 'neighborPort': neighbor_dict[port]['port']} @@ -268,8 +285,7 @@ def transceiver(): @click.argument('interfacename', required=False) @click.option('-d', '--dom', 'dump_dom', is_flag=True, help="Also display Digital Optical Monitoring (DOM) data") @click.option('--verbose', is_flag=True, help="Enable verbose output") -@clicommon.pass_db -def eeprom(db, interfacename, dump_dom, verbose): +def eeprom(interfacename, dump_dom, verbose): """Show interface transceiver EEPROM information""" ctx = click.get_current_context() @@ -280,7 +296,7 @@ def eeprom(db, interfacename, dump_dom, verbose): cmd += " --dom" if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) cmd += " -p {}".format(interfacename) @@ -289,8 +305,7 @@ def eeprom(db, interfacename, dump_dom, verbose): @transceiver.command() @click.argument('interfacename', required=False) @click.option('--verbose', is_flag=True, help="Enable verbose output") -@clicommon.pass_db -def lpmode(db, interfacename, verbose): +def lpmode(interfacename, verbose): """Show interface transceiver low-power mode status""" ctx = click.get_current_context() @@ -298,7 +313,7 @@ def lpmode(db, interfacename, verbose): cmd = "sudo sfputil show lpmode" if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) cmd += " -p {}".format(interfacename) @@ -316,7 +331,7 @@ def presence(db, interfacename, verbose): cmd = "sfpshow presence" if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, db, interfacename) + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) cmd += " -p {}".format(interfacename) diff --git a/show/main.py b/show/main.py index 712ae674a4..710a4a9efe 100755 --- a/show/main.py +++ b/show/main.py @@ -468,7 +468,7 @@ def subinterfaces(): @click.option('--verbose', is_flag=True, help="Enable verbose output") def status(subinterfacename, verbose): """Show sub port interface status information""" - cmd = "intfutil status " + cmd = "intfutil -c status" if subinterfacename is not None: sub_intf_sep_idx = subinterfacename.find(VLAN_SUB_INTERFACE_SEPARATOR) @@ -479,9 +479,9 @@ def status(subinterfacename, verbose): if clicommon.get_interface_naming_mode() == "alias": subinterfacename = iface_alias_converter.alias_to_name(subinterfacename) - cmd += subinterfacename + cmd += " -i {}".format(subinterfacename) else: - cmd += "subport" + cmd += " -i subport" run_command(cmd, display_cmd=verbose) # diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index 3841420f92..53e878dcd1 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -50,7 +50,7 @@ """ show_interface_description_Ethernet0_verbose_output="""\ -Running command: intfutil description Ethernet0 +Running command: intfutil -c description -i Ethernet0 Interface Oper Admin Alias Description ----------- ------ ------- --------- -------------------- Ethernet0 down up Ethernet0 ARISTA01T2:Ethernet1 @@ -83,7 +83,7 @@ def test_intf_status(self): assert result.output == show_interface_status_output # Test 'intfutil status' - output = subprocess.check_output('intfutil status', stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output('intfutil -c status', stderr=subprocess.STDOUT, shell=True) print(output) assert result.output == show_interface_status_output @@ -93,7 +93,7 @@ def test_intf_status_verbose(self): assert result.exit_code == 0 print(result.exit_code) print(result.output) - expected_output = "Running command: intfutil status" + expected_output = "Running command: intfutil -c status -d all" assert result.output.split('\n')[0] == expected_output def test_intf_status_Ethernet32(self): @@ -164,7 +164,7 @@ def test_subintf_status(self): self.assertEqual(result.output.strip(), expected_output) # Test 'intfutil status subport' - output = subprocess.check_output('intfutil status subport', stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output('intfutil -c status -i subport', stderr=subprocess.STDOUT, shell=True) print >> sys.stderr, output self.assertEqual(output.strip(), expected_output) @@ -172,7 +172,7 @@ def test_subintf_status(self): def test_subintf_status_verbose(self): result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["--verbose"]) print >> sys.stderr, result.output - expected_output = "Command: intfutil status subport" + expected_output = "Command: intfutil -c status -i subport" self.assertEqual(result.output.split('\n')[0], expected_output) @@ -189,7 +189,7 @@ def test_single_subintf_status(self): self.assertEqual(result.output.strip(), expected_output) # Test 'intfutil status Ethernet0.10' - output = subprocess.check_output('intfutil status Ethernet0.10', stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output('intfutil -c status -i Ethernet0.10', stderr=subprocess.STDOUT, shell=True) print >> sys.stderr, output self.assertEqual(output.strip(), expected_output) @@ -197,7 +197,7 @@ def test_single_subintf_status(self): def test_single_subintf_status_verbose(self): result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["Ethernet0.10", "--verbose"]) print >> sys.stderr, result.output - expected_output = "Command: intfutil status Ethernet0.10" + expected_output = "Command: intfutil -c status -i Ethernet0.10" self.assertEqual(result.output.split('\n')[0], expected_output) @@ -222,7 +222,7 @@ def test_single_subintf_status_alias_mode_verbose(self): result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["etp1.10", "--verbose"]) print >> sys.stderr, result.output - expected_output = "Command: intfutil status Ethernet0.10" + expected_output = "Command: intfutil -c status -i Ethernet0.10" self.assertEqual(result.output.split('\n')[0], expected_output) os.environ["SONIC_CLI_IFACE_MODE"] = "default" diff --git a/tests/mock_tables/asic0/__init__.py b/tests/mock_tables/asic0/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/mock_tables/asic0/appl_db.json b/tests/mock_tables/asic0/appl_db.json new file mode 100644 index 0000000000..cfe085962f --- /dev/null +++ b/tests/mock_tables/asic0/appl_db.json @@ -0,0 +1,72 @@ +{ + "PORT_TABLE:Ethernet0": { + "lanes": "33,34,35,36", + "description": "ARISTA01T2:Ethernet3/1/1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet1/1", + "oper_status": "up", + "admin_status": "up", + "role": "Ext", + "speed": "40000", + "asic_port_name": "Eth0-ASIC0" + }, + "PORT_TABLE:Ethernet4": { + "oper_status": "up", + "lanes": "29,30,31,32", + "description": "ARISTA01T2:Ethernet3/2/1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet1/2", + "admin_status": "up", + "role": "Ext", + "speed": "40000", + "asic_port_name": "Eth1-ASIC0" + }, + "PORT_TABLE:Ethernet-BP0": { + "oper_status": "up", + "lanes": "93,94,95,96", + "description": "ASIC1:Eth0-ASIC1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP0", + "admin_status": "up", + "role": "Int", + "speed": "40000", + "asic_port_name": "Eth16-ASIC0" + }, + "PORT_TABLE:Ethernet-BP4": { + "oper_status": "up", + "lanes": "97,98,99,100", + "description": "ASIC1:Eth1-ASIC1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP4", + "admin_status": "up", + "role": "Int", + "speed": "40000", + "asic_port_name": "Eth17-ASIC0" + }, + "LAG_MEMBER_TABLE:PortChannel1002:Ethernet0": { + "status": "disabled" + }, + "LAG_MEMBER_TABLE:PortChannel1002:Ethernet4": { + "status": "enabled" + }, + "LAG_MEMBER_TABLE:PortChannel4001:Ethernet-BP0": { + "status": "enabled" + }, + "LAG_MEMBER_TABLE:PortChannel4001:Ethernet-BP4": { + "status": "enabled" + }, + "LAG_TABLE:PortChannel1002": { + "admin_status": "up", + "mtu": "9100", + "oper_status": "up" + }, + "LAG_TABLE:PortChannel4001": { + "admin_status": "up", + "mtu": "9100", + "oper_status": "up" + } +} \ No newline at end of file diff --git a/tests/mock_tables/asic0/asic_db.json b/tests/mock_tables/asic0/asic_db.json new file mode 100644 index 0000000000..1a769b82b5 --- /dev/null +++ b/tests/mock_tables/asic0/asic_db.json @@ -0,0 +1,6 @@ +{ + "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000": { + "SAI_SWITCH_ATTR_INIT_SWITCH": "true", + "SAI_SWITCH_ATTR_SRC_MAC_ADDRESS": "DE:AD:BE:EF:CA:FE" + } +} diff --git a/tests/mock_tables/asic0/config_db.json b/tests/mock_tables/asic0/config_db.json new file mode 100644 index 0000000000..79adcf758e --- /dev/null +++ b/tests/mock_tables/asic0/config_db.json @@ -0,0 +1,87 @@ +{ + "DEVICE_METADATA|localhost": { + "asic_id": "01.00.0", + "asic_name": "asic0", + "bgp_asn": "65100", + "cloudtype": "None", + "default_bgp_status": "down", + "default_pfcwd_status": "enable", + "deployment_id": "None", + "docker_routing_config_mode": "separated", + "hostname": "sonic", + "hwsku": "multi_asic", + "mac": "02:42:f0:7f:01:05", + "platform": "multi_asic", + "region": "None", + "sub_role": "FrontEnd", + "type": "LeafRouter" + }, + "PORT|Ethernet0": { + "admin_status": "up", + "alias": "Ethernet1/1", + "asic_port_name": "Eth0-ASIC0", + "description": "ARISTA01T2:Ethernet3/1/1", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "role": "Ext", + "speed": "40000" + }, + "PORT|Ethernet4": { + "lanes": "29,30,31,32", + "description": "ARISTA01T2:Ethernet3/2/1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet1/2", + "admin_status": "up", + "role": "Ext", + "speed": "40000", + "asic_port_name": "Eth1-ASIC0" + }, + "PORT|Ethernet-BP0" : { + "lanes": "93,94,95,96", + "description": "ASIC1:Eth0-ASIC1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP0", + "admin_status": "up", + "role": "Int", + "speed": "40000", + "asic_port_name": "Eth16-ASIC0" + }, + "PORT|Ethernet-BP4" : { + "lanes": "97,98,99,100", + "description": "ASIC1:Eth1-ASIC1", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP4", + "admin_status": "up", + "role": "Int", + "speed": "40000", + "asic_port_name": "Eth17-ASIC0" + }, + "PORTCHANNEL|PortChannel1002": { + "admin_status": "up", + "members@": "Ethernet0,Ethernet4", + "min_links": "2", + "mtu": "9100" + }, + "PORTCHANNEL|PortChannel4001": { + "admin_status": "up", + "members@": "Ethernet-BP0,Ethernet-BP4", + "min_links": "2", + "mtu": "9100" + }, + "PORTCHANNEL_MEMBER|PortChannel1002|Ethernet0": { + "NULL": "NULL" + }, + "PORTCHANNEL_MEMBER|PortChannel1002|Ethernet4": { + "NULL": "NULL" + }, + "PORTCHANNEL_MEMBER|PortChannel4001|Ethernet-BP0": { + "NULL": "NULL" + }, + "PORTCHANNEL_MEMBER|PortChannel4001|Ethernet-BP4": { + "NULL": "NULL" + } +} \ No newline at end of file diff --git a/tests/mock_tables/asic0/counters_db.json b/tests/mock_tables/asic0/counters_db.json new file mode 100644 index 0000000000..2b2b600280 --- /dev/null +++ b/tests/mock_tables/asic0/counters_db.json @@ -0,0 +1,161 @@ +{ + "COUNTERS:oid:0x60000000005a3": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" + }, + "COUNTERS:oid:0x60000000005a1": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "608985", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "883", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" + }, + "COUNTERS:oid:0x600000000065f": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "1128", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "2", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "3", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "4", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "5", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "6", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "754", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "8" + }, + "COUNTERS:oid:0x60000000005a2": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "608985", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "883", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" + }, + "COUNTERS:oid:0x600000000063c": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" + }, + "COUNTERS:oid:0x600000000063d": { + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS": "608985", + "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS": "883", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", + "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" + }, + "COUNTERS_RIF_NAME_MAP": { + "Ethernet20": "oid:0x600000000065f", + "PortChannel0001": "oid:0x60000000005a1", + "PortChannel0002": "oid:0x60000000005a2", + "PortChannel0003": "oid:0x600000000063c", + "PortChannel0004": "oid:0x600000000063d", + "Vlan1000": "oid:0x60000000005a3" + }, + "COUNTERS_RIF_TYPE_MAP": { + "oid:0x60000000005a1": "SAI_ROUTER_INTERFACE_TYPE_PORT", + "oid:0x60000000005a2": "SAI_ROUTER_INTERFACE_TYPE_PORT", + "oid:0x60000000005a3": "SAI_ROUTER_INTERFACE_TYPE_VLAN", + "oid:0x600000000063c": "SAI_ROUTER_INTERFACE_TYPE_PORT", + "oid:0x600000000063d": "SAI_ROUTER_INTERFACE_TYPE_PORT", + "oid:0x600000000065f": "SAI_ROUTER_INTERFACE_TYPE_PORT" + }, + "COUNTERS:DATAACL:DEFAULT_RULE": { + "Bytes": "1", + "Packets": "2" + }, + "COUNTERS:DATAACL:RULE_1": { + "Bytes": "100", + "Packets": "101" + }, + "COUNTERS:DATAACL:RULE_2": { + "Bytes": "200", + "Packets": "201" + }, + "COUNTERS:DATAACL:RULE_3": { + "Bytes": "300", + "Packets": "301" + }, + "COUNTERS:DATAACL:RULE_4": { + "Bytes": "400", + "Packets": "401" + }, + "COUNTERS:DATAACL:RULE_05": { + "Bytes": "0", + "Packets": "0" + }, + "COUNTERS:EVERFLOW:RULE_6": { + "Bytes": "600", + "Packets": "601" + }, + "COUNTERS:DATAACL:RULE_7":{ + "Bytes": "700", + "Packets": "701" + }, + "COUNTERS:EVERFLOW:RULE_08": { + "Bytes": "0", + "Packets": "0" + }, + "COUNTERS:DATAACL:RULE_9": { + "Bytes": "900", + "Packets": "901" + }, + "COUNTERS:DATAACL:RULE_10": { + "Bytes": "1000", + "Packets": "1001" + }, + "COUNTERS:oid:0x1000000000002": { + "SAI_PORT_STAT_IF_IN_ERRORS": "10", + "SAI_PORT_STAT_IF_IN_DISCARDS": "100", + "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "80", + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "20" + }, + "COUNTERS:oid:0x1000000000004": { + "SAI_PORT_STAT_IF_IN_ERRORS": "0", + "SAI_PORT_STAT_IF_IN_DISCARDS": "1000", + "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800", + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100" + }, + "COUNTERS:oid:0x1000000000006": { + "SAI_PORT_STAT_IF_IN_ERRORS": "100", + "SAI_PORT_STAT_IF_IN_DISCARDS": "10", + "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "10", + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "0" + }, + "COUNTERS:oid:0x21000000000000": { + "SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE": "1000" + }, + "COUNTERS_PORT_NAME_MAP": { + "Ethernet0": "oid:0x1000000000002", + "Ethernet4": "oid:0x1000000000004", + "Ethernet8": "oid:0x1000000000006" + }, + "COUNTERS_LAG_NAME_MAP": { + "PortChannel0001": "oid:0x60000000005a1", + "PortChannel0002": "oid:0x60000000005a2", + "PortChannel0003": "oid:0x600000000063c", + "PortChannel0004": "oid:0x600000000063d" + }, + "COUNTERS_DEBUG_NAME_PORT_STAT_MAP": { + "DEBUG_0": "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE", + "DEBUG_2": "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS" + }, + "COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP": { + "DEBUG_1": "SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE" + } +} diff --git a/tests/mock_tables/asic0/database_config.json b/tests/mock_tables/asic0/database_config.json new file mode 100644 index 0000000000..d3028b0b45 --- /dev/null +++ b/tests/mock_tables/asic0/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis": { + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.1" +} \ No newline at end of file diff --git a/tests/mock_tables/asic1/__init__.py b/tests/mock_tables/asic1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/mock_tables/asic1/appl_db.json b/tests/mock_tables/asic1/appl_db.json new file mode 100644 index 0000000000..f5f67b26ce --- /dev/null +++ b/tests/mock_tables/asic1/appl_db.json @@ -0,0 +1,35 @@ +{ + "PORT_TABLE:Ethernet-BP256": { + "oper_status": "up", + "lanes": "61,62,63,64", + "description": "ASIC0:Eth16-ASIC0", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP256", + "admin_status": "up", + "speed": "40000", + "asic_port_name": "Eth0-ASIC1" + }, + "PORT_TABLE:Ethernet-BP260": { + "oper_status": "up", + "lanes": "57,58,59,60", + "description": "ASIC0:Eth17-ASIC0", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP260", + "admin_status": "up", + "speed": "40000", + "asic_port_name": "Eth1-ASIC1" + }, + "LAG_TABLE:PortChannel4009": { + "admin_status": "up", + "oper_status": "up", + "mtu": "9100" + }, + "LAG_MEMBER_TABLE:PortChannel4009:Ethernet-BP256": { + "status": "enabled" + }, + "LAG_MEMBER_TABLE:PortChannel4009:Ethernet-BP260": { + "status": "enabled" + } +} \ No newline at end of file diff --git a/tests/mock_tables/asic1/config_db.json b/tests/mock_tables/asic1/config_db.json new file mode 100644 index 0000000000..c8f97c5df1 --- /dev/null +++ b/tests/mock_tables/asic1/config_db.json @@ -0,0 +1,55 @@ +{ + "DEVICE_METADATA|localhost": { + "asic_id": "08:00.0", + "asic_name": "asic1", + "bgp_asn": "65100", + "cloudtype": "None", + "default_bgp_status": "down", + "default_pfcwd_status": "enable", + "deployment_id": "None", + "docker_routing_config_mode": "separated", + "hostname": "sonic", + "hwsku": "multi_asic", + "mac": "02:42:f0:7f:01:06", + "platform": "multi_asic", + "region": "None", + "sub_role": "BackEnd", + "type": "LeafRouter" + }, + "PORT|Ethernet-BP256": { + "admin_status": "up", + "alias": "Ethernet-BP256", + "asic_port_name": "Eth0-ASIC1", + "description": "ASIC0:Eth16-ASIC0", + "lanes": "61,62,63,64", + "mtu": "9100", + "pfc_asym": "off", + "role": "Int", + "speed": "40000" + }, + "PORT|Ethernet-BP260": { + "lanes": "57,58,59,60", + "description": "ASIC0:Eth17-ASIC0", + "pfc_asym": "off", + "mtu": "9100", + "alias": "Ethernet-BP260", + "admin_status": "up", + "speed": "40000", + "role": "Int", + "asic_port_name": "Eth1-ASIC1" + }, + "PORTCHANNEL|PortChannel4009": { + "admin_status": "up", + "members": [ + "Ethernet-BP256,Ethernet-BP260" + ], + "min_links": "2", + "mtu": "9100" + }, + "PORTCHANNEL_MEMBER|PortChannel4009|Ethernet-BP256": { + "NULL": "NULL" + }, + "PORTCHANNEL_MEMBER|PortChannel4009|Ethernet-BP260" : { + "NULL": "NULL" + } +} \ No newline at end of file diff --git a/tests/mock_tables/asic1/database_config.json b/tests/mock_tables/asic1/database_config.json new file mode 100644 index 0000000000..d3028b0b45 --- /dev/null +++ b/tests/mock_tables/asic1/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis": { + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.1" +} \ No newline at end of file diff --git a/tests/mock_tables/database_config.json b/tests/mock_tables/database_config.json new file mode 100644 index 0000000000..04c064abb3 --- /dev/null +++ b/tests/mock_tables/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis": { + "hostname" : "227.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.1" +} \ No newline at end of file diff --git a/tests/mock_tables/database_global.json b/tests/mock_tables/database_global.json new file mode 100644 index 0000000000..f6f35f7fde --- /dev/null +++ b/tests/mock_tables/database_global.json @@ -0,0 +1,16 @@ +{ + "INCLUDES" : [ + { + "include" : "database_config.json" + }, + { + "namespace" : "asic0", + "include" : "./asic0/database_config.json" + }, + { + "namespace" : "asic1", + "include" : "./asic1/database_config.json" + } + ], + "VERSION" : "1.0" +} \ No newline at end of file diff --git a/tests/mock_tables/dbconnector.py b/tests/mock_tables/dbconnector.py index dc1a64bc87..a06cfea202 100644 --- a/tests/mock_tables/dbconnector.py +++ b/tests/mock_tables/dbconnector.py @@ -5,8 +5,44 @@ import mock import mockredis import swsssdk.interface +from sonic_py_common import multi_asic +from swsssdk import SonicDBConfig, SonicV2Connector from swsssdk.interface import redis +def clean_up_config(): + # Set SonicDBConfig variables to initial state + # so that it can be loaded with single or multiple + # namespaces before the test begins. + SonicDBConfig._sonic_db_config = {} + SonicDBConfig._sonic_db_global_config_init = False + SonicDBConfig._sonic_db_config_init = False + +def load_namespace_config(): + # To support multi asic testing + # SonicDBConfig load_sonic_global_db_config + # is invoked to load multiple namespaces + clean_up_config() + SonicDBConfig.load_sonic_global_db_config( + global_db_file_path=os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'database_global.json')) + +def load_database_config(): + # Load local database_config.json for single namespace test scenario + clean_up_config() + SonicDBConfig.load_sonic_db_config( + sonic_db_file_path=os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'database_config.json')) + + +_old_connect_SonicV2Connector = SonicV2Connector.connect + +def connect_SonicV2Connector(self, db_name, retry_on=True): + + # add the namespace to kwargs for testing multi asic + self.dbintf.redis_kwargs['namespace'] = self.namespace + # Mock DB filename for unit-test + self.dbintf.redis_kwargs['db_name'] = db_name + _old_connect_SonicV2Connector(self, db_name, retry_on) def _subscribe_keyspace_notification(self, db_name, client): pass @@ -41,27 +77,25 @@ def clear(self): class SwssSyncClient(mockredis.MockRedis): def __init__(self, *args, **kwargs): super(SwssSyncClient, self).__init__(strict=True, *args, **kwargs) - db = kwargs.pop('db') - if db == 0: - fname = 'appl_db.json' - elif db == 1: - fname = 'asic_db.json' - elif db == 2: - fname = 'counters_db.json' - elif db == 4: - fname = 'config_db.json' - elif db == 6: - fname = 'state_db.json' - else: - raise ValueError("Invalid db") + # Namespace is added in kwargs specifically for unit-test + # to identify the file path to load the db json files. + namespace = kwargs.pop('namespace') + db_name = kwargs.pop('db_name') + fname = db_name.lower() + ".json" self.pubsub = MockPubSub() + + if namespace is not None and namespace is not multi_asic.DEFAULT_NAMESPACE: + fname = os.path.join(INPUT_DIR, namespace, fname) + else: + fname = os.path.join(INPUT_DIR, fname) + - fname = os.path.join(INPUT_DIR, fname) - with open(fname) as f: - js = json.load(f) - for h, table in js.items(): - for k, v in table.items(): - self.hset(h, k, v) + if os.path.exists(fname): + with open(fname) as f: + js = json.load(f) + for h, table in js.items(): + for k, v in table.items(): + self.hset(h, k, v) # Patch mockredis/mockredis/client.py # The official implementation will filter out keys with a slash '/' @@ -90,3 +124,4 @@ def keys(self, pattern='*'): swsssdk.interface.DBInterface._subscribe_keyspace_notification = _subscribe_keyspace_notification mockredis.MockRedis.config_set = config_set redis.StrictRedis = SwssSyncClient +SonicV2Connector.connect = connect_SonicV2Connector diff --git a/tests/mock_tables/mock_multi_asic.py b/tests/mock_tables/mock_multi_asic.py new file mode 100644 index 0000000000..79c8ebda1c --- /dev/null +++ b/tests/mock_tables/mock_multi_asic.py @@ -0,0 +1,20 @@ +# MONKEY PATCH!!! +import mock +from sonic_py_common import multi_asic + + +def mock_get_num_asics(): + return 2 + + +def mock_is_multi_asic(): + return True + + +def mock_get_namespace_list(namespace=None): + return ['asic0', 'asic1'] + + +multi_asic.get_num_asics = mock_get_num_asics +multi_asic.is_multi_asic = mock_is_multi_asic +multi_asic.get_namespace_list = mock_get_namespace_list diff --git a/tests/multi_asic_intfutil_test.py b/tests/multi_asic_intfutil_test.py new file mode 100644 index 0000000000..722ed92957 --- /dev/null +++ b/tests/multi_asic_intfutil_test.py @@ -0,0 +1,182 @@ +import os +import subprocess + +from click.testing import CliRunner + +root_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(root_path) +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 +""" +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 +""" + +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 +""" + +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 +""" +intf_description = """\ + Interface Oper Admin Alias Description +----------- ------ ------- ----------- ------------------------ + Ethernet0 up up Ethernet1/1 ARISTA01T2:Ethernet3/1/1 + Ethernet4 up up Ethernet1/2 ARISTA01T2:Ethernet3/2/1 +""" + +intf_description_all = """\ + Interface Oper Admin Alias Description +-------------- ------ ------- -------------- ------------------------ + Ethernet0 up up Ethernet1/1 ARISTA01T2:Ethernet3/1/1 + Ethernet4 up up Ethernet1/2 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 +Ethernet-BP260 up up Ethernet-BP260 ASIC0:Eth17-ASIC0 +""" + +intf_description_asic0 = """\ + Interface Oper Admin Alias Description +----------- ------ ------- ----------- ------------------------ + Ethernet0 up up Ethernet1/1 ARISTA01T2:Ethernet3/1/1 + Ethernet4 up up Ethernet1/2 ARISTA01T2:Ethernet3/2/1 +""" + +intf_description_asic0_all = """\ + Interface Oper Admin Alias Description +------------ ------ ------- ------------ ------------------------ + Ethernet0 up up Ethernet1/1 ARISTA01T2:Ethernet3/1/1 + Ethernet4 up up Ethernet1/2 ARISTA01T2:Ethernet3/2/1 +Ethernet-BP0 up up Ethernet-BP0 ASIC1:Eth0-ASIC1 +Ethernet-BP4 up up Ethernet-BP4 ASIC1:Eth1-ASIC1 +""" + +intf_invalid_asic_error="""ValueError: Unknown Namespace asic99""" + +class TestInterfacesMultiAsic(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + + def setUp(self): + self.runner = CliRunner() + + def get_result_and_return_code(self, cmd): + return_code = 0 + try: + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + return_code = e.returncode + #store only the error, no need for the traceback + output = e.output.strip().split("\n")[-1] + + return(return_code, output) + + def test_multi_asic_interface_status_all(self): + return_code, result = self.get_result_and_return_code( 'intfutil -c status -d all') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_status_all + + def test_multi_asic_interface_status(self): + return_code, result = self.get_result_and_return_code('intfutil -c status') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_status + + def test_multi_asic_interface_status_asic0_all(self): + return_code, result = self.get_result_and_return_code('intfutil -c status -n asic0 -d all') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_status_asic0_all + + def test_multi_asic_interface_status_asic0(self): + return_code, result = self.get_result_and_return_code('intfutil -c status -n asic0') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_status_asic0 + + def test_multi_asic_interface_desc(self): + return_code, result = self.get_result_and_return_code('intfutil -c description') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_description + + def test_multi_asic_interface_desc_all(self): + return_code, result = self.get_result_and_return_code( 'intfutil -c description -d all') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_description_all + + def test_multi_asic_interface_asic0(self): + return_code, result = self.get_result_and_return_code( 'intfutil -c description -n asic0') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_description_asic0 + + def test_multi_asic_interface_desc_asic0_all(self): + return_code, result = self.get_result_and_return_code('intfutil -c description -n asic0 -d all') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_description_asic0_all + + def test_invalid_asic_name(self): + return_code, result = self.get_result_and_return_code('intfutil -c description -n asic99 -d all') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 1 + assert result == intf_invalid_asic_error + + def test_invalid_asic_name(self): + return_code, result = self.get_result_and_return_code('intfutil -c status -n asic99') + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 1 + assert result == intf_invalid_asic_error + + 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" diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 912cfeb596..3b45ee92e5 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -8,8 +8,7 @@ from natsort import natsorted from utilities_common.db import Db - -from swsssdk import ConfigDBConnector +from sonic_py_common import multi_asic VLAN_SUB_INTERFACE_SEPARATOR = '.' @@ -122,13 +121,12 @@ class InterfaceAliasConverter(object): def __init__(self, db=None): if db is None: - self.config_db = ConfigDBConnector() - self.config_db.connect() + self.port_dict = multi_asic.get_port_table() else: self.config_db = db.cfgdb - + self.port_dict = self.config_db.get_table('PORT') self.alias_max_length = 0 - self.port_dict = self.config_db.get_table('PORT') + if not self.port_dict: click.echo(message="Warning: failed to retrieve PORT table from ConfigDB!", err=True)