diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-05 09:56:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-05 09:56:24 +0000 |
commit | 54de8bbe36d7d968c6367939277942518bd4e11f (patch) | |
tree | 924f8c16d9c8831679f355b88c620b9b356c9d5a /tests/topotests/lib | |
parent | Adding debian version 10.0.1-0.1. (diff) | |
download | frr-54de8bbe36d7d968c6367939277942518bd4e11f.tar.xz frr-54de8bbe36d7d968c6367939277942518bd4e11f.zip |
Merging upstream version 10.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/topotests/lib')
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/open/__init__.py | 32 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/update/__init__.py | 30 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/update/af.py | 6 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/update/nlri.py | 62 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py | 119 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bgp/update/rd.py | 24 | ||||
-rw-r--r-- | tests/topotests/lib/bmp_collector/bmp.py | 173 | ||||
-rwxr-xr-x | tests/topotests/lib/bmp_collector/bmpserver | 4 | ||||
-rw-r--r-- | tests/topotests/lib/checkping.py | 12 | ||||
-rw-r--r-- | tests/topotests/lib/common_config.py | 16 | ||||
-rwxr-xr-x | tests/topotests/lib/fe_client.py | 2 | ||||
-rwxr-xr-x | tests/topotests/lib/grpc-query.py | 96 | ||||
-rw-r--r-- | tests/topotests/lib/snmptest.py | 13 | ||||
-rw-r--r-- | tests/topotests/lib/topogen.py | 25 | ||||
-rw-r--r-- | tests/topotests/lib/topojson.py | 8 | ||||
-rw-r--r-- | tests/topotests/lib/topotest.py | 64 |
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": |