summaryrefslogtreecommitdiffstats
path: root/tests/topotests/lib
diff options
context:
space:
mode:
Diffstat (limited to 'tests/topotests/lib')
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/open/__init__.py32
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/__init__.py30
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/af.py6
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/nlri.py62
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py119
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/rd.py24
-rw-r--r--tests/topotests/lib/bmp_collector/bmp.py173
-rwxr-xr-xtests/topotests/lib/bmp_collector/bmpserver4
-rw-r--r--tests/topotests/lib/checkping.py12
-rw-r--r--tests/topotests/lib/common_config.py16
-rwxr-xr-xtests/topotests/lib/fe_client.py2
-rwxr-xr-xtests/topotests/lib/grpc-query.py96
-rw-r--r--tests/topotests/lib/snmptest.py13
-rw-r--r--tests/topotests/lib/topogen.py25
-rw-r--r--tests/topotests/lib/topojson.py8
-rw-r--r--tests/topotests/lib/topotest.py64
16 files changed, 393 insertions, 293 deletions
diff --git a/tests/topotests/lib/bmp_collector/bgp/open/__init__.py b/tests/topotests/lib/bmp_collector/bgp/open/__init__.py
index 6c814ee..e1e6b51 100644
--- a/tests/topotests/lib/bmp_collector/bgp/open/__init__.py
+++ b/tests/topotests/lib/bmp_collector/bgp/open/__init__.py
@@ -8,27 +8,29 @@ import struct
class BGPOpen:
- UNPACK_STR = '!16sHBBHH4sB'
+ UNPACK_STR = "!16sHBBHH4sB"
@classmethod
def dissect(cls, data):
- (marker,
- length,
- open_type,
- version,
- my_as,
- hold_time,
- bgp_id,
- optional_params_len) = struct.unpack_from(cls.UNPACK_STR, data)
+ (
+ marker,
+ length,
+ open_type,
+ version,
+ my_as,
+ hold_time,
+ bgp_id,
+ optional_params_len,
+ ) = struct.unpack_from(cls.UNPACK_STR, data)
- data = data[struct.calcsize(cls.UNPACK_STR) + optional_params_len:]
+ data = data[struct.calcsize(cls.UNPACK_STR) + optional_params_len :]
# XXX: parse optional parameters
return data, {
- 'version': version,
- 'my_as': my_as,
- 'hold_time': hold_time,
- 'bgp_id': ipaddress.ip_address(bgp_id),
- 'optional_params_len': optional_params_len,
+ "version": version,
+ "my_as": my_as,
+ "hold_time": hold_time,
+ "bgp_id": ipaddress.ip_address(bgp_id),
+ "optional_params_len": optional_params_len,
}
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/__init__.py b/tests/topotests/lib/bmp_collector/bgp/update/__init__.py
index d079b35..629e175 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/__init__.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/__init__.py
@@ -10,45 +10,47 @@ from .nlri import NlriIPv4Unicast
from .path_attributes import PathAttribute
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BGPUpdate:
- UNPACK_STR = '!16sHBH'
+ UNPACK_STR = "!16sHBH"
STATIC_SIZE = 23
@classmethod
def dissect(cls, data):
- msg = {'bmp_log_type': 'update'}
+ msg = {"bmp_log_type": "update"}
common_size = struct.calcsize(cls.UNPACK_STR)
- (marker,
- length,
- update_type,
- withdrawn_routes_len) = struct.unpack_from(cls.UNPACK_STR, data)
+ (marker, length, update_type, withdrawn_routes_len) = struct.unpack_from(
+ cls.UNPACK_STR, data
+ )
# get withdrawn routes
- withdrawn_routes = ''
+ withdrawn_routes = ""
if withdrawn_routes_len:
withdrawn_routes = NlriIPv4Unicast.parse(
- data[common_size:common_size + withdrawn_routes_len]
+ data[common_size : common_size + withdrawn_routes_len]
)
- msg['bmp_log_type'] = 'withdraw'
+ msg["bmp_log_type"] = "withdraw"
msg.update(withdrawn_routes)
# get path attributes
(total_path_attrs_len,) = struct.unpack_from(
- '!H', data[common_size+withdrawn_routes_len:])
+ "!H", data[common_size + withdrawn_routes_len :]
+ )
if total_path_attrs_len:
offset = cls.STATIC_SIZE + withdrawn_routes_len
- path_attrs_data = data[offset:offset + total_path_attrs_len]
+ path_attrs_data = data[offset : offset + total_path_attrs_len]
while path_attrs_data:
path_attrs_data, pattr = PathAttribute.dissect(path_attrs_data)
if pattr:
msg = {**msg, **pattr}
# get nlri
- nlri_len = length - cls.STATIC_SIZE - withdrawn_routes_len - total_path_attrs_len
+ nlri_len = (
+ length - cls.STATIC_SIZE - withdrawn_routes_len - total_path_attrs_len
+ )
if nlri_len > 0:
- nlri = NlriIPv4Unicast.parse(data[length - nlri_len:length])
+ nlri = NlriIPv4Unicast.parse(data[length - nlri_len : length])
msg.update(nlri)
return data[length:], msg
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/af.py b/tests/topotests/lib/bmp_collector/bgp/update/af.py
index 01af1ae..200b15a 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/af.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/af.py
@@ -19,7 +19,7 @@ SAFI_IP_FLOWSPEC = 133
SAFI_VPN_FLOWSPEC = 134
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class AddressFamily:
def __init__(self, afi, safi):
self.afi = afi
@@ -31,13 +31,13 @@ class AddressFamily:
return (self.afi, self.safi) == (other.afi, other.safi)
def __str__(self):
- return f'afi: {self.afi}, safi: {self.safi}'
+ return f"afi: {self.afi}, safi: {self.safi}"
def __hash__(self):
return hash((self.afi, self.safi))
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class AF:
IPv4_UNICAST = AddressFamily(AFI_IP, SAFI_UNICAST)
IPv6_UNICAST = AddressFamily(AFI_IP6, SAFI_UNICAST)
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/nlri.py b/tests/topotests/lib/bmp_collector/bgp/update/nlri.py
index c1720f1..b362520 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/nlri.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/nlri.py
@@ -13,7 +13,8 @@ from .rd import RouteDistinguisher
def decode_label(label):
# from frr
# frr encode just one label
- return (label[0] << 12) | (label[1] << 4) | (label[2] & 0xf0) >> 4
+ return (label[0] << 12) | (label[1] << 4) | (label[2] & 0xF0) >> 4
+
def padding(databin, len_):
"""
@@ -23,7 +24,8 @@ def padding(databin, len_):
"""
if len(databin) >= len_:
return databin
- return databin + b'\0' * (len_ - len(databin))
+ return databin + b"\0" * (len_ - len(databin))
+
def dissect_nlri(nlri_data, afi, safi):
"""
@@ -37,35 +39,34 @@ def dissect_nlri(nlri_data, afi, safi):
elif addr_family == AF.IPv6_UNICAST:
return NlriIPv6Unicast.parse(nlri_data)
- return {'ip_prefix': 'Unknown'}
+ return {"ip_prefix": "Unknown"}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv4Unicast:
-
@staticmethod
def parse(data):
"""parses prefixes from withdrawn_routes or nrli data"""
- (prefix_len,) = struct.unpack_from('!B', data)
+ (prefix_len,) = struct.unpack_from("!B", data)
prefix = padding(data[1:], 4)
- return {'ip_prefix': f'{ipaddress.IPv4Address(prefix)}/{prefix_len}'}
+ return {"ip_prefix": f"{ipaddress.IPv4Address(prefix)}/{prefix_len}"}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv6Unicast:
@staticmethod
def parse(data):
"""parses prefixes from withdrawn_routes or nrli data"""
- (prefix_len,) = struct.unpack_from('!B', data)
+ (prefix_len,) = struct.unpack_from("!B", data)
prefix = padding(data[1:], 16)
- return {'ip_prefix': f'{ipaddress.IPv6Address(prefix)}/{prefix_len}'}
+ return {"ip_prefix": f"{ipaddress.IPv6Address(prefix)}/{prefix_len}"}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv4Vpn:
- UNPACK_STR = '!B3s8s'
+ UNPACK_STR = "!B3s8s"
@classmethod
def parse(cls, data):
@@ -74,17 +75,17 @@ class NlriIPv4Vpn:
ipv4 = padding(data[offset:], 4)
# prefix_len = total_bits_len - label_bits_len - rd_bits_len
- prefix_len = bit_len - 3*8 - 8*8
+ prefix_len = bit_len - 3 * 8 - 8 * 8
return {
- 'label': decode_label(label),
- 'rd': str(RouteDistinguisher(rd)),
- 'ip_prefix': f'{ipaddress.IPv4Address(ipv4)}/{prefix_len}',
+ "label": decode_label(label),
+ "rd": str(RouteDistinguisher(rd)),
+ "ip_prefix": f"{ipaddress.IPv4Address(ipv4)}/{prefix_len}",
}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv6Vpn:
- UNPACK_STR = '!B3s8s'
+ UNPACK_STR = "!B3s8s"
@classmethod
def parse(cls, data):
@@ -93,48 +94,49 @@ class NlriIPv6Vpn:
offset = struct.calcsize(cls.UNPACK_STR)
ipv6 = padding(data[offset:], 16)
- prefix_len = bit_len - 3*8 - 8*8
+ prefix_len = bit_len - 3 * 8 - 8 * 8
return {
- 'label': decode_label(label),
- 'rd': str(RouteDistinguisher(rd)),
- 'ip_prefix': f'{ipaddress.IPv6Address(ipv6)}/{prefix_len}',
+ "label": decode_label(label),
+ "rd": str(RouteDistinguisher(rd)),
+ "ip_prefix": f"{ipaddress.IPv6Address(ipv6)}/{prefix_len}",
}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv4Mpls:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv6Mpls:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv4FlowSpec:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriIPv6FlowSpec:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriVpn4FlowSpec:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriVpn6FlowSpec:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class NlriL2EVPN:
pass
-#------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
class NlriL2VPNFlowSpec:
pass
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
index 6e82e9c..3694cb4 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
@@ -38,17 +38,18 @@ ORIGIN_EGP = 0x01
ORIGIN_INCOMPLETE = 0x02
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttribute:
PATH_ATTRS = {}
UNKNOWN_ATTR = None
- UNPACK_STR = '!BB'
+ UNPACK_STR = "!BB"
@classmethod
def register_path_attr(cls, path_attr):
def _register_path_attr(subcls):
cls.PATH_ATTRS[path_attr] = subcls
return subcls
+
return _register_path_attr
@classmethod
@@ -61,7 +62,7 @@ class PathAttribute:
offset = struct.calcsize(cls.UNPACK_STR)
# get attribute length
- attr_len_str = '!H' if (flags & PATH_ATTR_FLAG_EXTENDED_LENGTH) else '!B'
+ attr_len_str = "!H" if (flags & PATH_ATTR_FLAG_EXTENDED_LENGTH) else "!B"
(attr_len,) = struct.unpack_from(attr_len_str, data[offset:])
@@ -69,32 +70,34 @@ class PathAttribute:
path_attr_cls = cls.lookup_path_attr(type_code)
if path_attr_cls == cls.UNKNOWN_ATTR:
- return data[offset + attr_len:], None
+ return data[offset + attr_len :], None
- return data[offset+attr_len:], path_attr_cls.dissect(data[offset:offset+attr_len])
+ return data[offset + attr_len :], path_attr_cls.dissect(
+ data[offset : offset + attr_len]
+ )
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@PathAttribute.register_path_attr(PATH_ATTR_TYPE_ORIGIN)
class PathAttrOrigin:
ORIGIN_STR = {
- ORIGIN_IGP: 'IGP',
- ORIGIN_EGP: 'EGP',
- ORIGIN_INCOMPLETE: 'INCOMPLETE',
+ ORIGIN_IGP: "IGP",
+ ORIGIN_EGP: "EGP",
+ ORIGIN_INCOMPLETE: "INCOMPLETE",
}
@classmethod
def dissect(cls, data):
- (origin,) = struct.unpack_from('!B', data)
+ (origin,) = struct.unpack_from("!B", data)
- return {'origin': cls.ORIGIN_STR.get(origin, 'UNKNOWN')}
+ return {"origin": cls.ORIGIN_STR.get(origin, "UNKNOWN")}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@PathAttribute.register_path_attr(PATH_ATTR_TYPE_AS_PATH)
class PathAttrAsPath:
AS_PATH_TYPE_SET = 0x01
- AS_PATH_TYPE_SEQUENCE= 0x02
+ AS_PATH_TYPE_SEQUENCE = 0x02
@staticmethod
def get_asn_len(asns):
@@ -103,34 +106,34 @@ class PathAttrAsPath:
@classmethod
def dissect(cls, data):
- (_type, _len) = struct.unpack_from('!BB', data)
+ (_type, _len) = struct.unpack_from("!BB", data)
data = data[2:]
- _type_str = 'Ordred' if _type == cls.AS_PATH_TYPE_SEQUENCE else 'Raw'
+ _type_str = "Ordred" if _type == cls.AS_PATH_TYPE_SEQUENCE else "Raw"
segment = []
while data:
- (asn,) = struct.unpack_from('!I', data)
+ (asn,) = struct.unpack_from("!I", data)
segment.append(asn)
data = data[4:]
- return {'as_path': ' '.join(str(a) for a in segment)}
+ return {"as_path": " ".join(str(a) for a in segment)}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@PathAttribute.register_path_attr(PATH_ATTR_TYPE_NEXT_HOP)
class PathAttrNextHop:
@classmethod
def dissect(cls, data):
- (nexthop,) = struct.unpack_from('!4s', data)
- return {'bgp_nexthop': str(ipaddress.IPv4Address(nexthop))}
+ (nexthop,) = struct.unpack_from("!4s", data)
+ return {"bgp_nexthop": str(ipaddress.IPv4Address(nexthop))}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrMultiExitDisc:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@PathAttribute.register_path_attr(PATH_ATTR_TYPE_MP_REACH_NLRI)
class PathAttrMpReachNLRI:
"""
@@ -162,7 +165,8 @@ class PathAttrMpReachNLRI:
| Network Layer Reachability Information (variable) |
+---------------------------------------------------------+
"""
- UNPACK_STR = '!HBB'
+
+ UNPACK_STR = "!HBB"
NLRI_RESERVED_LEN = 1
@staticmethod
@@ -170,35 +174,35 @@ class PathAttrMpReachNLRI:
msg = {}
if nexthop_len == 4:
# IPv4
- (ipv4,) = struct.unpack_from('!4s', nexthop_data)
- msg['nxhp_ip'] = str(ipaddress.IPv4Address(ipv4))
+ (ipv4,) = struct.unpack_from("!4s", nexthop_data)
+ msg["nxhp_ip"] = str(ipaddress.IPv4Address(ipv4))
elif nexthop_len == 12:
# RD + IPv4
- (rd, ipv4) = struct.unpack_from('!8s4s', nexthop_data)
- msg['nxhp_ip'] = str(ipaddress.IPv4Address(ipv4))
- msg['nxhp_rd'] = str(RouteDistinguisher(rd))
+ (rd, ipv4) = struct.unpack_from("!8s4s", nexthop_data)
+ msg["nxhp_ip"] = str(ipaddress.IPv4Address(ipv4))
+ msg["nxhp_rd"] = str(RouteDistinguisher(rd))
elif nexthop_len == 16:
# IPv6
- (ipv6,) = struct.unpack_from('!16s', nexthop_data)
- msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
+ (ipv6,) = struct.unpack_from("!16s", nexthop_data)
+ msg["nxhp_ip"] = str(ipaddress.IPv6Address(ipv6))
elif nexthop_len == 24:
# RD + IPv6
- (rd, ipv6) = struct.unpack_from('!8s16s', nexthop_data)
- msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
- msg['nxhp_rd'] = str(RouteDistinguisher(rd))
+ (rd, ipv6) = struct.unpack_from("!8s16s", nexthop_data)
+ msg["nxhp_ip"] = str(ipaddress.IPv6Address(ipv6))
+ msg["nxhp_rd"] = str(RouteDistinguisher(rd))
elif nexthop_len == 32:
# IPv6 + IPv6 link-local
- (ipv6, link_local)= struct.unpack_from('!16s16s', nexthop_data)
- msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
- msg['nxhp_link-local'] = str(ipaddress.IPv6Address(link_local))
+ (ipv6, link_local) = struct.unpack_from("!16s16s", nexthop_data)
+ msg["nxhp_ip"] = str(ipaddress.IPv6Address(ipv6))
+ msg["nxhp_link-local"] = str(ipaddress.IPv6Address(link_local))
elif nexthop_len == 48:
# RD + IPv6 + RD + IPv6 link-local
- u_str = '!8s16s8s16s'
- (rd1, ipv6, rd2, link_local)= struct.unpack_from(u_str, nexthop_data)
- msg['nxhp_rd1'] = str(RouteDistinguisher(rd1))
- msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
- msg['nxhp_rd2'] = str(RouteDistinguisher(rd2))
- msg['nxhp_link-local'] = str(ipaddress.IPv6Address(link_local))
+ u_str = "!8s16s8s16s"
+ (rd1, ipv6, rd2, link_local) = struct.unpack_from(u_str, nexthop_data)
+ msg["nxhp_rd1"] = str(RouteDistinguisher(rd1))
+ msg["nxhp_ip"] = str(ipaddress.IPv6Address(ipv6))
+ msg["nxhp_rd2"] = str(RouteDistinguisher(rd2))
+ msg["nxhp_link-local"] = str(ipaddress.IPv6Address(link_local))
return msg
@@ -210,10 +214,10 @@ class PathAttrMpReachNLRI:
def dissect(cls, data):
(afi, safi, nexthop_len) = struct.unpack_from(cls.UNPACK_STR, data)
offset = struct.calcsize(cls.UNPACK_STR)
- msg = {'afi': afi, 'safi': safi}
+ msg = {"afi": afi, "safi": safi}
# dissect nexthop
- nexthop_data = data[offset: offset + nexthop_len]
+ nexthop_data = data[offset : offset + nexthop_len]
nexthop = cls.dissect_nexthop(nexthop_data, nexthop_len)
msg.update(nexthop)
@@ -227,7 +231,7 @@ class PathAttrMpReachNLRI:
return msg
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@PathAttribute.register_path_attr(PATH_ATTR_TYPE_MP_UNREACH_NLRI)
class PathAttrMpUnReachNLRI:
"""
@@ -239,13 +243,14 @@ class PathAttrMpUnReachNLRI:
| Withdrawn Routes (variable) |
+---------------------------------------------------------+
"""
- UNPACK_STR = '!HB'
+
+ UNPACK_STR = "!HB"
@classmethod
def dissect(cls, data):
(afi, safi) = struct.unpack_from(cls.UNPACK_STR, data)
offset = struct.calcsize(cls.UNPACK_STR)
- msg = {'bmp_log_type': 'withdraw','afi': afi, 'safi': safi}
+ msg = {"bmp_log_type": "withdraw", "afi": afi, "safi": safi}
if data[offset:]:
# dissect withdrawn_routes
@@ -254,51 +259,51 @@ class PathAttrMpUnReachNLRI:
return msg
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrLocalPref:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrAtomicAgregate:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrAggregator:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrCommunities:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrOriginatorID:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrClusterList:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrExtendedCommunities:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrPMSITunnel:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrLinkState:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class PathAttrLargeCommunities:
pass
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/rd.py b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
index c382fa8..3f08de5 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/rd.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
@@ -7,7 +7,7 @@ import ipaddress
import struct
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class RouteDistinguisher:
"""
type 0:
@@ -28,32 +28,32 @@ class RouteDistinguisher:
+ | 4-bytes AS number (4 bytes)| Service Provider 2 bytes)|
+-------------------------------------------------------------------------+
"""
+
def __init__(self, rd):
self.rd = rd
self.as_number = None
self.admin_ipv4 = None
self.four_bytes_as = None
self.assigned_sp = None
- self.repr_str = ''
+ self.repr_str = ""
self.dissect()
def dissect(self):
- (rd_type,) = struct.unpack_from('!H', self.rd)
+ (rd_type,) = struct.unpack_from("!H", self.rd)
if rd_type == 0:
- (self.as_number,
- self.assigned_sp) = struct.unpack_from('!HI', self.rd[2:])
- self.repr_str = f'{self.as_number}:{self.assigned_sp}'
+ (self.as_number, self.assigned_sp) = struct.unpack_from("!HI", self.rd[2:])
+ self.repr_str = f"{self.as_number}:{self.assigned_sp}"
elif rd_type == 1:
- (self.admin_ipv4,
- self.assigned_sp) = struct.unpack_from('!IH', self.rd[2:])
+ (self.admin_ipv4, self.assigned_sp) = struct.unpack_from("!IH", self.rd[2:])
ipv4 = str(ipaddress.IPv4Address(self.admin_ipv4))
- self.repr_str = f'{self.as_number}:{self.assigned_sp}'
+ self.repr_str = f"{self.as_number}:{self.assigned_sp}"
elif rd_type == 2:
- (self.four_bytes_as,
- self.assigned_sp) = struct.unpack_from('!IH', self.rd[2:])
- self.repr_str = f'{self.four_bytes_as}:{self.assigned_sp}'
+ (self.four_bytes_as, self.assigned_sp) = struct.unpack_from(
+ "!IH", self.rd[2:]
+ )
+ self.repr_str = f"{self.four_bytes_as}:{self.assigned_sp}"
def __str__(self):
return self.repr_str
diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py
index 57f642a..237decd 100644
--- a/tests/topotests/lib/bmp_collector/bmp.py
+++ b/tests/topotests/lib/bmp_collector/bmp.py
@@ -33,30 +33,33 @@ IS_FILTERED = 1 << 7
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
+
def bin2str_ipaddress(ip_bytes, is_ipv6=False):
if is_ipv6:
return str(ipaddress.IPv6Address(ip_bytes))
return str(ipaddress.IPv4Address(ip_bytes[-4:]))
-def log2file(logs):
+
+def log2file(logs, log_file):
"""
XXX: extract the useful information and save it in a flat dictionnary
"""
- with open(LOG_FILE, 'a') as f:
+ with open(log_file, "a") as f:
f.write(json.dumps(logs) + "\n")
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPCodes:
"""
XXX: complete the list, provide RFCs.
"""
+
VERSION = 0x3
BMP_MSG_TYPE_ROUTE_MONITORING = 0x00
BMP_MSG_TYPE_STATISTICS_REPORT = 0x01
- BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION = 0x02
- BMP_MSG_TYPE_PEER_UP_NOTIFICATION = 0x03
+ BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION = 0x02
+ BMP_MSG_TYPE_PEER_UP_NOTIFICATION = 0x03
BMP_MSG_TYPE_INITIATION = 0x04
BMP_MSG_TYPE_TERMINATION = 0x05
BMP_MSG_TYPE_ROUTE_MIRRORING = 0x06
@@ -107,15 +110,15 @@ class BMPCodes:
# peer down reason code
BMP_PEER_DOWN_LOCAL_NOTIFY = 0x01
- BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0X02
- BMP_PEER_DOWN_REMOTE_NOTIFY = 0X03
- BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0X04
+ BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0x02
+ BMP_PEER_DOWN_REMOTE_NOTIFY = 0x03
+ BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0x04
BMP_PEER_DOWN_INFO_NO_LONGER = 0x05
- BMP_PEER_DOWN_SYSTEM_CLOSED = 0X06
+ BMP_PEER_DOWN_SYSTEM_CLOSED = 0x06
# termincation message types
BMP_TERM_TYPE_STRING = 0x00
- BMP_TERM_TYPE_REASON = 0X01
+ BMP_TERM_TYPE_REASON = 0x01
# termination reason code
BMP_TERM_REASON_ADMIN_CLOSE = 0x00
@@ -126,31 +129,32 @@ class BMPCodes:
# policy route tlv
BMP_ROUTE_POLICY_TLV_VRF = 0x00
- BMP_ROUTE_POLICY_TLV_POLICY= 0x01
+ BMP_ROUTE_POLICY_TLV_POLICY = 0x01
BMP_ROUTE_POLICY_TLV_PRE_POLICY = 0x02
BMP_ROUTE_POLICY_TLV_POST_POLICY = 0x03
BMP_ROUTE_POLICY_TLV_STRING = 0x04
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPMsg:
"""
XXX: should we move register_msg_type and look_msg_type
to generic Type class.
"""
+
TYPES = {}
UNKNOWN_TYPE = None
- HDR_STR = '!BIB'
+ HDR_STR = "!BIB"
MIN_LEN = struct.calcsize(HDR_STR)
TYPES_STR = {
- BMPCodes.BMP_MSG_TYPE_INITIATION: 'initiation',
- BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: 'peer down notification',
- BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: 'peer up notification',
- BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: 'route monitoring',
- BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: 'statistics report',
- BMPCodes.BMP_MSG_TYPE_TERMINATION: 'termination',
- BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: 'route mirroring',
- BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: 'route policy',
+ BMPCodes.BMP_MSG_TYPE_INITIATION: "initiation",
+ BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: "peer down notification",
+ BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: "peer up notification",
+ BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: "route monitoring",
+ BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: "statistics report",
+ BMPCodes.BMP_MSG_TYPE_TERMINATION: "termination",
+ BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: "route mirroring",
+ BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: "route policy",
}
@classmethod
@@ -158,6 +162,7 @@ class BMPMsg:
def _register_type(subcls):
cls.TYPES[msgtype] = subcls
return subcls
+
return _register_type
@classmethod
@@ -179,15 +184,15 @@ class BMPMsg:
if len(data) < cls.MIN_LEN:
pass
else:
- _version, _len, _type = struct.unpack(cls.HDR_STR, data[0:cls.MIN_LEN])
+ _version, _len, _type = struct.unpack(cls.HDR_STR, data[0 : cls.MIN_LEN])
return _version, _len, _type
@classmethod
- def dissect(cls, data):
+ def dissect(cls, data, log_file=None):
global SEQ
version, msglen, msgtype = cls.dissect_header(data)
- msg_data = data[cls.MIN_LEN:msglen]
+ msg_data = data[cls.MIN_LEN : msglen]
data = data[msglen:]
if version != BMPCodes.VERSION:
@@ -202,13 +207,13 @@ class BMPMsg:
msg_cls.MSG_LEN = msglen - cls.MIN_LEN
logs = msg_cls.dissect(msg_data)
logs["seq"] = SEQ
- log2file(logs)
+ log2file(logs, log_file if log_file else LOG_FILE)
SEQ += 1
return data
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPPerPeerMessage:
"""
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
@@ -229,30 +234,33 @@ class BMPPerPeerMessage:
| Timestamp (microseconds) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
- PEER_UNPACK_STR = '!BB8s16sI4sII'
+
+ PEER_UNPACK_STR = "!BB8s16sI4sII"
PEER_TYPE_STR = {
- BMPCodes.BMP_PEER_GLOBAL_INSTANCE: 'global instance',
- BMPCodes.BMP_PEER_RD_INSTANCE: 'route distinguisher instance',
- BMPCodes.BMP_PEER_LOCAL_INSTANCE: 'local instance',
- BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: 'loc-rib instance',
+ BMPCodes.BMP_PEER_GLOBAL_INSTANCE: "global instance",
+ BMPCodes.BMP_PEER_RD_INSTANCE: "route distinguisher instance",
+ BMPCodes.BMP_PEER_LOCAL_INSTANCE: "local instance",
+ BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: "loc-rib instance",
}
@classmethod
def dissect(cls, data):
- (peer_type,
- peer_flags,
- peer_distinguisher,
- peer_address,
- peer_asn,
- peer_bgp_id,
- timestamp_secs,
- timestamp_microsecs) = struct.unpack_from(cls.PEER_UNPACK_STR, data)
-
- msg = {'peer_type': cls.PEER_TYPE_STR[peer_type]}
+ (
+ peer_type,
+ peer_flags,
+ peer_distinguisher,
+ peer_address,
+ peer_asn,
+ peer_bgp_id,
+ timestamp_secs,
+ timestamp_microsecs,
+ ) = struct.unpack_from(cls.PEER_UNPACK_STR, data)
+
+ msg = {"peer_type": cls.PEER_TYPE_STR[peer_type]}
if peer_type == 0x03:
- msg['is_filtered'] = bool(peer_flags & IS_FILTERED)
- msg['policy'] = 'loc-rib'
+ msg["is_filtered"] = bool(peer_flags & IS_FILTERED)
+ msg["policy"] = "loc-rib"
else:
# peer_flags = 0x0000 0000
# ipv6, post-policy, as-path, adj-rib-out, reserverdx4
@@ -260,29 +268,29 @@ class BMPPerPeerMessage:
is_as_path = bool(peer_flags & IS_AS_PATH)
is_post_policy = bool(peer_flags & IS_POST_POLICY)
is_ipv6 = bool(peer_flags & IS_IPV6)
- msg['policy'] = 'post-policy' if is_post_policy else 'pre-policy'
- msg['ipv6'] = is_ipv6
- msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6)
-
+ msg["policy"] = "post-policy" if is_post_policy else "pre-policy"
+ msg["ipv6"] = is_ipv6
+ msg["peer_ip"] = bin2str_ipaddress(peer_address, is_ipv6)
peer_bgp_id = bin2str_ipaddress(peer_bgp_id)
- timestamp = float(timestamp_secs) + timestamp_microsecs * (10 ** -6)
-
- data = data[struct.calcsize(cls.PEER_UNPACK_STR):]
- msg.update({
- 'peer_distinguisher': str(RouteDistinguisher(peer_distinguisher)),
- 'peer_asn': peer_asn,
- 'peer_bgp_id': peer_bgp_id,
- 'timestamp': str(datetime.datetime.fromtimestamp(timestamp)),
- })
+ timestamp = float(timestamp_secs) + timestamp_microsecs * (10**-6)
+
+ data = data[struct.calcsize(cls.PEER_UNPACK_STR) :]
+ msg.update(
+ {
+ "peer_distinguisher": str(RouteDistinguisher(peer_distinguisher)),
+ "peer_asn": peer_asn,
+ "peer_bgp_id": peer_bgp_id,
+ "timestamp": str(datetime.datetime.fromtimestamp(timestamp)),
+ }
+ )
return data, msg
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING)
class BMPRouteMonitoring(BMPPerPeerMessage):
-
@classmethod
def dissect(cls, data):
data, peer_msg = super().dissect(data)
@@ -290,7 +298,7 @@ class BMPRouteMonitoring(BMPPerPeerMessage):
return {**peer_msg, **update_msg}
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPStatisticsReport:
"""
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
@@ -303,10 +311,11 @@ class BMPStatisticsReport:
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
+
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPPeerDownNotification:
"""
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
@@ -316,10 +325,11 @@ class BMPPeerDownNotification:
| Data (present if Reason = 1, 2 or 3) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
+
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION)
class BMPPeerUpNotification(BMPPerPeerMessage):
"""
@@ -336,7 +346,8 @@ class BMPPeerUpNotification(BMPPerPeerMessage):
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
- UNPACK_STR = '!16sHH'
+
+ UNPACK_STR = "!16sHH"
MIN_LEN = struct.calcsize(UNPACK_STR)
MSG_LEN = None
@@ -344,16 +355,14 @@ class BMPPeerUpNotification(BMPPerPeerMessage):
def dissect(cls, data):
data, peer_msg = super().dissect(data)
- (local_addr,
- local_port,
- remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
+ (local_addr, local_port, remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
msg = {
**peer_msg,
**{
- 'local_ip': bin2str_ipaddress(local_addr, peer_msg.get('ipv6')),
- 'local_port': int(local_port),
- 'remote_port': int(remote_port),
+ "local_ip": bin2str_ipaddress(local_addr, peer_msg.get("ipv6")),
+ "local_port": int(local_port),
+ "remote_port": int(remote_port),
},
}
@@ -362,7 +371,7 @@ class BMPPeerUpNotification(BMPPerPeerMessage):
return msg
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_INITIATION)
class BMPInitiation:
"""
@@ -374,30 +383,31 @@ class BMPInitiation:
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
- TLV_STR = '!HH'
+
+ TLV_STR = "!HH"
MIN_LEN = struct.calcsize(TLV_STR)
FIELD_TO_STR = {
- BMPCodes.BMP_INIT_INFO_STRING: 'information',
- BMPCodes.BMP_INIT_ADMIN_LABEL: 'admin_label',
- BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: 'system_description',
- BMPCodes.BMP_INIT_SYSTEM_NAME: 'system_name',
- BMPCodes.BMP_INIT_VRF_TABLE_NAME: 'vrf_table_name',
+ BMPCodes.BMP_INIT_INFO_STRING: "information",
+ BMPCodes.BMP_INIT_ADMIN_LABEL: "admin_label",
+ BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: "system_description",
+ BMPCodes.BMP_INIT_SYSTEM_NAME: "system_name",
+ BMPCodes.BMP_INIT_VRF_TABLE_NAME: "vrf_table_name",
}
@classmethod
def dissect(cls, data):
msg = {}
while len(data) > cls.MIN_LEN:
- _type, _len = struct.unpack_from(cls.TLV_STR, data[0:cls.MIN_LEN])
- _value = data[cls.MIN_LEN: cls.MIN_LEN + _len].decode()
+ _type, _len = struct.unpack_from(cls.TLV_STR, data[0 : cls.MIN_LEN])
+ _value = data[cls.MIN_LEN : cls.MIN_LEN + _len].decode()
msg[cls.FIELD_TO_STR[_type]] = _value
- data = data[cls.MIN_LEN + _len:]
+ data = data[cls.MIN_LEN + _len :]
return msg
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPTermination:
"""
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
@@ -408,14 +418,15 @@ class BMPTermination:
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
+
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPRouteMirroring:
pass
-#------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
class BMPRoutePolicy:
pass
diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver
index 25b4a52..5257df7 100755
--- a/tests/topotests/lib/bmp_collector/bmpserver
+++ b/tests/topotests/lib/bmp_collector/bmpserver
@@ -16,10 +16,12 @@ BGP_MAX_SIZE = 4096
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--address", type=str, default="0.0.0.0")
parser.add_argument("-p", "--port", type=int, default=1789)
+parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log")
def main():
args = parser.parse_args()
ADDRESS, PORT = args.address, args.port
+ LOG_FILE = args.logfile
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -31,7 +33,7 @@ def main():
while True:
data = connection.recv(BGP_MAX_SIZE)
while len(data) > BMPMsg.MIN_LEN:
- data = BMPMsg.dissect(data)
+ data = BMPMsg.dissect(data, log_file=LOG_FILE)
except Exception as e:
# XXX: do something
pass
diff --git a/tests/topotests/lib/checkping.py b/tests/topotests/lib/checkping.py
index aaa6164..5500807 100644
--- a/tests/topotests/lib/checkping.py
+++ b/tests/topotests/lib/checkping.py
@@ -8,7 +8,7 @@ from lib.topolog import logger
from lib import topotest
-def check_ping(name, dest_addr, expect_connected, count, wait):
+def check_ping(name, dest_addr, expect_connected, count, wait, source_addr=None):
"""
Assert that ping to dest_addr is expected
* 'name': the router to set the ping from
@@ -18,9 +18,13 @@ def check_ping(name, dest_addr, expect_connected, count, wait):
* 'wait': how long ping should wait to receive all replies
"""
- def _check(name, dest_addr, match):
+ def _check(name, dest_addr, source_addr, match):
tgen = get_topogen()
- output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr))
+ cmd = "ping {}".format(dest_addr)
+ if source_addr:
+ cmd += " -I {}".format(source_addr)
+ cmd += " -c 1 -w 1"
+ output = tgen.gears[name].run(cmd)
logger.info(output)
if match not in output:
return "ping fail"
@@ -28,6 +32,6 @@ def check_ping(name, dest_addr, expect_connected, count, wait):
match = ", {} packet loss".format("0%" if expect_connected else "100%")
logger.info("[+] check {} {} {}".format(name, dest_addr, match))
tgen = get_topogen()
- func = functools.partial(_check, name, dest_addr, match)
+ func = functools.partial(_check, name, dest_addr, source_addr, match)
success, result = topotest.run_and_expect(func, None, count=count, wait=wait)
assert result is None, "Failed"
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 598db84..7787b6f 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -936,14 +936,26 @@ def generate_support_bundle():
"""
tgen = get_topogen()
+ if tgen is None:
+ logger.warn(
+ "Support bundle attempted to be generated, but topogen is not being used"
+ )
+ return True
+
router_list = tgen.routers()
test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0]
bundle_procs = {}
for rname, rnode in router_list.items():
logger.info("Spawn collection of support bundle for %s", rname)
- dst_bundle = "{}/{}/support_bundles/{}".format(tgen.logdir, rname, test_name)
- rnode.run("mkdir -p " + dst_bundle)
+ try:
+ dst_bundle = "{}/{}/support_bundles/{}".format(
+ tgen.logdir, rname, test_name
+ )
+ rnode.run("mkdir -p " + dst_bundle)
+ except Exception as err:
+ logger.error("Generation of Support bundle failed {}".format(err))
+ return True
gen_sup_cmd = [
"/usr/lib/frr/generate_support_bundle.py",
diff --git a/tests/topotests/lib/fe_client.py b/tests/topotests/lib/fe_client.py
index 07059cc..a475446 100755
--- a/tests/topotests/lib/fe_client.py
+++ b/tests/topotests/lib/fe_client.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py
index 8c4701c..13b6361 100755
--- a/tests/topotests/lib/grpc-query.py
+++ b/tests/topotests/lib/grpc-query.py
@@ -10,35 +10,46 @@ import argparse
import logging
import os
import sys
+import tempfile
import pytest
CWD = os.path.dirname(os.path.realpath(__file__))
-# This is painful but works if you have installed grpc and grpc_tools would be *way*
-# better if we actually built and installed these but ... python packaging.
try:
- import grpc
- import grpc_tools
-
- sys.path.append(os.path.dirname(CWD))
- from munet.base import commander
-
- commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
- commander.cmd_raises(
- f"python3 -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
- )
-except Exception as error:
- logging.error("can't create proto definition modules %s", error)
- raise
-
-try:
- sys.path[0:0] = "."
- import frr_northbound_pb2
- import frr_northbound_pb2_grpc
-except Exception as error:
- logging.error("can't import proto definition modules %s", error)
- raise
+ # Make sure we don't run-into ourselves in parallel operating environment
+ tmpdir = tempfile.mkdtemp(prefix="grpc-client-")
+
+ # This is painful but works if you have installed grpc and grpc_tools would be *way*
+ # better if we actually built and installed these but ... python packaging.
+ try:
+ import grpc_tools
+ from munet.base import commander
+
+ import grpc
+
+ commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
+ commander.cmd_raises(
+ "python3 -m grpc_tools.protoc"
+ f" --python_out={tmpdir} --grpc_python_out={tmpdir}"
+ f" -I {CWD}/../../../grpc frr-northbound.proto"
+ )
+ except Exception as error:
+ logging.error("can't create proto definition modules %s", error)
+ raise
+
+ try:
+ sys.path[0:0] = [tmpdir]
+ print(sys.path)
+ import frr_northbound_pb2
+ import frr_northbound_pb2_grpc
+
+ sys.path = sys.path[1:]
+ except Exception as error:
+ logging.error("can't import proto definition modules %s", error)
+ raise
+finally:
+ commander.cmd_nostatus(f"rm -rf {tmpdir}")
class GRPCClient:
@@ -57,16 +68,16 @@ class GRPCClient:
logging.debug("GRPC Capabilities: %s", response)
return response
- def get(self, xpath):
+ def get(self, xpath, encoding, gtype):
request = frr_northbound_pb2.GetRequest()
request.path.append(xpath)
- request.type = frr_northbound_pb2.GetRequest.ALL
- request.encoding = frr_northbound_pb2.XML
- xml = ""
+ request.type = gtype
+ request.encoding = encoding
+ result = ""
for r in self.stub.Get(request):
- logging.info('GRPC Get path: "%s" value: %s', request.path, r)
- xml += str(r.data.data)
- return xml
+ logging.debug('GRPC Get path: "%s" value: %s', request.path, r)
+ result += str(r.data.data)
+ return result
def next_action(action_list=None):
@@ -95,6 +106,7 @@ def main(*args):
)
parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
parser.add_argument("--check", action="store_true", help="check runable")
+ parser.add_argument("--xml", action="store_true", help="encode XML instead of JSON")
parser.add_argument("actions", nargs="*", help="GETCAP|GET,xpath")
args = parser.parse_args(*args)
@@ -107,20 +119,32 @@ def main(*args):
if args.check:
sys.exit(0)
+ encoding = frr_northbound_pb2.XML if args.xml else frr_northbound_pb2.JSON
+
c = GRPCClient(args.server, args.port)
for action in next_action(args.actions):
action = action.casefold()
- logging.info("GOT ACTION: %s", action)
+ logging.debug("GOT ACTION: %s", action)
if action == "getcap":
caps = c.get_capabilities()
- print("Capabilities:", caps)
+ print(caps)
elif action.startswith("get,"):
- # Print Interface State and Config
+ # Get and print config and state
+ _, xpath = action.split(",", 1)
+ logging.debug("Get XPath: %s", xpath)
+ print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.ALL))
+ elif action.startswith("get-config,"):
+ # Get and print config
+ _, xpath = action.split(",", 1)
+ logging.debug("Get Config XPath: %s", xpath)
+ print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.CONFIG))
+ # for _ in range(0, 1):
+ elif action.startswith("get-state,"):
+ # Get and print state
_, xpath = action.split(",", 1)
- print("Get XPath: ", xpath)
- xml = c.get(xpath)
- print("{}: {}".format(xpath, xml))
+ logging.debug("Get State XPath: %s", xpath)
+ print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.STATE))
# for _ in range(0, 1):
diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py
index 814813f..8e2e76d 100644
--- a/tests/topotests/lib/snmptest.py
+++ b/tests/topotests/lib/snmptest.py
@@ -85,15 +85,18 @@ class SnmpTester(object):
return out_dict, out_list
def get(self, oid):
- cmd = "snmpget {0} {1}".format(self._snmp_config(), oid)
-
+ cmd = "snmpget {0} {1} 2>&1 | grep -v SNMPv2-PDU".format(
+ self._snmp_config(), oid
+ )
result = self.router.cmd(cmd)
if "not found" in result:
return None
return self._get_snmp_value(result)
def get_next(self, oid):
- cmd = "snmpgetnext {0} {1}".format(self._snmp_config(), oid)
+ cmd = "snmpgetnext {0} {1} 2>&1 | grep -v SNMPv2-PDU".format(
+ self._snmp_config(), oid
+ )
result = self.router.cmd(cmd)
print("get_next: {}".format(result))
@@ -102,7 +105,9 @@ class SnmpTester(object):
return self._get_snmp_value(result)
def walk(self, oid):
- cmd = "snmpwalk {0} {1}".format(self._snmp_config(), oid)
+ cmd = "snmpwalk {0} {1} 2>&1 | grep -v SNMPv2-PDU".format(
+ self._snmp_config(), oid
+ )
result = self.router.cmd(cmd)
return self._parse_multiline(result)
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index 155d2d0..f49e30e 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -94,7 +94,9 @@ def get_exabgp_cmd(commander=None):
return False
version = m.group(1)
if topotest.version_cmp(version, "4.2.11") < 0:
- logging.debug("found exabgp version < 4.2.11 in %s will keep looking", exacmd)
+ logging.debug(
+ "found exabgp version < 4.2.11 in %s will keep looking", exacmd
+ )
return False
logger.info("Using ExaBGP version %s in %s", version, exacmd)
return True
@@ -746,6 +748,7 @@ class TopoRouter(TopoGear):
RD_PIM6 = 19
RD_MGMTD = 20
RD_TRAP = 21
+ RD_FPM_LISTENER = 22
RD = {
RD_FRR: "frr",
RD_ZEBRA: "zebra",
@@ -769,6 +772,7 @@ class TopoRouter(TopoGear):
RD_SNMP: "snmpd",
RD_MGMTD: "mgmtd",
RD_TRAP: "snmptrapd",
+ RD_FPM_LISTENER: "fpm_listener",
}
def __init__(self, tgen, cls, name, **params):
@@ -845,7 +849,8 @@ class TopoRouter(TopoGear):
TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP,
TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR,
- TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD, TopoRouter.RD_TRAP.
+ TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD, TopoRouter.RD_TRAP,
+ TopoRouter.RD_FPM_LISTENER.
Possible `source` values are `None` for an empty config file, a path name which is
used directly, or a file name with no path components which is first looked for
@@ -883,7 +888,12 @@ class TopoRouter(TopoGear):
# Enable all daemon command logging, logging files
# and set them to the start dir.
for daemon, enabled in nrouter.daemons.items():
- if enabled and daemon != "snmpd" and daemon != "snmptrapd":
+ if (
+ enabled
+ and daemon != "snmpd"
+ and daemon != "snmptrapd"
+ and daemon != "fpm_listener"
+ ):
self.vtysh_cmd(
"\n".join(
[
@@ -933,7 +943,7 @@ class TopoRouter(TopoGear):
# and set them to the start dir.
for daemon in daemons:
enabled = nrouter.daemons[daemon]
- if enabled and daemon != "snmpd":
+ if enabled and daemon != "snmpd" and daemon != "fpm_listener":
self.vtysh_cmd(
"\n".join(
[
@@ -1253,9 +1263,12 @@ class TopoBMPCollector(TopoHost):
gear += " TopoBMPCollector<>".format()
return gear
- def start(self):
+ def start(self, log_file=None):
+ log_arg = "-l {}".format(log_file) if log_file else ""
self.run(
- "{}/bmp_collector/bmpserver -a {} -p {}&".format(CWD, self.ip, self.port),
+ "{}/bmp_collector/bmpserver -a {} -p {} {}&".format(
+ CWD, self.ip, self.port, log_arg
+ ),
stdout=None,
)
diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py
index 901e4f6..148fb04 100644
--- a/tests/topotests/lib/topojson.py
+++ b/tests/topotests/lib/topojson.py
@@ -227,13 +227,17 @@ def build_topo_from_json(tgen, topo=None):
topo["routers"][destRouter]["links"][curSwitch][
"interface"
] = "{}-{}-eth{}".format(
- destRouter, curSwitch, topo["routers"][destRouter]["nextIfname"]
+ destRouter,
+ curSwitch,
+ topo["routers"][destRouter]["nextIfname"],
)
topo["switches"][curSwitch]["links"][destRouter][
"interface"
] = "{}-{}-eth{}".format(
- curSwitch, destRouter, topo["routers"][destRouter]["nextIfname"]
+ curSwitch,
+ destRouter,
+ topo["routers"][destRouter]["nextIfname"],
)
topo["routers"][destRouter]["nextIfname"] += 1
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 2bb8923..087d845 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -27,6 +27,7 @@ import time
import logging
from collections.abc import Mapping
from copy import deepcopy
+from pathlib import Path
import lib.topolog as topolog
from lib.micronet_compat import Node
@@ -1295,6 +1296,8 @@ def fix_netns_limits(ns):
sysctl_assure(ns, "net.ipv4.conf.all.ignore_routes_with_linkdown", 1)
sysctl_assure(ns, "net.ipv6.conf.all.ignore_routes_with_linkdown", 1)
+ sysctl_assure(ns, "net.ipv4.conf.default.ignore_routes_with_linkdown", 1)
+ sysctl_assure(ns, "net.ipv6.conf.default.ignore_routes_with_linkdown", 1)
# igmp
sysctl_atleast(ns, "net.ipv4.igmp_max_memberships", 1000)
@@ -1426,6 +1429,7 @@ class Router(Node):
"snmpd": 0,
"mgmtd": 0,
"snmptrapd": 0,
+ "fpm_listener": 0,
}
self.daemons_options = {"zebra": ""}
self.reportCores = True
@@ -1522,7 +1526,7 @@ class Router(Node):
pass
return ret
- def stopRouter(self, assertOnError=True, minErrorVersion="5.1"):
+ def stopRouter(self, assertOnError=True):
# Stop Running FRR Daemons
running = self.listDaemons()
if not running:
@@ -1569,9 +1573,6 @@ class Router(Node):
)
errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
if assertOnError and (errors is not None) and len(errors) > 0:
assert "Errors found - details follow:" == 0, errors
return errors
@@ -1802,6 +1803,8 @@ class Router(Node):
"Starts FRR daemons for this router."
asan_abort = bool(g_pytest_config.option.asan_abort)
+ cov_option = bool(g_pytest_config.option.cov_topotest)
+ cov_dir = Path(g_pytest_config.option.rundir) / "gcda"
gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
@@ -1835,13 +1838,6 @@ class Router(Node):
# Re-enable to allow for report per run
self.reportCores = True
- # XXX: glue code forward ported from removed function.
- if self.version is None:
- self.version = self.cmd(
- os.path.join(self.daemondir, "bgpd") + " -v"
- ).split()[2]
- logger.info("{}: running version: {}".format(self.name, self.version))
-
perfds = {}
perf_options = g_pytest_config.get_option("--perf-options", "-g")
for perf in g_pytest_config.get_option("--perf", []):
@@ -1896,7 +1892,11 @@ class Router(Node):
)
rediropt = " > {0}.out 2> {0}.err".format(daemon)
- if daemon == "snmpd":
+ if daemon == "fpm_listener":
+ binary = "/usr/lib/frr/fpm_listener"
+ cmdenv = ""
+ cmdopt = "-d {}".format(daemon_opts)
+ elif daemon == "snmpd":
binary = "/usr/sbin/snmpd"
cmdenv = ""
cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format(
@@ -1923,6 +1923,10 @@ class Router(Node):
self.logdir, self.name, daemon
)
+ if cov_option:
+ scount = os.environ["GCOV_PREFIX_STRIP"]
+ cmdenv += f"GCOV_PREFIX_STRIP={scount} GCOV_PREFIX={cov_dir}"
+
if valgrind_memleaks:
this_dir = os.path.dirname(
os.path.abspath(os.path.realpath(__file__))
@@ -2162,7 +2166,11 @@ class Router(Node):
"%s: %s %s started with rr", self, self.routertype, daemon
)
else:
- if daemon != "snmpd" and daemon != "snmptrapd":
+ if (
+ daemon != "snmpd"
+ and daemon != "snmptrapd"
+ and daemon != "fpm_listener"
+ ):
cmdopt += " -d "
cmdopt += rediropt
@@ -2175,12 +2183,16 @@ class Router(Node):
daemon,
error.returncode,
error.cmd,
- '\n:stdout: "{}"'.format(error.stdout.strip())
- if error.stdout
- else "",
- '\n:stderr: "{}"'.format(error.stderr.strip())
- if error.stderr
- else "",
+ (
+ '\n:stdout: "{}"'.format(error.stdout.strip())
+ if error.stdout
+ else ""
+ ),
+ (
+ '\n:stderr: "{}"'.format(error.stderr.strip())
+ if error.stderr
+ else ""
+ ),
)
else:
logger.debug("%s: %s %s started", self, self.routertype, daemon)
@@ -2212,6 +2224,11 @@ class Router(Node):
while "snmpd" in daemons_list:
daemons_list.remove("snmpd")
+ if "fpm_listener" in daemons_list:
+ start_daemon("fpm_listener")
+ while "fpm_listener" in daemons_list:
+ daemons_list.remove("fpm_listener")
+
# Now start all the other daemons
for daemon in daemons_list:
if self.daemons[daemon] == 0:
@@ -2263,9 +2280,7 @@ class Router(Node):
rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False)
return rc == 0 or "No such process" not in e
- def killRouterDaemons(
- self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1"
- ):
+ def killRouterDaemons(self, daemons, wait=True, assertOnError=True):
# Kill Running FRR
# Daemons(user specified daemon only) using SIGKILL
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
@@ -2325,9 +2340,6 @@ class Router(Node):
self.cmd("rm -- {}".format(daemonpidfile))
if wait:
errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
if assertOnError and len(errors) > 0:
assert "Errors found - details follow:" == 0, errors
else:
@@ -2407,6 +2419,8 @@ class Router(Node):
continue
if daemon == "snmptrapd":
continue
+ if daemon == "fpm_listener":
+ continue
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
if daemon == "staticd":