Skip to content

Commit

Permalink
Enable show ip bgp on sup and -n all for show ip bgp network (#3417)
Browse files Browse the repository at this point in the history
#### What I did

1. Enable "show ip bgp" on sup and "-n all" for show ip bgp network.
2. Modify README.doc to make the instructions of building and installing the wheel package more clear.
3. Improve the output format of rexec command

#### How I did it

Modify the code in show/main.py to enable "show ip bgp ..." on supervisors and modify show/bgp_frr_v4.py to add support for the new features.
Update README.md.
Modify the rexec implementation to improve the output format.
Add unit tests for the above change.

#### How to verify it

Run on a SONiC chassis
  • Loading branch information
BYGX-wcr committed Jul 25, 2024
1 parent 8ac8691 commit eda8f7a
Show file tree
Hide file tree
Showing 13 changed files with 282 additions and 24 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,20 @@ A convenient alternative is to let the SONiC build system configure a build envi
```
python3 setup.py bdist_wheel
```
Note: This command by default will not update the wheel package in target/. To specify the destination location of wheel package, use "-d" option.
#### To run unit tests
```
python3 setup.py test
```
#### To install the package on a SONiC machine
```
sudo pip uninstall sonic-utilities
sudo pip install YOUR_WHEEL_PACKAGE
```
Note: Don't use "--force-reinstall".
### sonic-utilities-data
Expand Down
14 changes: 12 additions & 2 deletions rcli/linecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import termios
import tty

from .utils import get_linecard_ip
from .utils import get_linecard_ip, get_linecard_hostname_from_module_name, get_linecard_module_name_from_hostname
from paramiko.py3compat import u
from paramiko import Channel

Expand All @@ -31,7 +31,17 @@ def __init__(self, linecard_name, username, password):
if not self.ip:
sys.exit(1)

self.linecard_name = linecard_name
# if the user passes linecard hostname, then try to get the module name for that linecard
module_name = get_linecard_module_name_from_hostname(linecard_name)
if module_name is None:
# if the module name cannot be found from host, assume the user has passed module name
self.module_name = linecard_name
self.hostname = get_linecard_hostname_from_module_name(linecard_name)
else:
# the user has passed linecard hostname
self.hostname = linecard_name
self.module_name = module_name

self.username = username
self.password = password

Expand Down
12 changes: 7 additions & 5 deletions rcli/rexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,22 @@ def cli(linecard_names, command, username):

if list(linecard_names) == ["all"]:
# Get all linecard names using autocompletion helper
linecard_names = rcli_utils.get_all_linecards(None, None, "")
module_names = sorted(rcli_utils.get_all_linecards(None, None, ""))
else:
module_names = linecard_names

linecards = []
# Iterate through each linecard, check if the login was successful
for linecard_name in linecard_names:
linecard = Linecard(linecard_name, username, password)
for module_name in module_names:
linecard = Linecard(module_name, username, password)
if not linecard.connection:
click.echo(f"Failed to connect to {linecard_name} with username {username}")
click.echo(f"Failed to connect to {module_name} with username {username}")
sys.exit(1)
linecards.append(linecard)

for linecard in linecards:
if linecard.connection:
click.echo(f"======== {linecard.linecard_name} output: ========")
click.echo(f"======== {linecard.module_name}|{linecard.hostname} output: ========")
click.echo(linecard.execute_cmd(command))


Expand Down
4 changes: 2 additions & 2 deletions rcli/rshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ def cli(linecard_name, username):
try:
linecard = Linecard(linecard_name, username, password)
if linecard.connection:
click.echo(f"Connecting to {linecard.linecard_name}")
click.echo(f"Connecting to {linecard.module_name}")
# If connection was created, connection exists.
# Otherwise, user will see an error message.
linecard.start_shell()
click.echo("Connection Closed")
except paramiko.ssh_exception.AuthenticationException:
click.echo(
f"Login failed on '{linecard.linecard_name}' with username '{linecard.username}'")
f"Login failed on '{linecard.module_name}' with username '{linecard.username}'")


if __name__=="__main__":
Expand Down
15 changes: 15 additions & 0 deletions rcli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ def get_linecard_module_name_from_hostname(linecard_name: str):

return None


def get_linecard_hostname_from_module_name(linecard_name: str):

chassis_state_db = connect_to_chassis_state_db()
keys = chassis_state_db.keys(chassis_state_db.CHASSIS_STATE_DB, '{}|{}'.format(CHASSIS_MODULE_HOSTNAME_TABLE, '*'))
for key in keys:
module_name = key.split('|')[1]
if module_name.replace('-', '').lower() == linecard_name.replace('-', '').lower():
hostname = chassis_state_db.get(chassis_state_db.CHASSIS_STATE_DB, key, CHASSIS_MODULE_HOSTNAME)
return hostname

return None


def get_linecard_ip(linecard_name: str):
"""
Given a linecard name, lookup its IP address in the midplane table
Expand All @@ -69,6 +83,7 @@ def get_linecard_ip(linecard_name: str):
return None
return module_ip


def get_module_ip_and_access_from_state_db(module_name):
state_db = connect_state_db()
data_dict = state_db.get_all(
Expand Down
38 changes: 31 additions & 7 deletions show/bgp_frr_v4.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import click
import sys
import subprocess

from sonic_py_common import multi_asic
from sonic_py_common import multi_asic, device_info
from show.main import ip
import utilities_common.bgp_util as bgp_util
import utilities_common.cli as clicommon
Expand All @@ -17,6 +19,12 @@
@ip.group(cls=clicommon.AliasedGroup)
def bgp():
"""Show IPv4 BGP (Border Gateway Protocol) information"""
if device_info.is_supervisor():
# if the device is a chassis, the command need to be executed by rexec
click.echo("Since the current device is a chassis supervisor, " +
"this command will be executed remotely on all linecards")
proc = subprocess.run(["rexec", "all"] + ["-c", " ".join(sys.argv)])
sys.exit(proc.returncode)
pass


Expand Down Expand Up @@ -102,10 +110,16 @@ def neighbors(ipaddress, info_type, namespace):
def network(ipaddress, info_type, namespace):
"""Show IP (IPv4) BGP network"""

if multi_asic.is_multi_asic() and namespace not in multi_asic.get_namespace_list():
ctx = click.get_current_context()
ctx.fail('-n/--namespace option required. provide namespace from list {}'\
.format(multi_asic.get_namespace_list()))
namespace = namespace.strip()
if multi_asic.is_multi_asic():
if namespace == multi_asic.DEFAULT_NAMESPACE:
ctx = click.get_current_context()
ctx.fail('-n/--namespace option required. provide namespace from list {}'
.format(multi_asic.get_namespace_list()))
if namespace != "all" and namespace not in multi_asic.get_namespace_list():
ctx = click.get_current_context()
ctx.fail('invalid namespace {}. provide namespace from list {}'
.format(namespace, multi_asic.get_namespace_list()))

command = 'show ip bgp'
if ipaddress is not None:
Expand All @@ -125,5 +139,15 @@ def network(ipaddress, info_type, namespace):
if info_type is not None:
command += ' {}'.format(info_type)

output = bgp_util.run_bgp_show_command(command, namespace)
click.echo(output.rstrip('\n'))
if namespace == "all":
if multi_asic.is_multi_asic():
for ns in multi_asic.get_namespace_list():
click.echo("\n======== namespace {} ========".format(ns))
output = bgp_util.run_bgp_show_command(command, ns)
click.echo(output.rstrip('\n'))
else:
output = bgp_util.run_bgp_show_command(command, "")
click.echo(output.rstrip('\n'))
else:
output = bgp_util.run_bgp_show_command(command, namespace)
click.echo(output.rstrip('\n'))
6 changes: 5 additions & 1 deletion show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,11 @@ def protocol(verbose):
ip.add_command(bgp)
from .bgp_frr_v6 import bgp
ipv6.add_command(bgp)

elif device_info.is_supervisor():
from .bgp_frr_v4 import bgp
ip.add_command(bgp)
from .bgp_frr_v6 import bgp
ipv6.add_command(bgp)
#
# 'link-local-mode' subcommand ("show ipv6 link-local-mode")
#
Expand Down
128 changes: 127 additions & 1 deletion tests/bgp_commands_input/bgp_network_test_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@
multi_asic_bgp_network_err = \
"""Error: -n/--namespace option required. provide namespace from list ['asic0', 'asic1']"""

multi_asic_bgp_network_asic_unknown_err = \
"""Error: invalid namespace asic_unknown. provide namespace from list ['asic0', 'asic1']"""

bgp_v4_network_asic0 = \
"""
BGP table version is 11256, local router ID is 10.1.0.32, vrf id 0
Expand Down Expand Up @@ -276,7 +279,7 @@
*=i10.0.0.42/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.44/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
"""

bgp_v4_network_ip_address_asic0 = \
Expand Down Expand Up @@ -311,6 +314,111 @@
Last update: Thu Apr 22 02:13:30 2021
"""

bgp_v4_network_all_asic = \
"""
======== namespace asic0 ========
BGP table version is 11256, local router ID is 10.1.0.32, vrf id 0
Default local pref 100, local AS 65100
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
* i0.0.0.0/0 10.1.0.2 100 0 65200 6666 6667 i
* i 10.1.0.0 100 0 65200 6666 6667 i
*= 10.0.0.5 0 65200 6666 6667 i
*> 10.0.0.1 0 65200 6666 6667 i
* i8.0.0.0/32 10.1.0.2 0 100 0 i
* i 10.1.0.0 0 100 0 i
* 0.0.0.0 0 32768 ?
*> 0.0.0.0 0 32768 i
*=i8.0.0.1/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*=i8.0.0.2/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*=i8.0.0.3/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*>i8.0.0.4/32 10.1.0.0 0 100 0 i
*>i8.0.0.5/32 10.1.0.2 0 100 0 i
* i10.0.0.0/31 10.1.0.2 0 100 0 ?
* i 10.1.0.0 0 100 0 ?
*> 0.0.0.0 0 32768 ?
* i10.0.0.4/31 10.1.0.2 0 100 0 ?
* i 10.1.0.0 0 100 0 ?
*> 0.0.0.0 0 32768 ?
*=i10.0.0.8/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.12/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.32/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.34/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.36/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.38/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.40/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.42/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.44/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
======== namespace asic1 ========
BGP table version is 11256, local router ID is 10.1.0.32, vrf id 0
Default local pref 100, local AS 65100
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
* i0.0.0.0/0 10.1.0.2 100 0 65200 6666 6667 i
* i 10.1.0.0 100 0 65200 6666 6667 i
*= 10.0.0.5 0 65200 6666 6667 i
*> 10.0.0.1 0 65200 6666 6667 i
* i8.0.0.0/32 10.1.0.2 0 100 0 i
* i 10.1.0.0 0 100 0 i
* 0.0.0.0 0 32768 ?
*> 0.0.0.0 0 32768 i
*=i8.0.0.1/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*=i8.0.0.2/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*=i8.0.0.3/32 10.1.0.2 0 100 0 i
*>i 10.1.0.0 0 100 0 i
*>i8.0.0.4/32 10.1.0.0 0 100 0 i
*>i8.0.0.5/32 10.1.0.2 0 100 0 i
* i10.0.0.0/31 10.1.0.2 0 100 0 ?
* i 10.1.0.0 0 100 0 ?
*> 0.0.0.0 0 32768 ?
* i10.0.0.4/31 10.1.0.2 0 100 0 ?
* i 10.1.0.0 0 100 0 ?
*> 0.0.0.0 0 32768 ?
*=i10.0.0.8/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.12/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.32/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.34/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.36/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.38/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.40/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.42/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
*=i10.0.0.44/31 10.1.0.2 0 100 0 ?
*>i 10.1.0.0 0 100 0 ?
"""

bgp_v6_network_asic0 = \
"""
BGP table version is 12849, local router ID is 10.1.0.32, vrf id 0
Expand Down Expand Up @@ -429,6 +537,9 @@ def mock_show_bgp_network_multi_asic(param):
return bgp_v6_network_ip_address_asic0
elif param == 'bgp_v6_network_bestpath_asic0':
return bgp_v6_network_ip_address_asic0_bestpath
elif param == "bgp_v4_network_all_asic":
# this is mocking the output of a single LC
return bgp_v4_network_asic0
else:
return ''

Expand All @@ -454,6 +565,11 @@ def mock_show_bgp_network_multi_asic(param):
'rc': 1,
'rc_output': bgp_v4_network_longer_prefixes_error
},
'bgp_v4_network_all_asic_on_single_asic': {
'args': ['-nall'],
'rc': 0,
'rc_output': bgp_v4_network
},
'bgp_v6_network': {
'args': [],
'rc': 0,
Expand Down Expand Up @@ -499,6 +615,16 @@ def mock_show_bgp_network_multi_asic(param):
'rc': 0,
'rc_output': bgp_v4_network_bestpath_asic0
},
'bgp_v4_network_all_asic': {
'args': ['-nall'],
'rc': 0,
'rc_output': bgp_v4_network_all_asic
},
'bgp_v4_network_asic_unknown': {
'args': ['-nasic_unknown'],
'rc': 2,
'rc_err_msg': multi_asic_bgp_network_asic_unknown_err
},
'bgp_v6_network_multi_asic': {
'args': [],
'rc': 2,
Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,13 @@ def mock_run_show_summ_bgp_command_no_ext_neigh_on_asic1(
else:
return ""

def mock_multi_asic_list():
return ["asic0", "asic1"]

# mock multi-asic list
if request.param == "bgp_v4_network_all_asic":
multi_asic.get_namespace_list = mock_multi_asic_list

_old_run_bgp_command = bgp_util.run_bgp_command
if request.param == 'ip_route_for_int_ip':
bgp_util.run_bgp_command = mock_run_bgp_command_for_static
Expand Down
3 changes: 3 additions & 0 deletions tests/mock_tables/chassis_state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD1": {
"module_hostname": "sonic-lc2"
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": {
"module_hostname": "sonic-lc3"
}

}
Loading

0 comments on commit eda8f7a

Please sign in to comment.