diff options
-rwxr-xr-x | ospfclient/ospfclient.py | 193 | ||||
-rw-r--r-- | ospfd/ospf_apiserver.c | 7 | ||||
-rw-r--r-- | ospfd/ospf_opaque.c | 19 | ||||
-rw-r--r-- | ospfd/ospf_route.c | 61 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r1/ospfd.conf | 7 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r1/zebra.conf | 2 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r2/ospfd.conf | 2 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r3/ospfd.conf | 2 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r4/ospfd.conf | 7 | ||||
-rw-r--r-- | tests/topotests/ospfapi/r4/zebra.conf | 2 | ||||
-rw-r--r-- | tests/topotests/ospfapi/test_ospf_clientapi.py | 212 |
11 files changed, 302 insertions, 212 deletions
diff --git a/ospfclient/ospfclient.py b/ospfclient/ospfclient.py index 7a7bfb13c..7477ef819 100755 --- a/ospfclient/ospfclient.py +++ b/ospfclient/ospfclient.py @@ -242,6 +242,16 @@ def nsm_name(state): return names.get(state, str(state)) +class WithNothing: + "An object that does nothing when used with `with` statement." + + async def __aenter__(self): + return + + async def __aexit__(self, *args, **kwargs): + return + + # -------------- # Client Classes # -------------- @@ -547,15 +557,17 @@ class OspfOpaqueClient(OspfApiClient): Args: server: hostname or IP address of server default is "localhost" + wait_ready: if True then wait for OSPF to signal ready, in newer versions + FRR ospfd is always ready so this overhead can be skipped. + default is False. Raises: Will raise exceptions for failures with various `socket` modules functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`. """ - def __init__(self, server="localhost"): + def __init__(self, server="localhost", wait_ready=False): handlers = { - MSG_READY_NOTIFY: self._ready_msg, MSG_LSA_UPDATE_NOTIFY: self._lsa_change_msg, MSG_LSA_DELETE_NOTIFY: self._lsa_change_msg, MSG_NEW_IF: self._if_msg, @@ -565,9 +577,13 @@ class OspfOpaqueClient(OspfApiClient): MSG_REACHABLE_CHANGE: self._reachable_msg, MSG_ROUTER_ID_CHANGE: self._router_id_msg, } + if wait_ready: + handlers[MSG_READY_NOTIFY] = self._ready_msg + super().__init__(server, handlers) - self.ready_lock = Lock() + self.wait_ready = wait_ready + self.ready_lock = Lock() if wait_ready else WithNothing() self.ready_cond = { LSA_TYPE_OPAQUE_LINK: {}, LSA_TYPE_OPAQUE_AREA: {}, @@ -604,13 +620,9 @@ class OspfOpaqueClient(OspfApiClient): mp = struct.pack(msg_fmt[mt], lsa_type, otype) await self.msg_send_raises(mt, mp) - async def _assure_opaque_ready(self, lsa_type, otype): - async with self.ready_lock: - if self.ready_cond[lsa_type].get(otype) is True: - return - - await self._register_opaque_data(lsa_type, otype) - await self.wait_opaque_ready(lsa_type, otype) + # If we are not waiting, mark ready for register check + if not self.wait_ready: + self.ready_cond[lsa_type][otype] = True async def _handle_msg_loop(self): try: @@ -643,6 +655,8 @@ class OspfOpaqueClient(OspfApiClient): return lsa async def _ready_msg(self, mt, msg, extra, lsa_type, otype, addr): + assert self.wait_ready + if lsa_type == LSA_TYPE_OPAQUE_LINK: e = "ifaddr {}".format(ip(addr)) elif lsa_type == LSA_TYPE_OPAQUE_AREA: @@ -812,6 +826,7 @@ class OspfOpaqueClient(OspfApiClient): Raises: See `msg_send_raises` """ + assert self.ready_cond.get(lsa_type, {}).get(otype) is True, "Not Registered!" if lsa_type == LSA_TYPE_OPAQUE_LINK: ifaddr, aid = int(addr), 0 @@ -829,7 +844,6 @@ class OspfOpaqueClient(OspfApiClient): *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, data), ) msg += data - await self._assure_opaque_ready(lsa_type, otype) await self.msg_send_raises(mt, msg) async def delete_opaque_data(self, addr, lsa_type, otype, oid, flags=0): @@ -841,21 +855,31 @@ class OspfOpaqueClient(OspfApiClient): Args: addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. oid: (3 octets) ID of this opaque data flags: (octet) optional flags (e.g., OSPF_API_DEL_ZERO_LEN_LSA, defaults to no flags) Raises: See `msg_send_raises` """ - if (lsa_type, otype) in self.opaque_change_cb: - del self.opaque_change_cb[(lsa_type, otype)] + assert self.ready_cond.get(lsa_type, {}).get(otype) is True, "Not Registered!" mt = MSG_DELETE_REQUEST - await self._assure_opaque_ready(lsa_type, otype) mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, flags, oid) await self.msg_send_raises(mt, mp) + async def is_registered(self, lsa_type, otype): + """Determine if an (lsa_type, otype) tuple has been registered with FRR + + This determines if the type has been registered, but not necessarily if it is + ready, if that is required use the `wait_opaque_ready` metheod. + + Args: + lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} + otype: (octet) opaque type. + """ + async with self.ready_lock: + return self.ready_cond.get(lsa_type, {}).get(otype) is not None + async def register_opaque_data(self, lsa_type, otype, callback=None): """Register intent to advertise opaque data. @@ -865,8 +889,7 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. callback: if given, callback will be called when changes are received for LSA of the given (lsa_type, otype). The callbacks signature is: @@ -882,6 +905,10 @@ class OspfOpaqueClient(OspfApiClient): Raises: See `msg_send_raises` """ + assert not await self.is_registered( + lsa_type, otype + ), "Registering registered type" + if callback: self.opaque_change_cb[(lsa_type, otype)] = callback elif (lsa_type, otype) in self.opaque_change_cb: @@ -900,6 +927,8 @@ class OspfOpaqueClient(OspfApiClient): if cond is True: return + assert self.wait_ready + logging.debug( "waiting for ready %s opaque-type %s", lsa_typename(lsa_type), otype ) @@ -920,8 +949,7 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. callback: if given, callback will be called when changes are received for LSA of the given (lsa_type, otype). The callbacks signature is: @@ -938,17 +966,8 @@ class OspfOpaqueClient(OspfApiClient): See `msg_send_raises` """ - if callback: - self.opaque_change_cb[(lsa_type, otype)] = callback - elif (lsa_type, otype) in self.opaque_change_cb: - logging.warning( - "OSPFCLIENT: register: removing callback for %s opaque-type %s", - lsa_typename(lsa_type), - otype, - ) - del self.opaque_change_cb[(lsa_type, otype)] - - return await self._assure_opaque_ready(lsa_type, otype) + await self.register_opaque_data(lsa_type, otype, callback) + await self.wait_opaque_ready(lsa_type, otype) async def unregister_opaque_data(self, lsa_type, otype): """Unregister intent to advertise opaque data. @@ -958,11 +977,13 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. Raises: See `msg_send_raises` """ + assert await self.is_registered( + lsa_type, otype + ), "Unregistering unregistered type" if (lsa_type, otype) in self.opaque_change_cb: del self.opaque_change_cb[(lsa_type, otype)] @@ -1068,6 +1089,17 @@ class OspfOpaqueClient(OspfApiClient): # ================ # CLI/Script Usage # ================ +def next_action(action_list=None): + "Get next action from list or STDIN" + if action_list: + for action in action_list: + yield action + else: + while True: + action = input("") + if not action: + break + yield action.strip() async def async_main(args): @@ -1086,50 +1118,53 @@ async def async_main(args): await c.req_ism_states() await c.req_nsm_states() - if args.actions: - for action in args.actions: - _s = action.split(",") - what = _s.pop(False) - if what.casefold() == "wait": - stime = int(_s.pop(False)) - logging.info("waiting %s seconds", stime) - await asyncio.sleep(stime) - logging.info("wait complete: %s seconds", stime) - continue - ltype = int(_s.pop(False)) - if ltype == 11: - addr = ip(0) - else: - aval = _s.pop(False) - try: - addr = ip(int(aval)) - except ValueError: - addr = ip(aval) - oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))] - if what.casefold() == "add": + for action in next_action(args.actions): + _s = action.split(",") + what = _s.pop(False) + if what.casefold() == "wait": + stime = int(_s.pop(False)) + logging.info("waiting %s seconds", stime) + await asyncio.sleep(stime) + logging.info("wait complete: %s seconds", stime) + continue + ltype = int(_s.pop(False)) + if ltype == 11: + addr = ip(0) + else: + aval = _s.pop(False) + try: + addr = ip(int(aval)) + except ValueError: + addr = ip(aval) + oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))] + + if not await c.is_registered(oargs[1], oargs[2]): + await c.register_opaque_data_wait(oargs[1], oargs[2]) + + if what.casefold() == "add": + try: + b = bytes.fromhex(_s.pop(False)) + except IndexError: + b = b"" + logging.info("opaque data is %s octets", len(b)) + # Needs to be multiple of 4 in length + mod = len(b) % 4 + if mod: + b += b"\x00" * (4 - mod) + logging.info("opaque padding to %s octets", len(b)) + + await c.add_opaque_data(*oargs, b) + else: + assert what.casefold().startswith("del") + f = 0 + if len(_s) >= 1: try: - b = bytes.fromhex(_s.pop(False)) + f = int(_s.pop(False)) except IndexError: - b = b"" - logging.info("opaque data is %s octets", len(b)) - # Needs to be multiple of 4 in length - mod = len(b) % 4 - if mod: - b += b"\x00" * (4 - mod) - logging.info("opaque padding to %s octets", len(b)) - - await c.add_opaque_data(*oargs, b) - else: - assert what.casefold().startswith("del") - f = 0 - if len(_s) >= 1: - try: - f = int(_s.pop(False)) - except IndexError: - f = 0 - await c.delete_opaque_data(*oargs, f) - if args.exit: - return 0 + f = 0 + await c.delete_opaque_data(*oargs, f) + if not args.actions or args.exit: + return 0 except Exception as error: logging.error("async_main: unexpected error: %s", error, exc_info=True) return 2 @@ -1145,19 +1180,23 @@ async def async_main(args): def main(*args): ap = argparse.ArgumentParser(args) + ap.add_argument("--logtag", default="CLIENT", help="tag to identify log messages") ap.add_argument("--exit", action="store_true", help="Exit after commands") ap.add_argument("--server", default="localhost", help="OSPF API server") ap.add_argument("-v", "--verbose", action="store_true", help="be verbose") ap.add_argument( "actions", nargs="*", - help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA|DEL_FLAG]", + help="WAIT,SEC|(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA|DEL_FLAG]", ) args = ap.parse_args() level = logging.DEBUG if args.verbose else logging.INFO logging.basicConfig( - level=level, format="%(asctime)s %(levelname)s: CLIENT: %(name)s %(message)s" + level=level, + format="%(asctime)s %(levelname)s: {}: %(name)s %(message)s".format( + args.logtag + ), ) logging.info("ospfclient: starting") diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 728945032..5e4fc30a2 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -2578,9 +2578,12 @@ static inline int cmp_route_nodes(struct route_node *orn, return 1; else if (!nrn) return -1; - else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr) + + uint32_t opn = ntohl(orn->p.u.prefix4.s_addr); + uint32_t npn = ntohl(nrn->p.u.prefix4.s_addr); + if (opn < npn) return -1; - else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr) + else if (opn > npn) return 1; else return 0; diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 2af5d09ed..2e8e48bb5 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -19,6 +19,7 @@ #include "thread.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ +#include "printfrr.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -1147,11 +1148,13 @@ void ospf_opaque_config_write_debug(struct vty *vty) void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, json_object *json) { + char buf[128], *bp; struct lsa_header *lsah = lsa->data; uint32_t lsid = ntohl(lsah->id.s_addr); uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); uint32_t opaque_id = GET_OPAQUE_ID(lsid); struct ospf_opaque_functab *functab; + int len, lenValid; /* Switch output functionality by vty address. */ if (vty != NULL) { @@ -1170,11 +1173,19 @@ void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, json, "opaqueType", ospf_opaque_type_name(opaque_type)); json_object_int_add(json, "opaqueId", opaque_id); - json_object_int_add(json, "opaqueDataLength", - ntohs(lsah->length) - - OSPF_LSA_HEADER_SIZE); + len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; + json_object_int_add(json, "opaqueDataLength", len); + lenValid = VALID_OPAQUE_INFO_LEN(lsah); json_object_boolean_add(json, "opaqueDataLengthValid", - VALID_OPAQUE_INFO_LEN(lsah)); + lenValid); + if (lenValid) { + bp = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), + "%*pHXn", (int)len, + (lsah + 1)); + json_object_string_add(json, "opaqueData", buf); + if (bp != buf) + XFREE(MTYPE_TMP, bp); + } } } else { zlog_debug(" Opaque-Type %u (%s)", opaque_type, diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index bcedd36ce..25cd32b6f 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -348,44 +348,49 @@ void ospf_route_install(struct ospf *ospf, struct route_table *rt) /* RFC2328 16.1. (4). For "router". */ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, - struct ospf_area *area, bool add_all) + struct ospf_area *area, bool add_only) { struct route_node *rn; struct ospf_route * or ; struct prefix_ipv4 p; struct router_lsa *lsa; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Start", __func__); - + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Start", __func__); + else + zlog_debug("%s: REACHRUN: Start", __func__); + } lsa = (struct router_lsa *)v->lsa; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: LS ID: %pI4", __func__, &lsa->header.id); - if (!OSPF_IS_AREA_BACKBONE(area)) - ospf_vl_up_check(area, lsa->header.id, v); + if (!add_only) { + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_vl_up_check(area, lsa->header.id, v); - if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT)) - area->shortcut_capability = 0; + if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT)) + area->shortcut_capability = 0; - /* If the newly added vertex is an area border router or AS boundary - router, a routing table entry is added whose destination type is - "router". */ - if (!add_all && !IS_ROUTER_LSA_BORDER(lsa) && - !IS_ROUTER_LSA_EXTERNAL(lsa)) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "%s: this router is neither ASBR nor ABR, skipping it", - __func__); - return; - } + /* If the newly added vertex is an area border router or AS + boundary router, a routing table entry is added whose + destination type is "router". */ + if (!IS_ROUTER_LSA_BORDER(lsa) && + !IS_ROUTER_LSA_EXTERNAL(lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this router is neither ASBR nor ABR, skipping it", + __func__); + return; + } - /* Update ABR and ASBR count in this area. */ - if (IS_ROUTER_LSA_BORDER(lsa)) - area->abr_count++; - if (IS_ROUTER_LSA_EXTERNAL(lsa)) - area->asbr_count++; + /* Update ABR and ASBR count in this area. */ + if (IS_ROUTER_LSA_BORDER(lsa)) + area->abr_count++; + if (IS_ROUTER_LSA_EXTERNAL(lsa)) + area->asbr_count++; + } /* The Options field found in the associated router-LSA is copied into the routing table entry's Optional capabilities field. Call @@ -433,8 +438,12 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, listnode_add(rn->info, or); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Stop", __func__); + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Stop", __func__); + else + zlog_debug("%s: REACHRUN: Stop", __func__); + } } /* RFC2328 16.1. (4). For transit network. */ diff --git a/tests/topotests/ospfapi/r1/ospfd.conf b/tests/topotests/ospfapi/r1/ospfd.conf index 8d1355684..338691bbf 100644 --- a/tests/topotests/ospfapi/r1/ospfd.conf +++ b/tests/topotests/ospfapi/r1/ospfd.conf @@ -4,7 +4,12 @@ interface r1-eth0 ip ospf dead-interval 10 ip ospf area 1.2.3.4 ! +interface r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 1.2.3.4 +! router ospf - ospf router-id 192.168.0.1 + ospf router-id 1.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r1/zebra.conf b/tests/topotests/ospfapi/r1/zebra.conf index aae87408c..745159141 100644 --- a/tests/topotests/ospfapi/r1/zebra.conf +++ b/tests/topotests/ospfapi/r1/zebra.conf @@ -2,3 +2,5 @@ interface r1-eth0 ip address 10.0.1.1/24 ! +interface r1-eth1 + ip address 10.0.4.1/24 diff --git a/tests/topotests/ospfapi/r2/ospfd.conf b/tests/topotests/ospfapi/r2/ospfd.conf index be0712742..1bf2a0c7f 100644 --- a/tests/topotests/ospfapi/r2/ospfd.conf +++ b/tests/topotests/ospfapi/r2/ospfd.conf @@ -10,6 +10,6 @@ interface r2-eth1 ip ospf area 1.2.3.4 ! router ospf - ospf router-id 192.168.0.2 + ospf router-id 2.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r3/ospfd.conf b/tests/topotests/ospfapi/r3/ospfd.conf index 77cd86a97..ecf2042b4 100644 --- a/tests/topotests/ospfapi/r3/ospfd.conf +++ b/tests/topotests/ospfapi/r3/ospfd.conf @@ -10,6 +10,6 @@ interface r3-eth1 ip ospf area 1.2.3.4 ! router ospf - ospf router-id 192.168.0.3 + ospf router-id 3.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r4/ospfd.conf b/tests/topotests/ospfapi/r4/ospfd.conf index 32e55d546..4de8caeb1 100644 --- a/tests/topotests/ospfapi/r4/ospfd.conf +++ b/tests/topotests/ospfapi/r4/ospfd.conf @@ -4,7 +4,12 @@ interface r4-eth0 ip ospf dead-interval 10 ip ospf area 1.2.3.4 ! +interface r4-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 1.2.3.4 +! router ospf - ospf router-id 192.168.0.4 + ospf router-id 4.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r4/zebra.conf b/tests/topotests/ospfapi/r4/zebra.conf index 702219720..233ffa5c7 100644 --- a/tests/topotests/ospfapi/r4/zebra.conf +++ b/tests/topotests/ospfapi/r4/zebra.conf @@ -2,3 +2,5 @@ interface r4-eth0 ip address 10.0.3.4/24 ! +interface r4-eth1 + ip address 10.0.4.4/24
\ No newline at end of file diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index 7622a4ca5..b01c96226 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -21,7 +21,7 @@ from datetime import datetime, timedelta import pytest from lib.common_config import retry, run_frr_cmd, step -from lib.micronet import comm_error +from lib.micronet import Timeout, comm_error from lib.topogen import Topogen, TopoRouter from lib.topotest import interface_set_status, json_cmp @@ -43,15 +43,20 @@ assert os.path.exists( # Test Setup # ---------- +# +# r1 - r2 +# | | +# r4 - r3 +# + @pytest.fixture(scope="function", name="tgen") def _tgen(request): "Setup/Teardown the environment and provide tgen argument to tests" nrouters = request.param - if nrouters == 1: - topodef = {"sw1:": ("r1",)} - else: - topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)} + topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)} + if nrouters == 4: + topodef["sw4"] = ("r4", "r1") tgen = Topogen(topodef, request.module.__name__) tgen.start_topology() @@ -94,23 +99,23 @@ def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json" def myreadline(f): buf = b"" while True: - # logging.info("READING 1 CHAR") + # logging.debug("READING 1 CHAR") c = f.read(1) if not c: return buf if buf else None buf += c - # logging.info("READ CHAR: '%s'", c) + # logging.debug("READ CHAR: '%s'", c) if c == b"\n": return buf -def _wait_output(p, regex, timeout=120): - retry_until = datetime.now() + timedelta(seconds=timeout) - while datetime.now() < retry_until: +def _wait_output(p, regex, maxwait=120): + timeout = Timeout(maxwait) + while not timeout.is_expired(): # line = p.stdout.readline() line = myreadline(p.stdout) if not line: - assert None, "Timeout waiting for '{}'".format(regex) + assert None, "EOF waiting for '{}'".format(regex) line = line.decode("utf-8") line = line.rstrip() if line: @@ -118,7 +123,9 @@ def _wait_output(p, regex, timeout=120): m = re.search(regex, line) if m: return m - assert None, "Failed to get output withint {}s".format(timeout) + assert None, "Failed to get output matching '{}' withint {} actual {}s".format( + regex, maxwait, timeout.elapsed() + ) # ----- @@ -128,12 +135,13 @@ def _wait_output(p, regex, timeout=120): def _test_reachability(tgen, testbin): waitlist = [ - "192.168.0.1,192.168.0.2,192.168.0.4", - "192.168.0.2,192.168.0.4", - "192.168.0.1,192.168.0.2,192.168.0.4", + "1.0.0.0,2.0.0.0,4.0.0.0", + "2.0.0.0,4.0.0.0", + "1.0.0.0,2.0.0.0,4.0.0.0", ] r2 = tgen.gears["r2"] r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] wait_args = [f"--wait={x}" for x in waitlist] @@ -151,10 +159,12 @@ def _test_reachability(tgen, testbin): step("reachable: check for modified reachability") interface_set_status(r2, "r2-eth0", False) + interface_set_status(r4, "r4-eth1", False) _wait_output(p, "SUCCESS: {}".format(waitlist[1])) step("reachable: check for restored reachability") interface_set_status(r2, "r2-eth0", True) + interface_set_status(r4, "r4-eth1", True) _wait_output(p, "SUCCESS: {}".format(waitlist[2])) except Exception as error: logging.error("ERROR: %s", error) @@ -169,16 +179,16 @@ def _test_reachability(tgen, testbin): def test_ospf_reachability(tgen): testbin = os.path.join(TESTDIR, "ctester.py") rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) _test_reachability(tgen, testbin) def _test_router_id(tgen, testbin): r1 = tgen.gears["r1"] waitlist = [ - "192.168.0.1", + "1.0.0.0", "1.1.1.1", - "192.168.0.1", + "1.0.0.0", ] mon_args = [f"--monitor={x}" for x in waitlist] @@ -200,7 +210,7 @@ def _test_router_id(tgen, testbin): _wait_output(p, "SUCCESS: {}".format(waitlist[1])) step("router id: check for restored router id") - r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 192.168.0.1") + r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0") _wait_output(p, "SUCCESS: {}".format(waitlist[2])) except Exception as error: logging.error("ERROR: %s", error) @@ -215,7 +225,7 @@ def _test_router_id(tgen, testbin): def test_ospf_router_id(tgen): testbin = os.path.join(TESTDIR, "ctester.py") rc, o, e = tgen.gears["r1"].net.cmd_status([testbin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) _test_router_id(tgen, testbin) @@ -230,13 +240,13 @@ def _test_add_data(tgen, apibin): try: p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", } ], @@ -252,7 +262,7 @@ def _test_add_data(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "00000202", }, @@ -272,13 +282,13 @@ def _test_add_data(tgen, apibin): p = None p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, } @@ -286,7 +296,7 @@ def _test_add_data(tgen, apibin): "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", }, ], @@ -302,7 +312,7 @@ def _test_add_data(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "00010101", }, @@ -323,13 +333,13 @@ def _test_add_data(tgen, apibin): p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, }, @@ -339,7 +349,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", }, ], @@ -351,7 +361,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.3", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "deadbeaf01234567", }, @@ -369,11 +379,11 @@ def _test_add_data(tgen, apibin): p = None input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, }, @@ -387,11 +397,11 @@ def _test_add_data(tgen, apibin): # Originate it again p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000002", }, ], @@ -402,7 +412,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.3", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", "opaqueData": "ebadf00d", }, @@ -412,6 +422,7 @@ def _test_add_data(tgen, apibin): json_cmd = "show ip ospf da opaque-as json" assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + logging.debug("sending interrupt to writer api client") p.send_signal(signal.SIGINT) time.sleep(2) p.wait() @@ -426,6 +437,7 @@ def _test_add_data(tgen, apibin): raise finally: if p: + logging.debug("cleanup: sending interrupt to writer api client") p.terminate() p.wait() @@ -434,7 +446,7 @@ def _test_add_data(tgen, apibin): def test_ospf_opaque_add_data3(tgen): apibin = os.path.join(CLIENTDIR, "ospfclient.py") rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) _test_add_data(tgen, apibin) @@ -446,10 +458,12 @@ def _test_opaque_add_del(tgen, apibin): p = None pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+") try: step("reachable: check for add notification") pread = r2.popen( - ["/usr/bin/timeout", "120", apibin, "-v"], + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], encoding=None, # don't buffer stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, @@ -479,30 +493,30 @@ def _test_opaque_add_del(tgen, apibin): "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", }, { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", }, ], "linkLocalOpaqueLsaCount": 2, "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", }, { "lsId": "231.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d30", + "checksum": "7690", }, ], "areaLocalOpaqueLsaCount": 2, @@ -511,15 +525,15 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", }, { "lsId": "232.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", }, ], "asExternalOpaqueLsaCount": 2, @@ -543,17 +557,17 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -568,17 +582,17 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d30", + "checksum": "7690", "length": 28, "opaqueDataLength": 8, }, @@ -590,17 +604,17 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -642,32 +656,32 @@ def _test_opaque_add_del(tgen, apibin): "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", }, { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", }, ], "linkLocalOpaqueLsaCount": 2, "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", }, { "lsId": "231.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", }, ], "areaLocalOpaqueLsaCount": 2, @@ -676,16 +690,16 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", }, { "lsId": "232.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", }, ], "asExternalOpaqueLsaCount": 2, @@ -703,18 +717,18 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -729,18 +743,18 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "lsaAge": 3600, "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", # data removed "length": 20, "opaqueDataLength": 0, @@ -753,18 +767,18 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -795,19 +809,19 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -823,18 +837,18 @@ def _test_opaque_add_del(tgen, apibin): { "lsaAge": 3600, "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "lsaAge": 3600, "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", # data removed "length": 20, "opaqueDataLength": 0, @@ -847,19 +861,19 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -918,7 +932,7 @@ def _test_opaque_add_del(tgen, apibin): def test_ospf_opaque_delete_data3(tgen): apibin = os.path.join(CLIENTDIR, "ospfclient.py") rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) _test_opaque_add_del(tgen, apibin) |