Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SHOW][BGP] support show ip(v6) bgp summary for multi asic platform #1064

Merged
merged 3 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions show/bgp_frr_v4.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import json

import click

import utilities_common.bgp_util as bgp_util
import utilities_common.cli as clicommon

from show.main import ip, run_command, get_bgp_summary_extended

import utilities_common.constants as constants
import utilities_common.multi_asic as multi_asic_util
from show.main import ip, run_command

###############################################################################
#
Expand All @@ -12,6 +15,7 @@
###############################################################################



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

# 'summary' subcommand ("show ip bgp summary")
@bgp.command()
def summary():
"""Show summarized information of IPv4 BGP state"""
try:
device_output = run_command('sudo vtysh -c "show ip bgp summary"', return_cmd=True)
get_bgp_summary_extended(device_output)
except Exception:
run_command('sudo vtysh -c "show ip bgp summary"')

@multi_asic_util.multi_asic_click_options
def summary(namespace, display):
bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(constants.IPV4, namespace,display)
bgp_util.display_bgp_summary(bgp_summary=bgp_summary, af=constants.IPV4)


# 'neighbors' subcommand ("show ip bgp neighbors")
@bgp.command()
Expand Down
16 changes: 8 additions & 8 deletions show/bgp_frr_v6.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import click

import utilities_common.cli as clicommon
from show.main import ipv6, run_command, get_bgp_summary_extended

from show.main import ipv6, run_command
import utilities_common.multi_asic as multi_asic_util
import utilities_common.bgp_util as bgp_util
import utilities_common.constants as constants

###############################################################################
#
Expand All @@ -19,13 +21,11 @@ def bgp():

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


# 'neighbors' subcommand ("show ipv6 bgp neighbors")
Expand Down
3 changes: 2 additions & 1 deletion show/bgp_quagga_v4.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import click
from show.main import AliasedGroup, ip, run_command, get_bgp_summary_extended
from show.main import AliasedGroup, ip, run_command
from utilities_common.bgp_util import get_bgp_summary_extended


###############################################################################
Expand Down
3 changes: 2 additions & 1 deletion show/bgp_quagga_v6.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import click
from show.main import AliasedGroup, ipv6, run_command, get_bgp_summary_extended
from show.main import AliasedGroup, ipv6, run_command
from utilities_common.bgp_util import get_bgp_summary_extended


###############################################################################
Expand Down
134 changes: 2 additions & 132 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import re
import subprocess
import sys
import ipaddress


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

try:
proc = subprocess.Popen(command,
Expand Down Expand Up @@ -95,33 +95,6 @@ def run_command(command, display_cmd=False, return_cmd=False):
iface_alias_converter = clicommon.InterfaceAliasConverter()


def get_bgp_summary_extended(command_output):
"""
Adds Neighbor name to the show ip[v6] bgp summary command
:param command: command to get bgp summary
"""
static_neighbors, dynamic_neighbors = get_bgp_neighbors_dict()
modified_output = []
my_list = iter(command_output.splitlines())
for element in my_list:
if element.startswith("Neighbor"):
element = "{}\tNeighborName".format(element)
modified_output.append(element)
elif not element or element.startswith("Total number "):
modified_output.append(element)
elif re.match(r"(\*?([0-9A-Fa-f]{1,4}:|\d+.\d+.\d+.\d+))", element.split()[0]):
first_element = element.split()[0]
ip = first_element[1:] if first_element.startswith("*") else first_element
name = get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors)
if len(element.split()) == 1:
modified_output.append(element)
element = next(my_list)
element = "{}\t{}".format(element, name)
modified_output.append(element)
else:
modified_output.append(element)
click.echo("\n".join(modified_output))


def connect_config_db():
"""
Expand All @@ -132,108 +105,6 @@ def connect_config_db():
return config_db


def get_neighbor_dict_from_table(db,table_name):
"""
returns a dict with bgp neighbor ip as key and neighbor name as value
:param table_name: config db table name
:param db: config_db
"""
neighbor_dict = {}
neighbor_data = db.get_table(table_name)
try:
for entry in neighbor_data.keys():
neighbor_dict[entry] = neighbor_data[entry].get(
'name') if 'name' in neighbor_data[entry].keys() else 'NotAvailable'
return neighbor_dict
except Exception:
return neighbor_dict


def is_ipv4_address(ipaddress):
"""
Checks if given ip is ipv4
:param ipaddress: unicode ipv4
:return: bool
"""
try:
ipaddress.IPv4Address(ipaddress)
return True
except ipaddress.AddressValueError as err:
return False


def is_ipv6_address(ipaddress):
"""
Checks if given ip is ipv6
:param ipaddress: unicode ipv6
:return: bool
"""
try:
ipaddress.IPv6Address(ipaddress)
return True
except ipaddress.AddressValueError as err:
return False


def get_dynamic_neighbor_subnet(db):
"""
Returns dict of description and subnet info from bgp_peer_range table
:param db: config_db
"""
dynamic_neighbor = {}
v4_subnet = {}
v6_subnet = {}
neighbor_data = db.get_table('BGP_PEER_RANGE')
try:
for entry in neighbor_data.keys():
new_key = neighbor_data[entry]['ip_range'][0]
new_value = neighbor_data[entry]['name']
if is_ipv4_address(unicode(neighbor_data[entry]['src_address'])):
v4_subnet[new_key] = new_value
elif is_ipv6_address(unicode(neighbor_data[entry]['src_address'])):
v6_subnet[new_key] = new_value
dynamic_neighbor["v4"] = v4_subnet
dynamic_neighbor["v6"] = v6_subnet
return dynamic_neighbor
except Exception:
return neighbor_data


def get_bgp_neighbors_dict():
"""
Uses config_db to get the bgp neighbors and names in dictionary format
:return:
"""
dynamic_neighbors = {}
config_db = connect_config_db()
static_neighbors = get_neighbor_dict_from_table(config_db, 'BGP_NEIGHBOR')
bgp_monitors = get_neighbor_dict_from_table(config_db, 'BGP_MONITORS')
static_neighbors.update(bgp_monitors)
dynamic_neighbors = get_dynamic_neighbor_subnet(config_db)
return static_neighbors, dynamic_neighbors


def get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors):
"""
return neighbor name for the ip provided
:param ip: ip address unicode
:param static_neighbors: statically defined bgp neighbors dict
:param dynamic_neighbors: subnet of dynamically defined neighbors dict
:return: name of neighbor
"""
if ip in static_neighbors.keys():
return static_neighbors[ip]
elif is_ipv4_address(unicode(ip)):
for subnet in dynamic_neighbors["v4"].keys():
if ipaddress.IPv4Address(unicode(ip)) in ipaddress.IPv4Network(unicode(subnet)):
return dynamic_neighbors["v4"][subnet]
elif is_ipv6_address(unicode(ip)):
for subnet in dynamic_neighbors["v6"].keys():
if ipaddress.IPv6Address(unicode(ip)) in ipaddress.IPv6Network(unicode(subnet)):
return dynamic_neighbors["v6"][subnet]
else:
return "NotAvailable"


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

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


#
# Inserting BGP functionality into cli's show parse-chain.
# BGP commands are determined by the routing-stack being elected.
Expand Down
137 changes: 137 additions & 0 deletions tests/bgp_commands_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import os

import pytest

from click.testing import CliRunner

show_bgp_summary_v4 = """\

IPv4 Unicast Summary:
BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0
BGP table version 12811
RIB entries 12817, using 2358328 bytes of memory
Peers 24, using 502080 KiB of memory
Peer groups 4, using 256 bytes of memory


Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName
----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- --------------
10.0.0.1 4 65200 5919 2717 0 0 0 1d21h11m 6402 ARISTA01T2
10.0.0.5 4 65200 5916 2714 0 0 0 1d21h10m 6402 ARISTA03T2
10.0.0.9 4 65200 5915 2713 0 0 0 1d21h09m 6402 ARISTA05T2
10.0.0.13 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA07T2
10.0.0.17 4 65200 5916 2713 0 0 0 1d21h09m 6402 ARISTA09T2
10.0.0.21 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA11T2
10.0.0.25 4 65200 5917 2716 0 0 0 1d21h11m 6402 ARISTA13T2
10.0.0.29 4 65200 5916 2714 0 0 0 1d21h10m 6402 ARISTA15T2
10.0.0.33 4 64001 0 0 0 0 0 never Active ARISTA01T0
10.0.0.35 4 64002 0 0 0 0 0 never Active ARISTA02T0
10.0.0.37 4 64003 0 0 0 0 0 never Active ARISTA03T0
10.0.0.39 4 64004 0 0 0 0 0 never Active ARISTA04T0
10.0.0.41 4 64005 0 0 0 0 0 never Active ARISTA05T0
10.0.0.43 4 64006 0 0 0 0 0 never Active ARISTA06T0
10.0.0.45 4 64007 0 0 0 0 0 never Active ARISTA07T0
10.0.0.47 4 64008 0 0 0 0 0 never Active ARISTA08T0
10.0.0.49 4 64009 0 0 0 0 0 never Active ARISTA09T0
10.0.0.51 4 64010 0 0 0 0 0 never Active ARISTA10T0
10.0.0.53 4 64011 0 0 0 0 0 never Active ARISTA11T0
10.0.0.55 4 64012 0 0 0 0 0 never Active ARISTA12T0
10.0.0.57 4 64013 0 0 0 0 0 never Active ARISTA13T0
10.0.0.59 4 64014 0 0 0 0 0 never Active ARISTA14T0
10.0.0.61 4 64015 0 0 0 0 0 never Active ARISTA15T0
10.0.0.63 4 64016 0 0 0 0 0 never Active ARISTA16T0
"""

show_bgp_summary_v6 = """\

IPv6 Unicast Summary:
BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0
BGP table version 8972
RIB entries 12817, using 2358328 bytes of memory
Peers 24, using 502080 KiB of memory
Peer groups 4, using 256 bytes of memory


Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName
----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- --------------
fc00::1a 4 65200 6665 6672 0 0 0 2d09h39m 6402 ARISTA07T2
fc00::2 4 65200 6666 7913 0 0 0 2d09h39m 6402 ARISTA01T2
fc00::2a 4 65200 6666 7913 0 0 0 2d09h39m 6402 ARISTA11T2
fc00::3a 4 65200 6666 7912 0 0 0 2d09h39m 6402 ARISTA15T2
fc00::4a 4 64003 0 0 0 0 0 never Active ARISTA03T0
fc00::4e 4 64004 0 0 0 0 0 never Active ARISTA04T0
fc00::5a 4 64007 0 0 0 0 0 never Active ARISTA07T0
fc00::5e 4 64008 0 0 0 0 0 never Active ARISTA08T0
fc00::6a 4 64011 0 0 0 0 0 never Connect ARISTA11T0
fc00::6e 4 64012 0 0 0 0 0 never Active ARISTA12T0
fc00::7a 4 64015 0 0 0 0 0 never Active ARISTA15T0
fc00::7e 4 64016 0 0 0 0 0 never Active ARISTA16T0
fc00::12 4 65200 6666 7915 0 0 0 2d09h39m 6402 ARISTA05T2
fc00::22 4 65200 6667 7915 0 0 0 2d09h39m 6402 ARISTA09T2
fc00::32 4 65200 6663 6669 0 0 0 2d09h36m 6402 ARISTA13T2
fc00::42 4 64001 0 0 0 0 0 never Active ARISTA01T0
fc00::46 4 64002 0 0 0 0 0 never Active ARISTA02T0
fc00::52 4 64005 0 0 0 0 0 never Active ARISTA05T0
fc00::56 4 64006 0 0 0 0 0 never Active ARISTA06T0
fc00::62 4 64009 0 0 0 0 0 never Active ARISTA09T0
fc00::66 4 64010 0 0 0 0 0 never Active ARISTA10T0
fc00::72 4 64013 0 0 0 0 0 never Active ARISTA13T0
fc00::76 4 64014 0 0 0 0 0 never Active ARISTA14T0
fc00::a 4 65200 6665 6671 0 0 0 2d09h38m 6402 ARISTA03T2
"""

show_error_invalid_json = """\
Usage: summary [OPTIONS]
Try 'summary --help' for help.

Error: bgp summary from bgp container not in json format
"""


class TestBgpCommands(object):
@classmethod
def setup_class(cls):
print("SETUP")
import mock_tables.dbconnector

@pytest.mark.parametrize('setup_single_bgp_instance',
['v4'], indirect=['setup_single_bgp_instance'])
def test_bgp_summary_v4(
self,
setup_bgp_commands,
setup_single_bgp_instance):
show = setup_bgp_commands
runner = CliRunner()
result = runner.invoke(
show.cli.commands["ip"].commands["bgp"].commands["summary"], [])
print("{}".format(result.output))
assert result.exit_code == 0
assert result.output == show_bgp_summary_v4

@pytest.mark.parametrize('setup_single_bgp_instance',
['v6'], indirect=['setup_single_bgp_instance'])
def test_bgp_summary_v6(
self,
setup_bgp_commands,
setup_single_bgp_instance):
show = setup_bgp_commands
runner = CliRunner()
result = runner.invoke(
show.cli.commands["ipv6"].commands["bgp"].commands["summary"], [])
print("{}".format(result.output))
assert result.exit_code == 0
assert result.output == show_bgp_summary_v6

@pytest.mark.parametrize('setup_single_bgp_instance',
[' '], indirect=['setup_single_bgp_instance'])
def test_bgp_summary_error(
self,
setup_bgp_commands,
setup_single_bgp_instance):
show = setup_bgp_commands
runner = CliRunner()
result = runner.invoke(
show.cli.commands["ipv6"].commands["bgp"].commands["summary"], [])
print("{}".format(result.output))
assert result.exit_code == 2
assert result.output == show_error_invalid_json
Loading