summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xospfclient/ospfclient.py193
-rw-r--r--ospfd/ospf_apiserver.c7
-rw-r--r--ospfd/ospf_opaque.c19
-rw-r--r--ospfd/ospf_route.c61
-rw-r--r--tests/topotests/ospfapi/r1/ospfd.conf7
-rw-r--r--tests/topotests/ospfapi/r1/zebra.conf2
-rw-r--r--tests/topotests/ospfapi/r2/ospfd.conf2
-rw-r--r--tests/topotests/ospfapi/r3/ospfd.conf2
-rw-r--r--tests/topotests/ospfapi/r4/ospfd.conf7
-rw-r--r--tests/topotests/ospfapi/r4/zebra.conf2
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py212
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)