Skip to content

Commit

Permalink
Implement BgpInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
qiluo-msft committed May 1, 2018
1 parent 69ff620 commit cb4d72e
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 1 deletion.
Empty file added __init__.py
Empty file.
Empty file.
130 changes: 130 additions & 0 deletions src/sonic_ax_impl/lib/vtysh_helper.py
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

1 change: 1 addition & 0 deletions src/sonic_ax_impl/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SonicMIB(
ieee802_1ab.LLDPRemTable,
dell.force10.SSeriesMIB,
cisco.mgmt.CiscoSystemExtMIB,
cisco.bgp4.CiscoBgp4MIB,
cisco.ciscoPfcExtMIB.cpfcIfTable,
cisco.ciscoPfcExtMIB.cpfcIfPriorityTable,
cisco.ciscoSwitchQosMIB.csqIfQosGroupStatsTable,
Expand Down
2 changes: 1 addition & 1 deletion src/sonic_ax_impl/mibs/vendor/cisco/__init__.py
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
45 changes: 45 additions & 0 deletions src/sonic_ax_impl/mibs/vendor/cisco/bgp4.py
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)
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
import tests.mock_tables.socket
import tests.mock_tables.imp
35 changes: 35 additions & 0 deletions tests/mock_tables/bgpsummary_ipv4.txt
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


31 changes: 31 additions & 0 deletions tests/mock_tables/bgpsummary_ipv6.txt
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

4 changes: 4 additions & 0 deletions tests/mock_tables/bgpsummary_ipv6_nobgp.txt
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
45 changes: 45 additions & 0 deletions tests/mock_tables/socket.py
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
Loading

0 comments on commit cb4d72e

Please sign in to comment.