-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
69ff620
commit cb4d72e
Showing
12 changed files
with
403 additions
and
1 deletion.
There are no files selected for viewing
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import re | ||
import ipaddress | ||
import socket | ||
|
||
HOST = '127.0.0.1' | ||
PORT = 2605 | ||
|
||
def union_bgp_sessions(): | ||
bgpsumm_ipv4 = show_bgp_summary('ip') | ||
sessions_ipv4 = parse_bgp_summary(bgpsumm_ipv4) | ||
|
||
bgpsumm_ipv6 = show_bgp_summary('ipv6') | ||
sessions_ipv6 = parse_bgp_summary(bgpsumm_ipv6) | ||
|
||
# Note: sessions_ipv4 will overwrite sessions_ipv6 if key is the same | ||
sessions = {} | ||
for ses in sessions_ipv6 + sessions_ipv4: | ||
nei = ses['Neighbor'] | ||
sessions[nei] = ses | ||
return sessions | ||
|
||
def vtysh_run(command): | ||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
s.connect((HOST, PORT)) | ||
|
||
cmd = b"zebra\n" + command.encode() + b"\nexit\n" | ||
s.send(cmd) | ||
|
||
acc = b"" | ||
while True: | ||
data = s.recv(1024) | ||
if not data: | ||
break | ||
acc += data | ||
|
||
s.close() | ||
return acc.decode('ascii', 'ignore') | ||
|
||
def show_bgp_summary(ipver): | ||
assert(ipver in ['ip', 'ipv6']) | ||
try: | ||
result = vtysh_run('show %s bgp summary' % ipver) | ||
|
||
except ConnectionRefusedError as e: | ||
raise RuntimeError('Failed to connect quagga socket') from e | ||
except OSError as e: | ||
raise RuntimeError('Socket error when talking with quagga') from e | ||
return result | ||
|
||
def parse_bgp_summary(summ): | ||
ls = summ.splitlines() | ||
bgpinfo = [] | ||
|
||
## Read until the table header | ||
n = len(ls) | ||
li = 0 | ||
while li < n: | ||
l = ls[li] | ||
if l.startswith('Neighbor '): break | ||
if l.startswith('No IPv'): # eg. No IPv6 neighbor is configured | ||
return bgpinfo | ||
if l.endswith('> exit'): # last command in the lines | ||
return bgpinfo | ||
li += 1 | ||
|
||
## Read and store the table header | ||
if li >= n: | ||
raise ValueError('No table header found') | ||
hl = ls[li] | ||
li += 1 | ||
ht = re.split('\s+', hl.rstrip()) | ||
hn = len(ht) | ||
|
||
## Read rows in the table | ||
while li < n: | ||
l = ls[li] | ||
li += 1 | ||
if l == '': break | ||
|
||
## Handle line wrap | ||
## ref: bgp_show_summary in https://github.com/Azure/sonic-quagga/blob/debian/0.99.24.1/bgpd/bgp_vty.c | ||
if ' ' not in l: | ||
## Read next line | ||
if li >= n: | ||
raise ValueError('Unexpected line wrap') | ||
l += ls[li] | ||
li += 1 | ||
|
||
## Note: State/PfxRcd field may be 'Idle (Admin)' | ||
lt = re.split('\s+', l.rstrip(), maxsplit = hn - 1) | ||
if len(lt) != hn: | ||
raise ValueError('Unexpected row in the table') | ||
dic = dict(zip(ht, lt)) | ||
bgpinfo.append(dic) | ||
return bgpinfo | ||
|
||
STATE_CODE = { | ||
"Idle": 1, | ||
"Idle (Admin)": 1, | ||
"Connect": 2, | ||
"Active": 3, | ||
"OpenSent": 4, | ||
"OpenConfirm": 5, | ||
"Established": 6 | ||
}; | ||
|
||
def bgp_peer_tuple(dic): | ||
nei = dic['Neighbor'] | ||
ver = dic['V'] | ||
sta = dic['State/PfxRcd'] | ||
|
||
# prefix '*' appears if the entry is a dynamic neighbor | ||
nei = nei[1:] if nei[0] == '*' else nei | ||
ip = ipaddress.ip_address(nei) | ||
if type(ip) is ipaddress.IPv4Address: | ||
oid_head = (1, 4) | ||
else: | ||
oid_head = (2, 16) | ||
|
||
oid_ip = tuple(i for i in ip.packed) | ||
|
||
if sta.isdigit(): | ||
status = 6 | ||
elif sta in STATE_CODE: | ||
status = STATE_CODE[sta] | ||
else: | ||
return None, None | ||
|
||
return oid_head + oid_ip, status | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
from . import mgmt | ||
from . import mgmt, bgp4 | ||
from . import ciscoPfcExtMIB | ||
from . import ciscoSwitchQosMIB | ||
from . import ciscoEntityFruControlMIB |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from bisect import bisect_right | ||
from sonic_ax_impl import mibs | ||
from sonic_ax_impl.lib import vtysh_helper | ||
from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry | ||
from ax_interface.mib import MIBEntry | ||
|
||
class BgpSessionUpdater(MIBUpdater): | ||
def __init__(self): | ||
super().__init__() | ||
self.session_status_map = {} | ||
self.session_status_list = [] | ||
|
||
def update_data(self): | ||
self.session_status_map = {} | ||
self.session_status_list = [] | ||
|
||
try: | ||
sessions = vtysh_helper.union_bgp_sessions() | ||
except RuntimeError as e: | ||
mibs.logger.error("Failed to union bgp sessions: {}.".format(e)) | ||
return | ||
|
||
for nei, ses in sessions.items(): | ||
oid, status = vtysh_helper.bgp_peer_tuple(ses) | ||
if oid is None: continue | ||
self.session_status_list.append(oid) | ||
self.session_status_map[oid] = status | ||
|
||
self.session_status_list.sort() | ||
|
||
def sessionstatus(self, sub_id): | ||
return self.session_status_map.get(sub_id, None) | ||
|
||
def get_next(self, sub_id): | ||
right = bisect_right(self.session_status_list, sub_id) | ||
if right >= len(self.session_status_list): | ||
return None | ||
|
||
return self.session_status_list[right] | ||
|
||
|
||
class CiscoBgp4MIB(metaclass=MIBMeta, prefix='.1.3.6.1.4.1.9.9.187'): | ||
bgpsession_updater = BgpSessionUpdater() | ||
|
||
cbgpPeer2State = SubtreeMIBEntry('1.2.5.1.3', bgpsession_updater, ValueType.INTEGER, bgpsession_updater.sessionstatus) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
import tests.mock_tables.socket | ||
import tests.mock_tables.imp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
|
||
Hello, this is Quagga (version 0.99.24.1). | ||
Copyright 1996-2005 Kunihiro Ishiguro, et al. | ||
|
||
User Access Verification | ||
|
||
"Password: | ||
str-msn2700-05> str-msn2700-05> show ip bgp summary | ||
BGP router identifier 10.1.0.32, local AS number 65100 | ||
RIB entries 13025, using 1425 KiB of memory | ||
Peers 16, using 291 KiB of memory | ||
|
||
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd | ||
10.0.0.57 4 64600 3253 3255 0 0 0 00:48:54 6402 | ||
10.0.0.59 4 64600 3252 3258 0 0 0 00:48:56 6402 | ||
10.0.0.61 4 64600 3253 3256 0 0 0 00:48:55 6402 | ||
10.0.0.63 4 64600 3252 56 0 0 0 00:48:57 6402 | ||
10.0.0.65 4 64016 205 9956 0 0 0 13:21:23 Idle | ||
10.0.0.67 4 65200 3210 3246 0 0 0 00:00:11 Idle (Admin) | ||
fc00::72 4 64600 3253 3255 0 0 0 00:48:54 0 | ||
fc00::76 4 64600 3253 3255 0 0 0 00:48:54 0 | ||
fc00::7a 4 64600 3253 3255 0 0 0 00:48:55 0 | ||
fc00::7e 4 64600 3253 54 0 0 0 00:48:55 0 | ||
fc00::2 4 65200 6608 6790 0 0 0 13:21:22 Active | ||
fc00::4 4 65200 6608 6790 0 0 0 13:21:22 Connect | ||
fc00::6 4 65200 6608 6790 0 0 0 13:21:22 OpenSent | ||
fc00::8 4 65200 6608 6790 0 0 0 13:21:22 OpenConfirm | ||
fc00::10 4 65200 6608 6790 0 0 0 13:21:22 Clearing | ||
fc00::12 4 65200 6608 6790 0 0 0 13:21:22 Deleted | ||
|
||
Total number of neighbors 15 | ||
str-msn2700-05> | ||
str-msn2700-05> exit | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
Hello, this is Quagga (version 0.99.24.1). | ||
Copyright 1996-2005 Kunihiro Ishiguro, et al. | ||
|
||
|
||
User Access Verification | ||
|
||
"Password: | ||
str-msn2700-05> | ||
BGP router identifier 10.1.0.32, local AS number 65100 | ||
RIB entries 13025, using 1425 KiB of memory | ||
Peers 11, using 291 KiB of memory | ||
|
||
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd | ||
fc00::72 4 64600 3253 3255 0 0 0 00:48:54 Idle (Admin) | ||
fc00::76 4 64600 3253 3255 0 0 0 00:48:54 0 | ||
fc00::7a 4 64600 3253 3255 0 0 0 00:48:55 0 | ||
fc00::7e 4 64600 3253 54 0 0 0 00:48:55 0 | ||
fc00::2 4 65200 6608 6790 0 0 0 13:21:22 Active | ||
fc00::4 4 65200 6608 6790 0 0 0 13:21:22 Connect | ||
fc00::6 4 65200 6608 6790 0 0 0 13:21:22 OpenSent | ||
fc00::8 4 65200 6608 6790 0 0 0 13:21:22 OpenConfirm | ||
fc00::10 4 65200 6608 6790 0 0 0 13:21:22 Clearing | ||
fc00::12 4 65200 6608 6790 0 0 0 13:21:22 Deleted | ||
2603:10b0:2800:cc1::2e | ||
4 64611 2919 92 0 0 0 01:18:59 0 | ||
|
||
Total number of neighbors 11 | ||
str-msn2700-05> | ||
str-msn2700-05> exit | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Hello, this is Quagga (version 0.99.24.1). Copyright 1996-2005 Kunihiro Ishiguro, et al. User Access Verification | ||
"Password: | ||
str-msn2700-03> show ipv6 bgp summary | ||
str-msn2700-03> exit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import os | ||
from collections import namedtuple | ||
import unittest | ||
from unittest import TestCase, mock | ||
from unittest.mock import patch, mock_open, MagicMock | ||
|
||
INPUT_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
|
||
import socket | ||
|
||
# Backup original class | ||
_socket_class = socket.socket | ||
|
||
# Monkey patch | ||
class MockSocket(_socket_class): | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(MockSocket, self).__init__(*args, **kwargs) | ||
self._string_sent = b'' | ||
|
||
def connect(self, *args, **kwargs): | ||
pass | ||
|
||
def send(self, *args, **kwargs): | ||
string = args[0] | ||
self._string_sent = string | ||
pass | ||
|
||
def recv(self, *args, **kwargs): | ||
if b'show ip bgp summary' in self._string_sent: | ||
filename = INPUT_DIR + '/bgpsummary_ipv4.txt' | ||
elif b'show ipv6 bgp summary' in self._string_sent: | ||
filename = INPUT_DIR + '/bgpsummary_ipv6.txt' | ||
else: | ||
return None | ||
|
||
self._string_sent = b'' | ||
ret = namedtuple('ret', ['returncode', 'stdout']) | ||
ret.returncode = 0 | ||
with open(filename, 'rb') as f: | ||
ret = f.read() | ||
return ret | ||
|
||
# Replace the function with mocked one | ||
socket.socket = MockSocket |
Oops, something went wrong.