From 7497c3f54f4b0b50c17e03fe5704b5b75ead06b7 Mon Sep 17 00:00:00 2001 From: cchoate Date: Wed, 15 Feb 2023 11:45:32 -0800 Subject: [PATCH 1/4] sonic-utilities: Create SONiC ISIS show commands * Update show/main.py Include "isis" as a cli group option for show commands * Create show/isis_frr.py Add "isis" as a cli group option for show commands and add support for "show isis neighbors", "show isis database", "show isis hostname", and "show isis interface" Signed-off-by: cchoate54@gmail.com --- show/isis_frr.py | 119 ++++ show/main.py | 2 + tests/conftest.py | 38 ++ tests/isis_frr_input/__init__.py | 0 tests/isis_frr_input/isis_frr_test_vector.py | 557 +++++++++++++++++++ tests/isis_frr_test.py | 127 +++++ 6 files changed, 843 insertions(+) create mode 100644 show/isis_frr.py create mode 100644 tests/isis_frr_input/__init__.py create mode 100644 tests/isis_frr_input/isis_frr_test_vector.py create mode 100644 tests/isis_frr_test.py diff --git a/show/isis_frr.py b/show/isis_frr.py new file mode 100644 index 0000000000..1b55381100 --- /dev/null +++ b/show/isis_frr.py @@ -0,0 +1,119 @@ +import click + +import utilities_common.bgp_util as bgp_util +import utilities_common.cli as clicommon +from natsort import natsorted +from swsscommon.swsscommon import ConfigDBConnector + + +############################################################################### +# +# 'show isis' cli stanza +# +###############################################################################  + +def get_interfaces(): + """Get list of interfaces + """ + tables = ['LOOPBACK_INTERFACE', 'PORT', 'PORTCHANNEL'] + data = [] + + config_db = ConfigDBConnector() + config_db.connect() + + for table_name in tables: + interface_list = list(config_db.get_table(table_name).keys()) + interface_list = [x for x in interface_list if isinstance(x, str)] + data += natsorted(interface_list) + return data + +INTERFACE_LIST = get_interfaces() + +def invalid_arg(input:str): + if input == '?': + ctx = click.get_current_context() + ctx.fail('The argument: "{}" is invalid. Try "-?".'.format(input)) + +@click.group(cls=clicommon.AliasedGroup, name="isis") +def isis(): + """Show ISIS (Intermediate System to Intermediate System) information""" + pass + + +# 'neighbors' subcommand ("show isis neighbors") +@isis.command() +@click.argument('system_id', required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def neighbors(system_id, verbose): + """Show ISIS neighbors""" + + command = 'show isis neighbor' + if system_id is not None: + invalid_arg(system_id) + command += ' {}'.format(system_id) + elif verbose: + command += ' detail' + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) + +# 'database' subcommand ("show isis database") +@isis.command() +@click.argument('lsp_id', required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def database(lsp_id, verbose): + """Show ISIS database""" + + command = 'show isis database' + if verbose: + command += ' detail' + if lsp_id is not None: + invalid_arg(lsp_id) + command += ' {0}'.format(lsp_id) + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) + +# 'hostname' subcommand ("show isis hostname") +@isis.command() +def hostname(): + """Show ISIS hostname""" + + command = 'show isis hostname' + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) + +# 'interface' subcommand ("show isis interface") +@isis.command() +@click.argument('interface', + metavar='[INTERFACE]', + type=click.Choice( + INTERFACE_LIST), + required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@click.option('--display', is_flag=True, help=f"Display available [INTERFACE] options:\n{INTERFACE_LIST}") +def interface(interface, verbose, display): + """Show ISIS interface""" + + if display: + d = f"[INTERFACE] options: {INTERFACE_LIST}" + click.echo(d) + + command = 'show isis interface' + + if interface is not None: + command += ' {0}'.format(interface) + elif verbose: + command += ' detail' + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) diff --git a/show/main.py b/show/main.py index a60e8411c2..eaa17e16ee 100755 --- a/show/main.py +++ b/show/main.py @@ -48,6 +48,7 @@ from . import flow_counters from . import gearbox from . import interfaces +from . import isis_frr from . import kdump from . import kube from . import muxcable @@ -276,6 +277,7 @@ def cli(ctx): cli.add_command(flow_counters.flowcnt_trap) cli.add_command(kdump.kdump) cli.add_command(interfaces.interfaces) +cli.add_command(isis_frr.isis) cli.add_command(kdump.kdump) cli.add_command(kube.kubernetes) cli.add_command(muxcable.muxcable) diff --git a/tests/conftest.py b/tests/conftest.py index b6b454ba09..f84d111117 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,12 @@ mock_show_bgp_network_single_asic, mock_show_bgp_network_multi_asic ) +from .isis_frr_input.isis_frr_test_vector import( + mock_show_isis_neighbors, + mock_show_isis_database, + mock_show_isis_hostname, + mock_show_isis_interface + ) from . import config_int_ip_common import utilities_common.constants as constants import config.main as config @@ -367,3 +373,35 @@ def mock_restart_dhcp_relay_service(): yield config.vlan.dhcp_relay_util.handle_restart_dhcp_relay_service = origin_func + +@pytest.fixture +def setup_single_isis_instance(request): + import utilities_common.bgp_util as bgp_util + + _old_run_bgp_command = bgp_util.run_bgp_command + + if request.param.startswith('isis_neighbor') or \ + request.param.startswith('isis_neighbor'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_neighbors(request)) + elif request.param.startswith('isis_database') or \ + request.param.startswith('isis_database'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_database(request)) + elif request.param.startswith('isis_hostname') or \ + request.param.startswith('isis_hostname'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_hostname(request)) + elif request.param.startswith('isis_interface') or \ + request.param.startswith('isis_interface'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_interface(request)) + yield + + bgp_util.run_bgp_command = _old_run_bgp_command + + +@pytest.fixture +def setup_isis_commands(): + import show.main as show + return show diff --git a/tests/isis_frr_input/__init__.py b/tests/isis_frr_input/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/isis_frr_input/isis_frr_test_vector.py b/tests/isis_frr_input/isis_frr_test_vector.py new file mode 100644 index 0000000000..24009b1379 --- /dev/null +++ b/tests/isis_frr_input/isis_frr_test_vector.py @@ -0,0 +1,557 @@ +isis_neighbors_output = \ +""" +Area 1: + System Id Interface L State Holdtime SNPA +sonic PortChannel01202 Up 25 2020.2020.2020 +sonic2 PortChannel01212 Up 25 2020.2020.2020 +""" + +isis_neighbors_system_id_output = \ +""" +Area 1: +sonic + Interface: PortChannel0120, Level: 2, State: Up, Expires in 24s + Adjacency flaps: 1, Last: 4d23h33m44s ago + Circuit type: L2, Speaks: IPv4, IPv6 + SNPA: 2020.2020.2020 + Area Address(es): + 49.0001 + IPv4 Address(es): + 104.44.20.172 + IPv6 Address(es): + fe80::2e6b:f5ff:fee7:9c9 + Global IPv6 Address(es): + 2603:1060:0:12::f0dd +""" + +isis_neighbors_hostname_output = \ +""" +Area 1: +sonic + Interface: PortChannel0120, Level: 2, State: Up, Expires in 24s + Adjacency flaps: 1, Last: 4d23h33m44s ago + Circuit type: L2, Speaks: IPv4, IPv6 + SNPA: 2020.2020.2020 + Area Address(es): + 49.0001 + IPv4 Address(es): + 104.44.20.172 + IPv6 Address(es): + fe80::2e6b:f5ff:fee7:9c9 + Global IPv6 Address(es): + 2603:1060:0:12::f0dd +""" + +isis_neighbors_verbose_output = \ +""" +Area 1: +sonic + Interface: PortChannel0120, Level: 2, State: Up, Expires in 24s + Adjacency flaps: 1, Last: 4d23h33m44s ago + Circuit type: L2, Speaks: IPv4, IPv6 + SNPA: 2020.2020.2020 + Area Address(es): + 49.0001 + IPv4 Address(es): + 104.44.20.172 + IPv6 Address(es): + fe80::2e6b:f5ff:fee7:9c9 + Global IPv6 Address(es): + 2603:1060:0:12::f0dd +sonic2 + Interface: PortChannel0121, Level: 2, State: Up, Expires in 24s + Adjacency flaps: 1, Last: 4d23h33m44s ago + Circuit type: L2, Speaks: IPv4, IPv6 + SNPA: 2020.2020.2020 + Area Address(es): + 49.0001 + IPv4 Address(es): + 104.44.20.182 + IPv6 Address(es): + fe80::2e6b:f5ff:fee7:9d4 + Global IPv6 Address(es): + 2603:1060:0:12::f0e8 +""" + +isis_neighbors_invalid_hostname_output = \ +"""Invalid system id sonic3 +""" + +isis_neighbors_invalid_system_id_output = \ +"""Invalid system id 10.10.10 +""" + +isis_neighbors_invalid_help_output = \ +"""Usage: neighbors [OPTIONS] [SYSTEM_ID] +Try "neighbors --help" for help. + +Error: The argument: "?" is invalid. Try "-?". +""" + +def mock_show_isis_neighbors(request): + if request.param == 'isis_neighbors_output': + return isis_neighbors_output + elif request.param == 'isis_neighbors_system_id_output': + return isis_neighbors_system_id_output + elif request.param == 'isis_neighbors_hostname_output': + return isis_neighbors_hostname_output + elif request.param == 'isis_neighbors_verbose_output': + return isis_neighbors_verbose_output + elif request.param == 'isis_neighbors_invalid_hostname_output': + return isis_neighbors_invalid_hostname_output + elif request.param == 'isis_neighbors_invalid_system_id_output': + return isis_neighbors_invalid_system_id_output + elif request.param == 'isis_neighbors_invalid_help_output': + return isis_neighbors_invalid_help_output + else: + return "" + + +isis_database_output = \ +"""Area 1: +IS-IS Level-2 link-state database: +LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL +sonic1.00-00 1284 0x0000020e 0x3d7e 48072 0/0/0 +sonic1.00-01 197 0x00000136 0x4474 64797 0/0/0 +sonic2.00-00 1192 0x000001ae 0xd970 47837 0/0/0 +sonic2.00-01 367 0x00000136 0xe315 31986 0/0/0 +sonic3.00-00 1319 0x000001a9 0x3349 47881 0/0/0 +sonic3.00-00 1115 0x000002e7 0x1b38 54629 0/0/0 + 6 LSPs +""" + +isis_database_lsp_id_output = \ +"""Area 1: +IS-IS Level-2 link-state database: +LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL +sonic1.00-00 1284 0x0000020e 0x3d7e 48072 0/0/0 +""" + +isis_database_verbose_output = \ +"""Area 1: +IS-IS Level-2 link-state database: +LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL +sonic1.00-00 1284 0x0000020e 0x3d7e 47678 0/0/0 + Protocols Supported: IPv4, IPv6 + Area Address: 39.752f.0100.0014.0000.b000.1000 + Hostname: sonic1 + TE Router ID: 104.44.1.146 + Router Capability: 104.44.1.146 , D:0, S:0 + Segment Routing: I:1 V:1, Global Block Base: 16000 Range: 8000 + SR Local Block Base: 15000 Range: 1000 + SR Algorithm: + 0: SPF + 1: Strict SPF + Node Maximum SID Depth: 10 + Extended Reachability: 1040.4400.1056.00 (Metric: 5000) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.19.218 + Remote Interface IP Address(es): 104.44.19.219 + Local Interface IPv6 Address(es): 2603:1060:0:10::f36d + Remote Interface IPv6 Address(es): 2603:1060:0:10::f36e + Maximum Bandwidth: 1.125e+09 (Bytes/sec) + Maximum Reservable Bandwidth: 5.625e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 5.625e+08 (Bytes/sec), [1]: 5.625e+08 (Bytes/sec) + [2]: 5.625e+08 (Bytes/sec), [3]: 5.625e+08 (Bytes/sec) + [4]: 5.625e+08 (Bytes/sec), [5]: 5.625e+08 (Bytes/sec) + [6]: 5.625e+08 (Bytes/sec), [7]: 5.625e+08 (Bytes/sec) + Traffic Engineering Metric: 5000 + Adjacency-SID: 100006, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100008, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + Extended Reachability: 1040.4400.1145.00 (Metric: 500) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.28.152 + Remote Interface IP Address(es): 104.44.28.153 + Local Interface IPv6 Address(es): 2603:1060:0:10::f309 + Remote Interface IPv6 Address(es): 2603:1060:0:10::f30a + Maximum Bandwidth: 1.5e+09 (Bytes/sec) + Maximum Reservable Bandwidth: 7.5e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 7.5e+08 (Bytes/sec), [1]: 7.5e+08 (Bytes/sec) + [2]: 7.5e+08 (Bytes/sec), [3]: 7.5e+08 (Bytes/sec) + [4]: 7.5e+08 (Bytes/sec), [5]: 7.5e+08 (Bytes/sec) + [6]: 7.5e+08 (Bytes/sec), [7]: 7.5e+08 (Bytes/sec) + Traffic Engineering Metric: 500 + Adjacency-SID: 100026, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100028, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + Extended Reachability: 1040.4400.1056.00 (Metric: 5000) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.18.252 + Remote Interface IP Address(es): 104.44.18.253 + Local Interface IPv6 Address(es): 2603:1060:0:10::f2a1 + Remote Interface IPv6 Address(es): 2603:1060:0:10::f2a2 + Maximum Bandwidth: 7.5e+08 (Bytes/sec) + Maximum Reservable Bandwidth: 3.75e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 3.75e+08 (Bytes/sec), [1]: 3.75e+08 (Bytes/sec) + [2]: 3.75e+08 (Bytes/sec), [3]: 3.75e+08 (Bytes/sec) + [4]: 3.75e+08 (Bytes/sec), [5]: 3.75e+08 (Bytes/sec) + [6]: 3.75e+08 (Bytes/sec), [7]: 3.75e+08 (Bytes/sec) + Traffic Engineering Metric: 5000 + Adjacency-SID: 100002, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100004, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + IPv4 Interface Address: 104.44.1.146 + IPv6 Interface Address: 2603:1060:0:1::ff3e + Extended IP Reachability: 104.44.1.146/32 (Metric: 0) + Subtlvs: + SR Prefix-SID Index: 2025, Algorithm: 0, Flags: NODE PHP + Extended IP Reachability: 104.44.7.40/31 (Metric: 20000) + Extended IP Reachability: 104.44.17.0/31 (Metric: 100000) + Extended IP Reachability: 104.44.18.174/31 (Metric: 500) + Extended IP Reachability: 104.44.18.252/31 (Metric: 5000) + Extended IP Reachability: 104.44.19.218/31 (Metric: 5000) + Extended IP Reachability: 104.44.28.152/31 (Metric: 500) + Extended IP Reachability: 104.44.33.174/31 (Metric: 500) + Extended IP Reachability: 104.44.33.184/31 (Metric: 500) + Extended IP Reachability: 104.44.29.12/31 (Metric: 10000) + IPv6 Reachability: 2603:1060:0:1::ff3e/128 (Metric: 0) + IPv6 Reachability: 2603:1060:0:10::f000/126 (Metric: 20000) + IPv6 Reachability: 2603:1060:0:10::f2a0/126 (Metric: 5000) + IPv6 Reachability: 2603:1060:0:10::f2cc/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f308/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f35c/126 (Metric: 100000) + IPv6 Reachability: 2603:1060:0:10::f36c/126 (Metric: 5000) + IPv6 Reachability: 2603:1060:0:12::f4d4/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:12::f4e8/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f3d4/126 (Metric: 10000) + +....... + + 6 LSPs +""" + +isis_database_lsp_id_verbose_output = \ +"""Area 1: +IS-IS Level-2 link-state database: +LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL +sonic1.00-00 1284 0x0000020e 0x3d7e 47678 0/0/0 + Protocols Supported: IPv4, IPv6 + Area Address: 39.752f.0100.0014.0000.b000.1000 + Hostname: sonic1 + TE Router ID: 104.44.1.146 + Router Capability: 104.44.1.146 , D:0, S:0 + Segment Routing: I:1 V:1, Global Block Base: 16000 Range: 8000 + SR Local Block Base: 15000 Range: 1000 + SR Algorithm: + 0: SPF + 1: Strict SPF + Node Maximum SID Depth: 10 + Extended Reachability: 1040.4400.1056.00 (Metric: 5000) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.19.218 + Remote Interface IP Address(es): 104.44.19.219 + Local Interface IPv6 Address(es): 2603:1060:0:10::f36d + Remote Interface IPv6 Address(es): 2603:1060:0:10::f36e + Maximum Bandwidth: 1.125e+09 (Bytes/sec) + Maximum Reservable Bandwidth: 5.625e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 5.625e+08 (Bytes/sec), [1]: 5.625e+08 (Bytes/sec) + [2]: 5.625e+08 (Bytes/sec), [3]: 5.625e+08 (Bytes/sec) + [4]: 5.625e+08 (Bytes/sec), [5]: 5.625e+08 (Bytes/sec) + [6]: 5.625e+08 (Bytes/sec), [7]: 5.625e+08 (Bytes/sec) + Traffic Engineering Metric: 5000 + Adjacency-SID: 100006, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100008, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + Extended Reachability: 1040.4400.1145.00 (Metric: 500) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.28.152 + Remote Interface IP Address(es): 104.44.28.153 + Local Interface IPv6 Address(es): 2603:1060:0:10::f309 + Remote Interface IPv6 Address(es): 2603:1060:0:10::f30a + Maximum Bandwidth: 1.5e+09 (Bytes/sec) + Maximum Reservable Bandwidth: 7.5e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 7.5e+08 (Bytes/sec), [1]: 7.5e+08 (Bytes/sec) + [2]: 7.5e+08 (Bytes/sec), [3]: 7.5e+08 (Bytes/sec) + [4]: 7.5e+08 (Bytes/sec), [5]: 7.5e+08 (Bytes/sec) + [6]: 7.5e+08 (Bytes/sec), [7]: 7.5e+08 (Bytes/sec) + Traffic Engineering Metric: 500 + Adjacency-SID: 100026, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100028, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + Extended Reachability: 1040.4400.1056.00 (Metric: 5000) + Administrative Group: 0x10002 + Local Interface IP Address(es): 104.44.18.252 + Remote Interface IP Address(es): 104.44.18.253 + Local Interface IPv6 Address(es): 2603:1060:0:10::f2a1 + Remote Interface IPv6 Address(es): 2603:1060:0:10::f2a2 + Maximum Bandwidth: 7.5e+08 (Bytes/sec) + Maximum Reservable Bandwidth: 3.75e+08 (Bytes/sec) + Unreserved Bandwidth: + [0]: 3.75e+08 (Bytes/sec), [1]: 3.75e+08 (Bytes/sec) + [2]: 3.75e+08 (Bytes/sec), [3]: 3.75e+08 (Bytes/sec) + [4]: 3.75e+08 (Bytes/sec), [5]: 3.75e+08 (Bytes/sec) + [6]: 3.75e+08 (Bytes/sec), [7]: 3.75e+08 (Bytes/sec) + Traffic Engineering Metric: 5000 + Adjacency-SID: 100002, Weight: 0, Flags: F:0 B:0, V:1, L:1, S:0, P:0 + Adjacency-SID: 100004, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0 + IPv4 Interface Address: 104.44.1.146 + IPv6 Interface Address: 2603:1060:0:1::ff3e + Extended IP Reachability: 104.44.1.146/32 (Metric: 0) + Subtlvs: + SR Prefix-SID Index: 2025, Algorithm: 0, Flags: NODE PHP + Extended IP Reachability: 104.44.7.40/31 (Metric: 20000) + Extended IP Reachability: 104.44.17.0/31 (Metric: 100000) + Extended IP Reachability: 104.44.18.174/31 (Metric: 500) + Extended IP Reachability: 104.44.18.252/31 (Metric: 5000) + Extended IP Reachability: 104.44.19.218/31 (Metric: 5000) + Extended IP Reachability: 104.44.28.152/31 (Metric: 500) + Extended IP Reachability: 104.44.33.174/31 (Metric: 500) + Extended IP Reachability: 104.44.33.184/31 (Metric: 500) + Extended IP Reachability: 104.44.29.12/31 (Metric: 10000) + IPv6 Reachability: 2603:1060:0:1::ff3e/128 (Metric: 0) + IPv6 Reachability: 2603:1060:0:10::f000/126 (Metric: 20000) + IPv6 Reachability: 2603:1060:0:10::f2a0/126 (Metric: 5000) + IPv6 Reachability: 2603:1060:0:10::f2cc/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f308/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f35c/126 (Metric: 100000) + IPv6 Reachability: 2603:1060:0:10::f36c/126 (Metric: 5000) + IPv6 Reachability: 2603:1060:0:12::f4d4/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:12::f4e8/126 (Metric: 500) + IPv6 Reachability: 2603:1060:0:10::f3d4/126 (Metric: 10000) +""" + +isis_database_unknown_lsp_id_output = \ +"""Area 1: +""" + +isis_database_invalid_help_output = \ +"""Usage: database [OPTIONS] [LSP_ID] +Try "database --help" for help. + +Error: The argument: "?" is invalid. Try "-?". +""" + +def mock_show_isis_database(request): + if request.param == 'isis_database_output': + return isis_database_output + elif request.param == 'isis_database_lsp_id_output': + return isis_database_lsp_id_output + elif request.param == 'isis_database_verbose_output': + return isis_database_verbose_output + elif request.param == 'isis_database_lsp_id_verbose_output': + return isis_database_lsp_id_verbose_output + elif request.param == 'isis_database_unknown_lsp_id_output': + return isis_database_unknown_lsp_id_output + elif request.param == 'isis_database_invalid_help_output': + return isis_database_invalid_help_output + else: + return "" + +isis_hostname_output = \ +"""vrf : default +Level System ID Dynamic Hostname +2 1000.2000.4000 sonic2 + * 1000.2000.3000 sonic +""" + +isis_hostname_invalid_help_output = \ +"""Usage: hostname [OPTIONS] +Try "hostname --help" for help. + +Error: Got unexpected extra argument (?) +""" + + +def mock_show_isis_hostname(request): + if request.param == 'isis_hostname_output': + return isis_hostname_output + elif request.param == 'isis_hostname_invalid_help_output': + return isis_hostname_invalid_help_output + else: + return "" + +isis_interface_output = \ +"""Area 1: + Interface CircId State Type Level + PortChannel01200x0 Up p2p L2 +""" + +isis_interface_invalid_help_output = \ +"""Usage: interface [OPTIONS] [INTERFACE] +Try "interface --help" for help. + +Error: The argument: "?" is invalid. Try "-?". +""" + +isis_interface_ifname_output = \ +"""Area 1: + Interface: PortChannel0002, State: Up, Active, Circuit Id: 0x0 + Type: p2p, Level: L2 + Level-2 Information: + Metric: 10, Active neighbors: 1 + Hello interval: 3, Holddown count: 10 (no-pad) + CNSP interval: 10, PSNP interval: 2 + IP Prefix(es): + 104.44.20.173/31 + IPv6 Link-Locals: + fe80::5054:8ff:fe6c:8b47/64 +""" + +isis_interface_ifname_output = \ +"""Area 1: + Interface: PortChannel0002, State: Up, Active, Circuit Id: 0x0 + Type: p2p, Level: L2 + Level-2 Information: + Metric: 10, Active neighbors: 1 + Hello interval: 3, Holddown count: 10 (no-pad) + CNSP interval: 10, PSNP interval: 2 + IP Prefix(es): + 104.44.20.173/31 + IPv6 Link-Locals: + fe80::5054:8ff:fe6c:8b47/64 +""" + +isis_interface_verbose_output = \ +"""Area 1: + Interface: PortChannel0002, State: Up, Active, Circuit Id: 0x0 + Type: p2p, Level: L2 + Level-2 Information: + Metric: 10, Active neighbors: 1 + Hello interval: 3, Holddown count: 10 (no-pad) + CNSP interval: 10, PSNP interval: 2 + IP Prefix(es): + 104.44.20.175/31 + IPv6 Link-Locals: + fe80::5054:8ff:fe6c:8b49/64 + Interface: PortChannel0121, State: Up, Active, Circuit Id: 0x0 + Type: p2p, Level: L2 + Level-2 Information: + Metric: 10, Active neighbors: 1 + Hello interval: 3, Holddown count: 10 (no-pad) + CNSP interval: 10, PSNP interval: 2 + IP Prefix(es): + 104.44.20.175/31 + IPv6 Link-Locals: + fe80::5054:8ff:fe6c:8b49/64 +""" + +isis_interface_unknown_ifname_output = \ +'Usage: interface [OPTIONS] [INTERFACE]\n' \ +'Try "interface --help" for help.\n\n' \ +'Error: Invalid value for "[INTERFACE]": invalid choice: sonic1. ' \ +'(choose from Ethernet0, Ethernet4, Ethernet8, Ethernet12, Ethernet16, ' \ +'Ethernet20, Ethernet24, Ethernet28, Ethernet32, Ethernet36, Ethernet40, ' \ +'Ethernet44, Ethernet48, Ethernet52, Ethernet56, Ethernet60, Ethernet64, ' \ +'Ethernet68, Ethernet72, Ethernet76, Ethernet80, Ethernet84, Ethernet88, ' \ +'Ethernet92, Ethernet96, Ethernet100, Ethernet104, Ethernet108, Ethernet112, ' \ +'Ethernet116, Ethernet120, Ethernet124, PortChannel0001, PortChannel0002, ' \ +'PortChannel0003, PortChannel0004, PortChannel1001)\n' + +def mock_show_isis_interface(request): + if request.param == 'isis_interface_output': + return isis_interface_output + elif request.param == 'isis_interface_ifname_output': + return isis_interface_ifname_output + elif request.param == 'isis_interface_verbose_output': + return isis_interface_verbose_output + elif request.param == 'isis_interface_ifname_verbose_output': + return isis_interface_ifname_output + elif request.param == 'isis_interface_unknown_ifname_output': + return isis_interface_unknown_ifname_output + else: + return "" + + +testData = { + 'isis_neighbors': { + 'args': [], + 'rc': 0, + 'rc_output': isis_neighbors_output + }, + 'isis_neighbors_system_id': { + 'args': ['1000.2000.3000'], + 'rc': 0, + 'rc_output': isis_neighbors_system_id_output + }, + 'isis_neighbors_hostname': { + 'args': ['sonic'], + 'rc': 0, + 'rc_output': isis_neighbors_hostname_output + }, + 'isis_neighbors_verbose': { + 'args': ['--verbose'], + 'rc': 0, + 'rc_output': isis_neighbors_verbose_output + }, + 'isis_neighbors_invalid_hostname': { + 'args': ['sonic3'], + 'rc': 0, + 'rc_output': isis_neighbors_invalid_hostname_output + }, + 'isis_neighbors_invalid_system_id': { + 'args': ['10.10.10'], + 'rc': 0, + 'rc_output': isis_neighbors_invalid_system_id_output + }, + 'isis_neighbors_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': isis_neighbors_invalid_help_output + }, + 'isis_database': { + 'args': [], + 'rc': 0, + 'rc_output': isis_database_output + }, + 'isis_database_lsp_id': { + 'args': ['sonic1.00-00'], + 'rc': 0, + 'rc_output': isis_database_lsp_id_output + }, + 'isis_database_verbose': { + 'args': ['--verbose'], + 'rc': 0, + 'rc_output': isis_database_verbose_output + }, + 'isis_database_lsp_id_verbose': { + 'args': ['sonic1.00-00', '--verbose'], + 'rc': 0, + 'rc_output': isis_database_lsp_id_verbose_output + }, + 'isis_database_unknown_lsp_id': { + 'args': ['sonic1'], + 'rc': 0, + 'rc_output': isis_database_unknown_lsp_id_output + }, + 'isis_database_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': isis_database_invalid_help_output + }, + 'isis_hostname': { + 'args': [], + 'rc': 0, + 'rc_output': isis_hostname_output + }, + 'isis_hostname_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': isis_hostname_invalid_help_output + }, + 'isis_interface': { + 'args': [], + 'rc': 0, + 'rc_output': isis_interface_output + }, + 'isis_interface_ifname': { + 'args': ['PortChannel0002'], + 'rc': 0, + 'rc_output': isis_interface_ifname_output + }, + 'isis_interface_verbose': { + 'args': ['--verbose'], + 'rc': 0, + 'rc_output': isis_interface_verbose_output + }, + 'isis_interface_ifname_verbose': { + 'args': ['PortChannel0002', '--verbose'], + 'rc': 0, + 'rc_output': isis_interface_ifname_output + }, + 'isis_interface_unknown_ifname': { + 'args': ['sonic1'], + 'rc': 2, + 'rc_output': isis_interface_unknown_ifname_output + }, +} diff --git a/tests/isis_frr_test.py b/tests/isis_frr_test.py new file mode 100644 index 0000000000..946cdc2580 --- /dev/null +++ b/tests/isis_frr_test.py @@ -0,0 +1,127 @@ +import pytest + +from click.testing import CliRunner +import show.main as show +from .isis_frr_input.isis_frr_test_vector import * + + +def executor(test_vector, show, exec_cmd): + runner = CliRunner() + input = testData[test_vector] + + result = runner.invoke(exec_cmd, input['args']) + + print(result.exit_code) + print(result.output) + + if input['rc'] == 0: + assert result.exit_code == 0 + else: + assert result.exit_code == input['rc'] + + if 'rc_err_msg' in input: + output = result.output.strip().split("\n")[-1] + assert input['rc_err_msg'] == output + + if 'rc_output' in input: + assert result.output == input['rc_output'] + + if 'rc_warning_msg' in input: + output = result.output.strip().split("\n")[0] + assert input['rc_warning_msg'] in output + + +class TestIsisNeighbors(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_neighbors_output', 'isis_neighbors'), + ('isis_neighbors_system_id_output', 'isis_neighbors_system_id'), + ('isis_neighbors_hostname_output', 'isis_neighbors_hostname'), + ('isis_neighbors_verbose_output', 'isis_neighbors_verbose'), + ('isis_neighbors_invalid_hostname_output', 'isis_neighbors_invalid_hostname'), + ('isis_neighbors_invalid_system_id_output', 'isis_neighbors_invalid_system_id'), + ('isis_neighbors_invalid_help_output', 'isis_neighbors_invalid_help') + ], + indirect=['setup_single_isis_instance']) + def test_isis_neighbors(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["neighbors"] + executor(test_vector, show, exec_cmd) + + +class TestIsisDatabase(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_database_output', 'isis_database'), + ('isis_database_lsp_id_output', 'isis_database_lsp_id'), + ('isis_database_verbose_output', 'isis_database_verbose'), + ('isis_database_lsp_id_verbose_output', 'isis_database_lsp_id_verbose'), + ('isis_database_unknown_lsp_id_output', 'isis_database_unknown_lsp_id'), + ('isis_database_invalid_help_output', 'isis_database_invalid_help') + ], + indirect=['setup_single_isis_instance']) + def test_isis_database(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["database"] + executor(test_vector, show, exec_cmd) + + +class TestIsisHostname(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_hostname_output', 'isis_hostname'), + ('isis_hostname_invalid_help_output', 'isis_hostname_invalid_help') + ], + indirect=['setup_single_isis_instance']) + def test_bgp_neighbors(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["hostname"] + executor(test_vector, show, exec_cmd) + + +class TestIsisInterface(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_interface_output', 'isis_interface'), + ('isis_interface_ifname_output', 'isis_interface_ifname'), + ('isis_interface_verbose_output', 'isis_interface_verbose'), + ('isis_interface_ifname_verbose_output', 'isis_interface_ifname_verbose'), + ('isis_interface_unknown_ifname_output', 'isis_interface_unknown_ifname'), + ], + indirect=['setup_single_isis_instance']) + def test_isis_interface(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["interface"] + executor(test_vector, show, exec_cmd) From 13d69df7340952eebce19b8aabd1238540019c0c Mon Sep 17 00:00:00 2001 From: Isabella de Leon Date: Mon, 6 Mar 2023 16:39:37 -0800 Subject: [PATCH 2/4] [show/tests] Add support for "show isis topology" Signed-off-by: ideleon@microsoft.com --- show/isis_frr.py | 19 +++++ tests/conftest.py | 9 ++- tests/isis_frr_input/isis_frr_test_vector.py | 76 ++++++++++++++++++++ tests/isis_frr_test.py | 23 ++++++ 4 files changed, 125 insertions(+), 2 deletions(-) diff --git a/show/isis_frr.py b/show/isis_frr.py index 1b55381100..6bd9f2b0ff 100644 --- a/show/isis_frr.py +++ b/show/isis_frr.py @@ -117,3 +117,22 @@ def interface(interface, verbose, display): output += bgp_util.run_bgp_show_command(command) click.echo(output.rstrip('\n')) + +# 'topology' subcommand ("show isis topology") +@isis.command() +@click.option('--level-1', is_flag=True, help="Show IS-IS level-1 information") +@click.option('--level-2', is_flag=True, help="Show IS-IS level-2 information") +def topology(level_1, level_2): + """Show ISIS topology""" + + command = 'show isis topology' + + if level_1: + command += ' level-1' + elif level_2: + command += ' level-2' + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index f84d111117..ebe9702556 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,8 @@ mock_show_isis_neighbors, mock_show_isis_database, mock_show_isis_hostname, - mock_show_isis_interface + mock_show_isis_interface, + mock_show_isis_topology ) from . import config_int_ip_common import utilities_common.constants as constants @@ -395,7 +396,11 @@ def setup_single_isis_instance(request): elif request.param.startswith('isis_interface') or \ request.param.startswith('isis_interface'): bgp_util.run_bgp_command = mock.MagicMock( - return_value=mock_show_isis_interface(request)) + return_value=mock_show_isis_interface(request)) + elif request.param.startswith('isis_topology') or \ + request.param.startswith('isis_topology'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_topology(request)) yield bgp_util.run_bgp_command = _old_run_bgp_command diff --git a/tests/isis_frr_input/isis_frr_test_vector.py b/tests/isis_frr_input/isis_frr_test_vector.py index 24009b1379..3c43466323 100644 --- a/tests/isis_frr_input/isis_frr_test_vector.py +++ b/tests/isis_frr_input/isis_frr_test_vector.py @@ -452,6 +452,62 @@ def mock_show_isis_interface(request): else: return "" +isis_topology_output = \ +"""Area 1: +IS-IS paths to level-2 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +vlab-01 +10.0.0.56/31 IP internal 0 vlab-01(4) +10.1.0.32/32 IP internal 0 vlab-01(4) +ARISTA01T1 TE-IS 10 ARISTA01T1 PortChannel101 vlab-01(4) +10.0.0.56/31 IP TE 16777225 ARISTA01T1 PortChannel101 ARISTA01T1(4) + +IS-IS paths to level-2 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +vlab-01 +fc00::70/126 IP6 internal 0 vlab-01(4) +fc00:1::32/128 IP6 internal 0 vlab-01(4) +""" + +isis_topology_invalid_help_output = \ +"""Usage: topology [OPTIONS] +Try "topology --help" for help. + +Error: Got unexpected extra argument (?) +""" + +isis_topology_level_1_output = \ +"""Area 1: +""" + +isis_topology_level_2_output = \ +"""Area 1: +IS-IS paths to level-2 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +vlab-01 +10.0.0.56/31 IP internal 0 vlab-01(4) +10.1.0.32/32 IP internal 0 vlab-01(4) +ARISTA01T1 TE-IS 10 ARISTA01T1 PortChannel101 vlab-01(4) +10.0.0.56/31 IP TE 16777225 ARISTA01T1 PortChannel101 ARISTA01T1(4) + +IS-IS paths to level-2 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +vlab-01 +fc00::70/126 IP6 internal 0 vlab-01(4) +fc00:1::32/128 IP6 internal 0 vlab-01(4) +""" + +def mock_show_isis_topology(request): + if request.param == 'isis_topology_output': + return isis_topology_output + elif request.param == 'isis_topology_invalid_help_output': + return isis_topology_invalid_help_output + elif request.param == 'isis_topology_level_1_output': + return isis_topology_level_1_output + elif request.param == 'isis_topology_level_2_output': + return isis_topology_level_2_output + else: + return "" testData = { 'isis_neighbors': { @@ -554,4 +610,24 @@ def mock_show_isis_interface(request): 'rc': 2, 'rc_output': isis_interface_unknown_ifname_output }, + 'isis_topology': { + 'args': [], + 'rc': 0, + 'rc_output': isis_topology_output + }, + 'isis_topology_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': isis_topology_invalid_help_output + }, + 'isis_topology_level_1': { + 'args': ['--level-1'], + 'rc': 0, + 'rc_output': isis_topology_level_1_output + }, + 'isis_topology_level_2': { + 'args': ['--level-2'], + 'rc': 0, + 'rc_output': isis_topology_level_2_output + }, } diff --git a/tests/isis_frr_test.py b/tests/isis_frr_test.py index 946cdc2580..97382a67b5 100644 --- a/tests/isis_frr_test.py +++ b/tests/isis_frr_test.py @@ -125,3 +125,26 @@ def test_isis_interface(self, show = setup_isis_commands exec_cmd = show.cli.commands["isis"].commands["interface"] executor(test_vector, show, exec_cmd) + + +class TestIsisTopology(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_topology_output', 'isis_topology'), + ('isis_topology_invalid_help_output', 'isis_topology_invalid_help'), + ('isis_topology_level_1_output', 'isis_topology_level_1'), + ('isis_topology_level_2_output', 'isis_topology_level_2') + ], + indirect=['setup_single_isis_instance']) + def test_isis_topology(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["topology"] + executor(test_vector, show, exec_cmd) From 3665b943fcc097d9fd61483c42ed91ec32043fd8 Mon Sep 17 00:00:00 2001 From: Isabella de Leon Date: Tue, 4 Apr 2023 15:52:41 -0700 Subject: [PATCH 3/4] [show/tests] Add support for "show isis summary" Signed-off-by: ideleon@microsoft.com --- show/isis_frr.py | 13 ++++ tests/conftest.py | 8 ++- tests/isis_frr_input/isis_frr_test_vector.py | 75 ++++++++++++++++++++ tests/isis_frr_test.py | 21 ++++++ 4 files changed, 115 insertions(+), 2 deletions(-) diff --git a/show/isis_frr.py b/show/isis_frr.py index 1b55381100..2222895f65 100644 --- a/show/isis_frr.py +++ b/show/isis_frr.py @@ -117,3 +117,16 @@ def interface(interface, verbose, display): output += bgp_util.run_bgp_show_command(command) click.echo(output.rstrip('\n')) + + +# 'summary' subcommand ("show isis summary") +@isis.command() +def summary(): + """Show ISIS summary""" + + command = 'show isis summary' + + output = "" + output += bgp_util.run_bgp_show_command(command) + + click.echo(output.rstrip('\n')) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index f84d111117..1a861503f8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,8 @@ mock_show_isis_neighbors, mock_show_isis_database, mock_show_isis_hostname, - mock_show_isis_interface + mock_show_isis_interface, + mock_show_isis_summary ) from . import config_int_ip_common import utilities_common.constants as constants @@ -395,7 +396,10 @@ def setup_single_isis_instance(request): elif request.param.startswith('isis_interface') or \ request.param.startswith('isis_interface'): bgp_util.run_bgp_command = mock.MagicMock( - return_value=mock_show_isis_interface(request)) + return_value=mock_show_isis_interface(request)) + elif request.param.startswith('isis_summary'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_isis_summary(request)) yield bgp_util.run_bgp_command = _old_run_bgp_command diff --git a/tests/isis_frr_input/isis_frr_test_vector.py b/tests/isis_frr_input/isis_frr_test_vector.py index 24009b1379..3baea18727 100644 --- a/tests/isis_frr_input/isis_frr_test_vector.py +++ b/tests/isis_frr_input/isis_frr_test_vector.py @@ -453,6 +453,71 @@ def mock_show_isis_interface(request): return "" +isis_summary_output = \ +""" +r1# show isis summary +vrf : default +Process Id : 4663 +System Id : 0000.0000.0000 +Up time : 00:04:31 ago +Number of areas : 1 +Area 1: + Net: 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00 + TX counters per PDU type: + L2 IIH: 144 + L2 LSP: 4 + L2 CSNP: 29 + LSP RXMT: 0 + RX counters per PDU type: + L2 IIH: 143 + L2 LSP: 4 + Drop counters per PDU type: + L2 IIH: 1 + Advertise high metrics: Disabled + Level-1: + LSP0 regenerated: 3 + LSPs purged: 0 + SPF: + minimum interval : 1 + IPv4 route computation: + last run elapsed : 00:04:25 ago + last run duration : 111 usec + run count : 3 + IPv6 route computation: + last run elapsed : 00:04:25 ago + last run duration : 23 usec + run count : 3 + Level-2: + LSP0 regenerated: 4 + LSPs purged: 0 + SPF: + minimum interval : 1 + IPv4 route computation: + last run elapsed : 00:04:21 ago + last run duration : 45 usec + run count : 9 + IPv6 route computation: + last run elapsed : 00:04:21 ago + last run duration : 14 usec + run count : 9 +""" + +isis_summary_invalid_help_output = \ +"""Usage: summary [OPTIONS] +Try "summary --help" for help. + +Error: Got unexpected extra argument (?) +""" + +def mock_show_isis_summary(request): + if request.param == 'isis_summary_output': + return isis_summary_output + elif request.param == 'isis_summary_invalid_help_output': + return isis_summary_invalid_help_output + else: + return "" + + testData = { 'isis_neighbors': { 'args': [], @@ -554,4 +619,14 @@ def mock_show_isis_interface(request): 'rc': 2, 'rc_output': isis_interface_unknown_ifname_output }, + 'isis_summary': { + 'args': [], + 'rc': 0, + 'rc_output': isis_summary_output + }, + 'isis_summary_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': isis_summary_invalid_help_output + }, } diff --git a/tests/isis_frr_test.py b/tests/isis_frr_test.py index 946cdc2580..0165984796 100644 --- a/tests/isis_frr_test.py +++ b/tests/isis_frr_test.py @@ -125,3 +125,24 @@ def test_isis_interface(self, show = setup_isis_commands exec_cmd = show.cli.commands["isis"].commands["interface"] executor(test_vector, show, exec_cmd) + + +class TestIsisSummary(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('isis_summary_output', 'isis_summary'), + ('isis_summary_invalid_help_output', 'isis_summary_invalid_help') + ], + indirect=['setup_single_isis_instance']) + def test_isis_summary(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["isis"].commands["summary"] + executor(test_vector, show, exec_cmd) \ No newline at end of file From 4e9b5616f94259798307788ad4fca56afbe56e55 Mon Sep 17 00:00:00 2001 From: Isabella de Leon Date: Tue, 4 Apr 2023 14:57:27 -0700 Subject: [PATCH 4/4] [show/tests] Add support for "show run isis" Signed-off-by: ideleon@microsoft.com --- show/main.py | 58 ++++++++++++++++++++ tests/conftest.py | 8 ++- tests/isis_frr_input/isis_frr_test_vector.py | 57 +++++++++++++++++++ tests/isis_frr_test.py | 21 +++++++ 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/show/main.py b/show/main.py index eaa17e16ee..e96490c375 100755 --- a/show/main.py +++ b/show/main.py @@ -1481,6 +1481,64 @@ def bgp(namespace, verbose): print(output) +# 'isis' subcommand ("show runningconfiguration isis") +@runningconfiguration.command() +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@click.option('--config_db', is_flag=True, help="Enable config DB output") +@click.option('--namespace', '-n', 'namespace', required=False, default=None, type=str, show_default=False, + help='Option needed for multi-asic only: provide namespace name', + callback=multi_asic_util.multi_asic_namespace_validation_callback) +def isis(namespace, config_db, verbose): + """ + Show ISIS running configuration + Note: + multi-asic can run 'show run isis' and show from all asics, or 'show run isis -n ' + single-asic only run 'show run isis', '-n' is not available + """ + + if multi_asic.is_multi_asic(): + if namespace and namespace not in multi_asic.get_namespace_list(): + ctx = click.get_current_context() + ctx.fail("invalid value for -n/--namespace option. provide namespace from list {}".format(multi_asic.get_namespace_list())) + if not multi_asic.is_multi_asic() and namespace: + ctx = click.get_current_context() + ctx.fail("-n/--namespace is not available for single asic") + + if (config_db): + full_isis_conf = {} + + cmd = "sonic-cfggen -d --var-json ISIS_GLOBAL" + global_output = run_command(cmd, return_cmd=True) + + cmd = "sonic-cfggen -d --var-json ISIS_LEVEL" + level_output = run_command(cmd, return_cmd=True) + + cmd = "sonic-cfggen -d --var-json ISIS_INTERFACE" + interface_output = run_command(cmd, return_cmd=True) + + full_isis_conf["ISIS_GLOBAL"] = json.loads(global_output) + full_isis_conf["ISIS_LEVEL"] = json.loads(level_output) + full_isis_conf["ISIS_INTERFACE"] = json.loads(interface_output) + + click.echo(json.dumps(full_isis_conf, indent=4)) + else: + output = "" + cmd = "show running-config isis" + import utilities_common.bgp_util as bgp_util + if multi_asic.is_multi_asic(): + if not namespace: + ns_list = multi_asic.get_namespace_list() + for ns in ns_list: + output += "\n------------Showing running config isis on {}------------\n".format(ns) + output += bgp_util.run_bgp_show_command(cmd, ns) + else: + output += "\n------------Showing running config isis on {}------------\n".format(namespace) + output += bgp_util.run_bgp_show_command(cmd, namespace) + else: + output += bgp_util.run_bgp_show_command(cmd) + click.echo(output.rstrip('\n')) + + # 'interfaces' subcommand ("show runningconfiguration interfaces") @runningconfiguration.command() @click.argument('interfacename', required=False) diff --git a/tests/conftest.py b/tests/conftest.py index f84d111117..c33ece1241 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,8 @@ mock_show_isis_neighbors, mock_show_isis_database, mock_show_isis_hostname, - mock_show_isis_interface + mock_show_isis_interface, + mock_show_run_isis ) from . import config_int_ip_common import utilities_common.constants as constants @@ -395,7 +396,10 @@ def setup_single_isis_instance(request): elif request.param.startswith('isis_interface') or \ request.param.startswith('isis_interface'): bgp_util.run_bgp_command = mock.MagicMock( - return_value=mock_show_isis_interface(request)) + return_value=mock_show_isis_interface(request)) + elif request.param.startswith('show_run_isis'): + bgp_util.run_bgp_command = mock.MagicMock( + return_value=mock_show_run_isis(request)) yield bgp_util.run_bgp_command = _old_run_bgp_command diff --git a/tests/isis_frr_input/isis_frr_test_vector.py b/tests/isis_frr_input/isis_frr_test_vector.py index 24009b1379..88ef020540 100644 --- a/tests/isis_frr_input/isis_frr_test_vector.py +++ b/tests/isis_frr_input/isis_frr_test_vector.py @@ -452,6 +452,53 @@ def mock_show_isis_interface(request): else: return "" +show_run_isis_output = \ +"""Building configuration... + +Current configuration: +! +frr version 8.2.2 +frr defaults traditional +hostname vlab-01 +log syslog informational +log facility local4 +no service integrated-vtysh-config +! +password zebra +enable password zebra +! +interface PortChannel101 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point +exit +! +router isis 1 + is-type level-2-only + net 49.0001.1720.1700.0002.00 + lsp-mtu 1383 + lsp-timers level-1 gen-interval 30 refresh-interval 900 max-lifetime 1200 + lsp-timers level-2 gen-interval 30 refresh-interval 305 max-lifetime 900 + log-adjacency-changes +exit +! +end +""" + +show_run_isis_invalid_help_output = \ +"""Usage: isis [OPTIONS] +Try "isis --help" for help. + +Error: Got unexpected extra argument (?) +""" + +def mock_show_run_isis(request): + if request.param == 'show_run_isis_output': + return show_run_isis_output + elif request.param == 'show_run_isis_invalid_help_output': + return show_run_isis_invalid_help_output + else: + return "" testData = { 'isis_neighbors': { @@ -554,4 +601,14 @@ def mock_show_isis_interface(request): 'rc': 2, 'rc_output': isis_interface_unknown_ifname_output }, + 'show_run_isis': { + 'args': [], + 'rc': 0, + 'rc_output': show_run_isis_output + }, + 'show_run_isis_invalid_help': { + 'args': ['?'], + 'rc': 2, + 'rc_output': show_run_isis_invalid_help_output + }, } diff --git a/tests/isis_frr_test.py b/tests/isis_frr_test.py index 946cdc2580..3fe6e27633 100644 --- a/tests/isis_frr_test.py +++ b/tests/isis_frr_test.py @@ -125,3 +125,24 @@ def test_isis_interface(self, show = setup_isis_commands exec_cmd = show.cli.commands["isis"].commands["interface"] executor(test_vector, show, exec_cmd) + + +class TestShowRunIsis(object): + + @classmethod + def setup_class(cls): + print("SETUP") + + @pytest.mark.parametrize('setup_single_isis_instance, test_vector', + [ + ('show_run_isis_output', 'show_run_isis'), + ('show_run_isis_invalid_help_output', 'show_run_isis_invalid_help') + ], + indirect=['setup_single_isis_instance']) + def test_show_run_isis(self, + setup_isis_commands, + setup_single_isis_instance, + test_vector): + show = setup_isis_commands + exec_cmd = show.cli.commands["runningconfiguration"].commands["isis"] + executor(test_vector, show, exec_cmd)