Skip to content

Commit 5e661d8

Browse files
authored
[SHOW][BGP] support show ip(v6) bgp summary for multi asic platform (#1064)
Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <arlakshm@microsoft.com> The following changes are done - Support show ip(v6) bgp summary for multi asic platforms Add the following 2 multi asic options to the cli command [-n, --namespace] to allow user to display the information for given namespaces If this option is not present the information from all the namespaces will be displayed [-d, --display] to allow user to display information related both internal and external interfaces If this option is not present only external interfaces/neighbors will be display - For FRR bgp containers, get the bgp summary in json and display to the user add a new file bgp_util.py to have all the common bgp related functions. add new functions to get bgp summary from FRR in json, parse the json and render the output - Add unit test for show ip(v4) bgp summary add new unit test to show ip(v4) bgp summary
1 parent 8934479 commit 5e661d8

12 files changed

+1912
-199
lines changed

show/bgp_frr_v4.py

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import json
2+
13
import click
24

5+
import utilities_common.bgp_util as bgp_util
36
import utilities_common.cli as clicommon
4-
5-
from show.main import ip, run_command, get_bgp_summary_extended
6-
7+
import utilities_common.constants as constants
8+
import utilities_common.multi_asic as multi_asic_util
9+
from show.main import ip, run_command
710

811
###############################################################################
912
#
@@ -12,6 +15,7 @@
1215
###############################################################################
1316

1417

18+
1519
@ip.group(cls=clicommon.AliasedGroup)
1620
def bgp():
1721
"""Show IPv4 BGP (Border Gateway Protocol) information"""
@@ -20,14 +24,11 @@ def bgp():
2024

2125
# 'summary' subcommand ("show ip bgp summary")
2226
@bgp.command()
23-
def summary():
24-
"""Show summarized information of IPv4 BGP state"""
25-
try:
26-
device_output = run_command('sudo vtysh -c "show ip bgp summary"', return_cmd=True)
27-
get_bgp_summary_extended(device_output)
28-
except Exception:
29-
run_command('sudo vtysh -c "show ip bgp summary"')
30-
27+
@multi_asic_util.multi_asic_click_options
28+
def summary(namespace, display):
29+
bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(constants.IPV4, namespace,display)
30+
bgp_util.display_bgp_summary(bgp_summary=bgp_summary, af=constants.IPV4)
31+
3132

3233
# 'neighbors' subcommand ("show ip bgp neighbors")
3334
@bgp.command()

show/bgp_frr_v6.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import click
22

33
import utilities_common.cli as clicommon
4-
from show.main import ipv6, run_command, get_bgp_summary_extended
5-
4+
from show.main import ipv6, run_command
5+
import utilities_common.multi_asic as multi_asic_util
6+
import utilities_common.bgp_util as bgp_util
7+
import utilities_common.constants as constants
68

79
###############################################################################
810
#
@@ -19,13 +21,11 @@ def bgp():
1921

2022
# 'summary' subcommand ("show ipv6 bgp summary")
2123
@bgp.command()
22-
def summary():
24+
@multi_asic_util.multi_asic_click_options
25+
def summary(namespace, display):
2326
"""Show summarized information of IPv6 BGP state"""
24-
try:
25-
device_output = run_command('sudo vtysh -c "show bgp ipv6 summary"', return_cmd=True)
26-
get_bgp_summary_extended(device_output)
27-
except Exception:
28-
run_command('sudo vtysh -c "show bgp ipv6 summary"')
27+
bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(constants.IPV6, namespace,display)
28+
bgp_util.display_bgp_summary(bgp_summary=bgp_summary, af=constants.IPV6)
2929

3030

3131
# 'neighbors' subcommand ("show ipv6 bgp neighbors")

show/bgp_quagga_v4.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import click
2-
from show.main import AliasedGroup, ip, run_command, get_bgp_summary_extended
2+
from show.main import AliasedGroup, ip, run_command
3+
from utilities_common.bgp_util import get_bgp_summary_extended
34

45

56
###############################################################################

show/bgp_quagga_v6.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import click
2-
from show.main import AliasedGroup, ipv6, run_command, get_bgp_summary_extended
2+
from show.main import AliasedGroup, ipv6, run_command
3+
from utilities_common.bgp_util import get_bgp_summary_extended
34

45

56
###############################################################################

show/main.py

+2-132
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import re
88
import subprocess
99
import sys
10-
import ipaddress
10+
1111

1212
import click
1313
from natsort import natsorted
@@ -35,7 +35,7 @@
3535
# location (configdb?), so that we prevent the continous execution of this
3636
# bash oneliner. To be revisited once routing-stack info is tracked somewhere.
3737
def get_routing_stack():
38-
command = "sudo docker ps | grep bgp | awk '{print$2}' | cut -d'-' -f3 | cut -d':' -f1"
38+
command = "sudo docker ps | grep bgp | awk '{print$2}' | cut -d'-' -f3 | cut -d':' -f1 | head -n 1"
3939

4040
try:
4141
proc = subprocess.Popen(command,
@@ -95,33 +95,6 @@ def run_command(command, display_cmd=False, return_cmd=False):
9595
iface_alias_converter = clicommon.InterfaceAliasConverter()
9696

9797

98-
def get_bgp_summary_extended(command_output):
99-
"""
100-
Adds Neighbor name to the show ip[v6] bgp summary command
101-
:param command: command to get bgp summary
102-
"""
103-
static_neighbors, dynamic_neighbors = get_bgp_neighbors_dict()
104-
modified_output = []
105-
my_list = iter(command_output.splitlines())
106-
for element in my_list:
107-
if element.startswith("Neighbor"):
108-
element = "{}\tNeighborName".format(element)
109-
modified_output.append(element)
110-
elif not element or element.startswith("Total number "):
111-
modified_output.append(element)
112-
elif re.match(r"(\*?([0-9A-Fa-f]{1,4}:|\d+.\d+.\d+.\d+))", element.split()[0]):
113-
first_element = element.split()[0]
114-
ip = first_element[1:] if first_element.startswith("*") else first_element
115-
name = get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors)
116-
if len(element.split()) == 1:
117-
modified_output.append(element)
118-
element = next(my_list)
119-
element = "{}\t{}".format(element, name)
120-
modified_output.append(element)
121-
else:
122-
modified_output.append(element)
123-
click.echo("\n".join(modified_output))
124-
12598

12699
def connect_config_db():
127100
"""
@@ -132,108 +105,6 @@ def connect_config_db():
132105
return config_db
133106

134107

135-
def get_neighbor_dict_from_table(db,table_name):
136-
"""
137-
returns a dict with bgp neighbor ip as key and neighbor name as value
138-
:param table_name: config db table name
139-
:param db: config_db
140-
"""
141-
neighbor_dict = {}
142-
neighbor_data = db.get_table(table_name)
143-
try:
144-
for entry in neighbor_data.keys():
145-
neighbor_dict[entry] = neighbor_data[entry].get(
146-
'name') if 'name' in neighbor_data[entry].keys() else 'NotAvailable'
147-
return neighbor_dict
148-
except Exception:
149-
return neighbor_dict
150-
151-
152-
def is_ipv4_address(ipaddress):
153-
"""
154-
Checks if given ip is ipv4
155-
:param ipaddress: unicode ipv4
156-
:return: bool
157-
"""
158-
try:
159-
ipaddress.IPv4Address(ipaddress)
160-
return True
161-
except ipaddress.AddressValueError as err:
162-
return False
163-
164-
165-
def is_ipv6_address(ipaddress):
166-
"""
167-
Checks if given ip is ipv6
168-
:param ipaddress: unicode ipv6
169-
:return: bool
170-
"""
171-
try:
172-
ipaddress.IPv6Address(ipaddress)
173-
return True
174-
except ipaddress.AddressValueError as err:
175-
return False
176-
177-
178-
def get_dynamic_neighbor_subnet(db):
179-
"""
180-
Returns dict of description and subnet info from bgp_peer_range table
181-
:param db: config_db
182-
"""
183-
dynamic_neighbor = {}
184-
v4_subnet = {}
185-
v6_subnet = {}
186-
neighbor_data = db.get_table('BGP_PEER_RANGE')
187-
try:
188-
for entry in neighbor_data.keys():
189-
new_key = neighbor_data[entry]['ip_range'][0]
190-
new_value = neighbor_data[entry]['name']
191-
if is_ipv4_address(unicode(neighbor_data[entry]['src_address'])):
192-
v4_subnet[new_key] = new_value
193-
elif is_ipv6_address(unicode(neighbor_data[entry]['src_address'])):
194-
v6_subnet[new_key] = new_value
195-
dynamic_neighbor["v4"] = v4_subnet
196-
dynamic_neighbor["v6"] = v6_subnet
197-
return dynamic_neighbor
198-
except Exception:
199-
return neighbor_data
200-
201-
202-
def get_bgp_neighbors_dict():
203-
"""
204-
Uses config_db to get the bgp neighbors and names in dictionary format
205-
:return:
206-
"""
207-
dynamic_neighbors = {}
208-
config_db = connect_config_db()
209-
static_neighbors = get_neighbor_dict_from_table(config_db, 'BGP_NEIGHBOR')
210-
bgp_monitors = get_neighbor_dict_from_table(config_db, 'BGP_MONITORS')
211-
static_neighbors.update(bgp_monitors)
212-
dynamic_neighbors = get_dynamic_neighbor_subnet(config_db)
213-
return static_neighbors, dynamic_neighbors
214-
215-
216-
def get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors):
217-
"""
218-
return neighbor name for the ip provided
219-
:param ip: ip address unicode
220-
:param static_neighbors: statically defined bgp neighbors dict
221-
:param dynamic_neighbors: subnet of dynamically defined neighbors dict
222-
:return: name of neighbor
223-
"""
224-
if ip in static_neighbors.keys():
225-
return static_neighbors[ip]
226-
elif is_ipv4_address(unicode(ip)):
227-
for subnet in dynamic_neighbors["v4"].keys():
228-
if ipaddress.IPv4Address(unicode(ip)) in ipaddress.IPv4Network(unicode(subnet)):
229-
return dynamic_neighbors["v4"][subnet]
230-
elif is_ipv6_address(unicode(ip)):
231-
for subnet in dynamic_neighbors["v6"].keys():
232-
if ipaddress.IPv6Address(unicode(ip)) in ipaddress.IPv6Network(unicode(subnet)):
233-
return dynamic_neighbors["v6"][subnet]
234-
else:
235-
return "NotAvailable"
236-
237108

238109
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?'])
239110

@@ -1038,7 +909,6 @@ def protocol(verbose):
1038909
cmd = 'sudo vtysh -c "show ipv6 protocol"'
1039910
run_command(cmd, display_cmd=verbose)
1040911

1041-
1042912
#
1043913
# Inserting BGP functionality into cli's show parse-chain.
1044914
# BGP commands are determined by the routing-stack being elected.

tests/bgp_commands_test.py

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import os
2+
3+
import pytest
4+
5+
from click.testing import CliRunner
6+
7+
show_bgp_summary_v4 = """\
8+
9+
IPv4 Unicast Summary:
10+
BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0
11+
BGP table version 12811
12+
RIB entries 12817, using 2358328 bytes of memory
13+
Peers 24, using 502080 KiB of memory
14+
Peer groups 4, using 256 bytes of memory
15+
16+
17+
Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName
18+
----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- --------------
19+
10.0.0.1 4 65200 5919 2717 0 0 0 1d21h11m 6402 ARISTA01T2
20+
10.0.0.5 4 65200 5916 2714 0 0 0 1d21h10m 6402 ARISTA03T2
21+
10.0.0.9 4 65200 5915 2713 0 0 0 1d21h09m 6402 ARISTA05T2
22+
10.0.0.13 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA07T2
23+
10.0.0.17 4 65200 5916 2713 0 0 0 1d21h09m 6402 ARISTA09T2
24+
10.0.0.21 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA11T2
25+
10.0.0.25 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA13T2
26+
10.0.0.29 4 65200 5916 2714 0 0 0 1d21h10m 6402 ARISTA15T2
27+
10.0.0.33 4 64001 0 0 0 0 0 never Active ARISTA01T0
28+
10.0.0.35 4 64002 0 0 0 0 0 never Active ARISTA02T0
29+
10.0.0.37 4 64003 0 0 0 0 0 never Active ARISTA03T0
30+
10.0.0.39 4 64004 0 0 0 0 0 never Active ARISTA04T0
31+
10.0.0.41 4 64005 0 0 0 0 0 never Active ARISTA05T0
32+
10.0.0.43 4 64006 0 0 0 0 0 never Active ARISTA06T0
33+
10.0.0.45 4 64007 0 0 0 0 0 never Active ARISTA07T0
34+
10.0.0.47 4 64008 0 0 0 0 0 never Active ARISTA08T0
35+
10.0.0.49 4 64009 0 0 0 0 0 never Active ARISTA09T0
36+
10.0.0.51 4 64010 0 0 0 0 0 never Active ARISTA10T0
37+
10.0.0.53 4 64011 0 0 0 0 0 never Active ARISTA11T0
38+
10.0.0.55 4 64012 0 0 0 0 0 never Active ARISTA12T0
39+
10.0.0.57 4 64013 0 0 0 0 0 never Active ARISTA13T0
40+
10.0.0.59 4 64014 0 0 0 0 0 never Active ARISTA14T0
41+
10.0.0.61 4 64015 0 0 0 0 0 never Active ARISTA15T0
42+
10.0.0.63 4 64016 0 0 0 0 0 never Active ARISTA16T0
43+
"""
44+
45+
show_bgp_summary_v6 = """\
46+
47+
IPv6 Unicast Summary:
48+
BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0
49+
BGP table version 8972
50+
RIB entries 12817, using 2358328 bytes of memory
51+
Peers 24, using 502080 KiB of memory
52+
Peer groups 4, using 256 bytes of memory
53+
54+
55+
Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName
56+
----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- --------------
57+
fc00::1a 4 65200 6665 6672 0 0 0 2d09h39m 6402 ARISTA07T2
58+
fc00::2 4 65200 6666 7913 0 0 0 2d09h39m 6402 ARISTA01T2
59+
fc00::2a 4 65200 6666 7913 0 0 0 2d09h39m 6402 ARISTA11T2
60+
fc00::3a 4 65200 6666 7912 0 0 0 2d09h39m 6402 ARISTA15T2
61+
fc00::4a 4 64003 0 0 0 0 0 never Active ARISTA03T0
62+
fc00::4e 4 64004 0 0 0 0 0 never Active ARISTA04T0
63+
fc00::5a 4 64007 0 0 0 0 0 never Active ARISTA07T0
64+
fc00::5e 4 64008 0 0 0 0 0 never Active ARISTA08T0
65+
fc00::6a 4 64011 0 0 0 0 0 never Connect ARISTA11T0
66+
fc00::6e 4 64012 0 0 0 0 0 never Active ARISTA12T0
67+
fc00::7a 4 64015 0 0 0 0 0 never Active ARISTA15T0
68+
fc00::7e 4 64016 0 0 0 0 0 never Active ARISTA16T0
69+
fc00::12 4 65200 6666 7915 0 0 0 2d09h39m 6402 ARISTA05T2
70+
fc00::22 4 65200 6667 7915 0 0 0 2d09h39m 6402 ARISTA09T2
71+
fc00::32 4 65200 6663 6669 0 0 0 2d09h36m 6402 ARISTA13T2
72+
fc00::42 4 64001 0 0 0 0 0 never Active ARISTA01T0
73+
fc00::46 4 64002 0 0 0 0 0 never Active ARISTA02T0
74+
fc00::52 4 64005 0 0 0 0 0 never Active ARISTA05T0
75+
fc00::56 4 64006 0 0 0 0 0 never Active ARISTA06T0
76+
fc00::62 4 64009 0 0 0 0 0 never Active ARISTA09T0
77+
fc00::66 4 64010 0 0 0 0 0 never Active ARISTA10T0
78+
fc00::72 4 64013 0 0 0 0 0 never Active ARISTA13T0
79+
fc00::76 4 64014 0 0 0 0 0 never Active ARISTA14T0
80+
fc00::a 4 65200 6665 6671 0 0 0 2d09h38m 6402 ARISTA03T2
81+
"""
82+
83+
show_error_invalid_json = """\
84+
Usage: summary [OPTIONS]
85+
Try 'summary --help' for help.
86+
87+
Error: bgp summary from bgp container not in json format
88+
"""
89+
90+
91+
class TestBgpCommands(object):
92+
@classmethod
93+
def setup_class(cls):
94+
print("SETUP")
95+
import mock_tables.dbconnector
96+
97+
@pytest.mark.parametrize('setup_single_bgp_instance',
98+
['v4'], indirect=['setup_single_bgp_instance'])
99+
def test_bgp_summary_v4(
100+
self,
101+
setup_bgp_commands,
102+
setup_single_bgp_instance):
103+
show = setup_bgp_commands
104+
runner = CliRunner()
105+
result = runner.invoke(
106+
show.cli.commands["ip"].commands["bgp"].commands["summary"], [])
107+
print("{}".format(result.output))
108+
assert result.exit_code == 0
109+
assert result.output == show_bgp_summary_v4
110+
111+
@pytest.mark.parametrize('setup_single_bgp_instance',
112+
['v6'], indirect=['setup_single_bgp_instance'])
113+
def test_bgp_summary_v6(
114+
self,
115+
setup_bgp_commands,
116+
setup_single_bgp_instance):
117+
show = setup_bgp_commands
118+
runner = CliRunner()
119+
result = runner.invoke(
120+
show.cli.commands["ipv6"].commands["bgp"].commands["summary"], [])
121+
print("{}".format(result.output))
122+
assert result.exit_code == 0
123+
assert result.output == show_bgp_summary_v6
124+
125+
@pytest.mark.parametrize('setup_single_bgp_instance',
126+
[' '], indirect=['setup_single_bgp_instance'])
127+
def test_bgp_summary_error(
128+
self,
129+
setup_bgp_commands,
130+
setup_single_bgp_instance):
131+
show = setup_bgp_commands
132+
runner = CliRunner()
133+
result = runner.invoke(
134+
show.cli.commands["ipv6"].commands["bgp"].commands["summary"], [])
135+
print("{}".format(result.output))
136+
assert result.exit_code == 2
137+
assert result.output == show_error_invalid_json

0 commit comments

Comments
 (0)