]> git.proxmox.com Git - mirror_frr.git/blame_incremental - tests/topotests/ospfapi/test_ospf_clientapi.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tests / topotests / ospfapi / test_ospf_clientapi.py
... / ...
CommitLineData
1#!/usr/bin/env python
2# -*- coding: utf-8 eval: (blacken-mode 1) -*-
3# SPDX-License-Identifier: GPL-2.0-or-later
4#
5# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
6#
7
8"""
9test_ospf_clientapi.py: Test the OSPF client API.
10"""
11
12import logging
13import os
14import re
15import signal
16import subprocess
17import sys
18import time
19from datetime import datetime, timedelta
20
21import pytest
22
23from lib.common_config import retry, run_frr_cmd, step
24from lib.micronet import Timeout, comm_error
25from lib.topogen import Topogen, TopoRouter
26from lib.topotest import interface_set_status, json_cmp
27
28pytestmark = [pytest.mark.ospfd]
29
30CWD = os.path.dirname(os.path.realpath(__file__))
31TESTDIR = os.path.abspath(CWD)
32
33CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
34if not os.path.exists(CLIENTDIR):
35 CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
36
37assert os.path.exists(
38 os.path.join(CLIENTDIR, "ospfclient.py")
39), "can't locate ospfclient.py"
40
41
42# ----------
43# Test Setup
44# ----------
45
46#
47# r1 - r2
48# | |
49# r4 - r3
50#
51
52
53@pytest.fixture(scope="function", name="tgen")
54def _tgen(request):
55 "Setup/Teardown the environment and provide tgen argument to tests"
56 nrouters = request.param
57 topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
58 if nrouters == 4:
59 topodef["sw4"] = ("r4", "r1")
60
61 tgen = Topogen(topodef, request.module.__name__)
62 tgen.start_topology()
63
64 router_list = tgen.routers()
65 for _, router in router_list.items():
66 router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
67 router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
68 router.net.daemons_options["ospfd"] = "--apiserver"
69
70 tgen.start_router()
71
72 yield tgen
73
74 tgen.stop_topology()
75
76
77# Fixture that executes before each test
78@pytest.fixture(autouse=True)
79def skip_on_failure(tgen):
80 if tgen.routers_have_failure():
81 pytest.skip("skipped because of previous test failure")
82
83
84# ------------
85# Test Utility
86# ------------
87
88
89@retry(retry_timeout=45)
90def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
91 del tgen
92 show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
93 if not bool(show_ospf_json):
94 return "ospf is not running"
95 result = json_cmp(show_ospf_json, input_dict)
96 return str(result) if result else None
97
98
99def myreadline(f):
100 buf = b""
101 while True:
102 # logging.debug("READING 1 CHAR")
103 c = f.read(1)
104 if not c:
105 return buf if buf else None
106 buf += c
107 # logging.debug("READ CHAR: '%s'", c)
108 if c == b"\n":
109 return buf
110
111
112def _wait_output(p, regex, maxwait=120):
113 timeout = Timeout(maxwait)
114 while not timeout.is_expired():
115 # line = p.stdout.readline()
116 line = myreadline(p.stdout)
117 if not line:
118 assert None, "EOF waiting for '{}'".format(regex)
119 line = line.decode("utf-8")
120 line = line.rstrip()
121 if line:
122 logging.debug("GOT LINE: '%s'", line)
123 m = re.search(regex, line)
124 if m:
125 return m
126 assert None, "Failed to get output matching '{}' withint {} actual {}s".format(
127 regex, maxwait, timeout.elapsed()
128 )
129
130
131# -----
132# Tests
133# -----
134
135
136def _test_reachability(tgen, testbin):
137 waitlist = [
138 "1.0.0.0,2.0.0.0,4.0.0.0",
139 "2.0.0.0,4.0.0.0",
140 "1.0.0.0,2.0.0.0,4.0.0.0",
141 ]
142 r2 = tgen.gears["r2"]
143 r3 = tgen.gears["r3"]
144 r4 = tgen.gears["r4"]
145
146 wait_args = [f"--wait={x}" for x in waitlist]
147
148 p = None
149 try:
150 step("reachable: check for initial reachability")
151 p = r3.popen(
152 ["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
153 encoding=None, # don't buffer
154 stdin=subprocess.DEVNULL,
155 stdout=subprocess.PIPE,
156 stderr=subprocess.STDOUT,
157 )
158 _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
159
160 step("reachable: check for modified reachability")
161 interface_set_status(r2, "r2-eth0", False)
162 interface_set_status(r4, "r4-eth1", False)
163 _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
164
165 step("reachable: check for restored reachability")
166 interface_set_status(r2, "r2-eth0", True)
167 interface_set_status(r4, "r4-eth1", True)
168 _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
169 except Exception as error:
170 logging.error("ERROR: %s", error)
171 raise
172 finally:
173 if p:
174 p.terminate()
175 p.wait()
176
177
178@pytest.mark.parametrize("tgen", [4], indirect=True)
179def test_ospf_reachability(tgen):
180 testbin = os.path.join(TESTDIR, "ctester.py")
181 rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
182 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
183 _test_reachability(tgen, testbin)
184
185
186def _test_router_id(tgen, testbin):
187 r1 = tgen.gears["r1"]
188 waitlist = [
189 "1.0.0.0",
190 "1.1.1.1",
191 "1.0.0.0",
192 ]
193
194 mon_args = [f"--monitor={x}" for x in waitlist]
195
196 p = None
197 try:
198 step("router id: check for initial router id")
199 p = r1.popen(
200 ["/usr/bin/timeout", "120", testbin, "-v", *mon_args],
201 encoding=None, # don't buffer
202 stdin=subprocess.DEVNULL,
203 stdout=subprocess.PIPE,
204 stderr=subprocess.STDOUT,
205 )
206 _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
207
208 step("router id: check for modified router id")
209 r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.1.1.1")
210 _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
211
212 step("router id: check for restored router id")
213 r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0")
214 _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
215 except Exception as error:
216 logging.error("ERROR: %s", error)
217 raise
218 finally:
219 if p:
220 p.terminate()
221 p.wait()
222
223
224@pytest.mark.parametrize("tgen", [2], indirect=True)
225def test_ospf_router_id(tgen):
226 testbin = os.path.join(TESTDIR, "ctester.py")
227 rc, o, e = tgen.gears["r1"].net.cmd_status([testbin, "--help"])
228 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
229 _test_router_id(tgen, testbin)
230
231
232def _test_add_data(tgen, apibin):
233 "Test adding opaque data to domain"
234
235 r1 = tgen.gears["r1"]
236
237 step("add opaque: add opaque link local")
238
239 p = None
240 try:
241 p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
242 input_dict = {
243 "routerId": "1.0.0.0",
244 "areas": {
245 "1.2.3.4": {
246 "linkLocalOpaqueLsa": [
247 {
248 "lsId": "230.0.0.2",
249 "advertisedRouter": "1.0.0.0",
250 "sequenceNumber": "80000001",
251 }
252 ],
253 }
254 },
255 }
256 # Wait for it to show up
257 assert verify_ospf_database(tgen, r1, input_dict) is None
258
259 input_dict = {
260 "linkLocalOpaqueLsa": {
261 "areas": {
262 "1.2.3.4": [
263 {
264 "linkStateId": "230.0.0.2",
265 "advertisingRouter": "1.0.0.0",
266 "lsaSeqNumber": "80000001",
267 "opaqueData": "00000202",
268 },
269 ],
270 }
271 },
272 }
273 # verify content
274 json_cmd = "show ip ospf da opaque-link json"
275 assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
276
277 step("reset client, add opaque area, verify link local flushing")
278
279 p.send_signal(signal.SIGINT)
280 time.sleep(2)
281 p.wait()
282 p = None
283 p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
284 input_dict = {
285 "routerId": "1.0.0.0",
286 "areas": {
287 "1.2.3.4": {
288 "linkLocalOpaqueLsa": [
289 {
290 "lsId": "230.0.0.2",
291 "advertisedRouter": "1.0.0.0",
292 "sequenceNumber": "80000001",
293 "lsaAge": 3600,
294 }
295 ],
296 "areaLocalOpaqueLsa": [
297 {
298 "lsId": "231.0.0.1",
299 "advertisedRouter": "1.0.0.0",
300 "sequenceNumber": "80000001",
301 },
302 ],
303 }
304 },
305 }
306 # Wait for it to show up
307 assert verify_ospf_database(tgen, r1, input_dict) is None
308
309 input_dict = {
310 "areaLocalOpaqueLsa": {
311 "areas": {
312 "1.2.3.4": [
313 {
314 "linkStateId": "231.0.0.1",
315 "advertisingRouter": "1.0.0.0",
316 "lsaSeqNumber": "80000001",
317 "opaqueData": "00010101",
318 },
319 ],
320 }
321 },
322 }
323 # verify content
324 json_cmd = "show ip ospf da opaque-area json"
325 assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
326
327 step("reset client, add opaque AS, verify area flushing")
328
329 p.send_signal(signal.SIGINT)
330 time.sleep(2)
331 p.wait()
332 p = None
333
334 p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
335 input_dict = {
336 "routerId": "1.0.0.0",
337 "areas": {
338 "1.2.3.4": {
339 "areaLocalOpaqueLsa": [
340 {
341 "lsId": "231.0.0.1",
342 "advertisedRouter": "1.0.0.0",
343 "sequenceNumber": "80000001",
344 "lsaAge": 3600,
345 },
346 ],
347 }
348 },
349 "asExternalOpaqueLsa": [
350 {
351 "lsId": "232.0.0.3",
352 "advertisedRouter": "1.0.0.0",
353 "sequenceNumber": "80000001",
354 },
355 ],
356 }
357 # Wait for it to show up
358 assert verify_ospf_database(tgen, r1, input_dict) is None
359
360 input_dict = {
361 "asExternalOpaqueLsa": [
362 {
363 "linkStateId": "232.0.0.3",
364 "advertisingRouter": "1.0.0.0",
365 "lsaSeqNumber": "80000001",
366 "opaqueData": "deadbeaf01234567",
367 },
368 ]
369 }
370 # verify content
371 json_cmd = "show ip ospf da opaque-as json"
372 assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
373
374 step("stop client, verify AS flushing")
375
376 p.send_signal(signal.SIGINT)
377 time.sleep(2)
378 p.wait()
379 p = None
380
381 input_dict = {
382 "routerId": "1.0.0.0",
383 "asExternalOpaqueLsa": [
384 {
385 "lsId": "232.0.0.3",
386 "advertisedRouter": "1.0.0.0",
387 "sequenceNumber": "80000001",
388 "lsaAge": 3600,
389 },
390 ],
391 }
392 # Wait for it to be flushed
393 assert verify_ospf_database(tgen, r1, input_dict) is None
394
395 step("start client adding opaque domain, verify new sequence number and data")
396
397 # Originate it again
398 p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
399 input_dict = {
400 "routerId": "1.0.0.0",
401 "asExternalOpaqueLsa": [
402 {
403 "lsId": "232.0.0.3",
404 "advertisedRouter": "1.0.0.0",
405 "sequenceNumber": "80000002",
406 },
407 ],
408 }
409 assert verify_ospf_database(tgen, r1, input_dict) is None
410
411 input_dict = {
412 "asExternalOpaqueLsa": [
413 {
414 "linkStateId": "232.0.0.3",
415 "advertisingRouter": "1.0.0.0",
416 "lsaSeqNumber": "80000002",
417 "opaqueData": "ebadf00d",
418 },
419 ]
420 }
421 # verify content
422 json_cmd = "show ip ospf da opaque-as json"
423 assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
424
425 logging.debug("sending interrupt to writer api client")
426 p.send_signal(signal.SIGINT)
427 time.sleep(2)
428 p.wait()
429 p = None
430
431 except Exception:
432 if p:
433 p.terminate()
434 if p.wait():
435 comm_error(p)
436 p = None
437 raise
438 finally:
439 if p:
440 logging.debug("cleanup: sending interrupt to writer api client")
441 p.terminate()
442 p.wait()
443
444
445@pytest.mark.parametrize("tgen", [2], indirect=True)
446def test_ospf_opaque_add_data3(tgen):
447 apibin = os.path.join(CLIENTDIR, "ospfclient.py")
448 rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
449 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
450 _test_add_data(tgen, apibin)
451
452
453def _test_opaque_add_del(tgen, apibin):
454 "Test adding opaque data to domain"
455
456 r1 = tgen.gears["r1"]
457 r2 = tgen.gears["r2"]
458
459 p = None
460 pread = None
461 # Log to our stdin, stderr
462 pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
463 try:
464 step("reachable: check for add notification")
465 pread = r2.popen(
466 ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
467 encoding=None, # don't buffer
468 stdin=subprocess.DEVNULL,
469 stdout=subprocess.PIPE,
470 stderr=subprocess.STDOUT,
471 )
472 p = r1.popen(
473 [
474 apibin,
475 "-v",
476 "add,9,10.0.1.1,230,1",
477 "add,9,10.0.1.1,230,2,00000202",
478 "wait,1",
479 "add,10,1.2.3.4,231,1",
480 "add,10,1.2.3.4,231,2,0102030405060708",
481 "wait,1",
482 "add,11,232,1",
483 "add,11,232,2,ebadf00d",
484 "wait,20",
485 "del,9,10.0.1.1,230,2,0",
486 "del,10,1.2.3.4,231,2,1",
487 "del,11,232,1,1",
488 ]
489 )
490 add_input_dict = {
491 "areas": {
492 "1.2.3.4": {
493 "linkLocalOpaqueLsa": [
494 {
495 "lsId": "230.0.0.1",
496 "advertisedRouter": "1.0.0.0",
497 "sequenceNumber": "80000001",
498 "checksum": "76bf",
499 },
500 {
501 "lsId": "230.0.0.2",
502 "advertisedRouter": "1.0.0.0",
503 "sequenceNumber": "80000001",
504 "checksum": "8aa2",
505 },
506 ],
507 "linkLocalOpaqueLsaCount": 2,
508 "areaLocalOpaqueLsa": [
509 {
510 "lsId": "231.0.0.1",
511 "advertisedRouter": "1.0.0.0",
512 "sequenceNumber": "80000001",
513 "checksum": "5bd8",
514 },
515 {
516 "lsId": "231.0.0.2",
517 "advertisedRouter": "1.0.0.0",
518 "sequenceNumber": "80000001",
519 "checksum": "7690",
520 },
521 ],
522 "areaLocalOpaqueLsaCount": 2,
523 },
524 },
525 "asExternalOpaqueLsa": [
526 {
527 "lsId": "232.0.0.1",
528 "advertisedRouter": "1.0.0.0",
529 "sequenceNumber": "80000001",
530 "checksum": "5ed5",
531 },
532 {
533 "lsId": "232.0.0.2",
534 "advertisedRouter": "1.0.0.0",
535 "sequenceNumber": "80000001",
536 "checksum": "d9bd",
537 },
538 ],
539 "asExternalOpaqueLsaCount": 2,
540 }
541
542 step("reachable: check for add LSAs")
543 json_cmd = "show ip ospf da json"
544 assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
545 assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
546
547 numcs = 3
548 json_cmds = [
549 "show ip ospf da opaque-link json",
550 "show ip ospf da opaque-area json",
551 "show ip ospf da opaque-as json",
552 ]
553 add_detail_input_dict = [
554 {
555 "linkLocalOpaqueLsa": {
556 "areas": {
557 "1.2.3.4": [
558 {
559 "linkStateId": "230.0.0.1",
560 "advertisingRouter": "1.0.0.0",
561 "lsaSeqNumber": "80000001",
562 "checksum": "76bf",
563 "length": 20,
564 "opaqueDataLength": 0,
565 },
566 {
567 "linkStateId": "230.0.0.2",
568 "advertisingRouter": "1.0.0.0",
569 "lsaSeqNumber": "80000001",
570 "checksum": "8aa2",
571 "length": 24,
572 "opaqueId": 2,
573 "opaqueDataLength": 4,
574 },
575 ]
576 }
577 }
578 },
579 {
580 "areaLocalOpaqueLsa": {
581 "areas": {
582 "1.2.3.4": [
583 {
584 "linkStateId": "231.0.0.1",
585 "advertisingRouter": "1.0.0.0",
586 "lsaSeqNumber": "80000001",
587 "checksum": "5bd8",
588 "length": 20,
589 "opaqueDataLength": 0,
590 },
591 {
592 "linkStateId": "231.0.0.2",
593 "advertisingRouter": "1.0.0.0",
594 "lsaSeqNumber": "80000001",
595 "checksum": "7690",
596 "length": 28,
597 "opaqueDataLength": 8,
598 },
599 ],
600 },
601 },
602 },
603 {
604 "asExternalOpaqueLsa": [
605 {
606 "linkStateId": "232.0.0.1",
607 "advertisingRouter": "1.0.0.0",
608 "lsaSeqNumber": "80000001",
609 "checksum": "5ed5",
610 "length": 20,
611 "opaqueDataLength": 0,
612 },
613 {
614 "linkStateId": "232.0.0.2",
615 "advertisingRouter": "1.0.0.0",
616 "lsaSeqNumber": "80000001",
617 "checksum": "d9bd",
618 "length": 24,
619 "opaqueDataLength": 4,
620 },
621 ],
622 },
623 ]
624 i = 0
625 while i < numcs:
626 step("reachable: check for add LSA details: %s" % json_cmds[i])
627 assert (
628 verify_ospf_database(tgen, r1, add_detail_input_dict[i], json_cmds[i])
629 is None
630 )
631 assert (
632 verify_ospf_database(tgen, r2, add_detail_input_dict[i], json_cmds[i])
633 is None
634 )
635 i += 1
636
637 # Wait for add notification
638 # RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
639
640 ls_ids = [
641 "230.0.0.1",
642 "230.0.0.2",
643 "231.0.0.1",
644 "231.0.0.2",
645 "232.0.0.1",
646 "232.0.0.2",
647 ]
648 for ls_id in ls_ids:
649 step("reachable: check for API add notification: %s" % ls_id)
650 waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
651 _ = _wait_output(pread, waitfor)
652
653 del_input_dict = {
654 "areas": {
655 "1.2.3.4": {
656 "linkLocalOpaqueLsa": [
657 {
658 "lsId": "230.0.0.1",
659 "advertisedRouter": "1.0.0.0",
660 "sequenceNumber": "80000001",
661 "checksum": "76bf",
662 },
663 {
664 "lsId": "230.0.0.2",
665 "advertisedRouter": "1.0.0.0",
666 "lsaAge": 3600,
667 "sequenceNumber": "80000001",
668 "checksum": "8aa2",
669 },
670 ],
671 "linkLocalOpaqueLsaCount": 2,
672 "areaLocalOpaqueLsa": [
673 {
674 "lsId": "231.0.0.1",
675 "advertisedRouter": "1.0.0.0",
676 "sequenceNumber": "80000001",
677 "checksum": "5bd8",
678 },
679 {
680 "lsId": "231.0.0.2",
681 "advertisedRouter": "1.0.0.0",
682 "lsaAge": 3600,
683 "sequenceNumber": "80000002",
684 "checksum": "4fe2",
685 },
686 ],
687 "areaLocalOpaqueLsaCount": 2,
688 },
689 },
690 "asExternalOpaqueLsa": [
691 {
692 "lsId": "232.0.0.1",
693 "advertisedRouter": "1.0.0.0",
694 "lsaAge": 3600,
695 "sequenceNumber": "80000001",
696 "checksum": "5ed5",
697 },
698 {
699 "lsId": "232.0.0.2",
700 "advertisedRouter": "1.0.0.0",
701 "sequenceNumber": "80000001",
702 "checksum": "d9bd",
703 },
704 ],
705 "asExternalOpaqueLsaCount": 2,
706 }
707
708 step("reachable: check for explicit withdrawal LSAs")
709 json_cmd = "show ip ospf da json"
710 assert verify_ospf_database(tgen, r1, del_input_dict, json_cmd) is None
711 assert verify_ospf_database(tgen, r2, del_input_dict, json_cmd) is None
712
713 del_detail_input_dict = [
714 {
715 "linkLocalOpaqueLsa": {
716 "areas": {
717 "1.2.3.4": [
718 {
719 "linkStateId": "230.0.0.1",
720 "advertisingRouter": "1.0.0.0",
721 "lsaSeqNumber": "80000001",
722 "checksum": "76bf",
723 "length": 20,
724 "opaqueDataLength": 0,
725 },
726 {
727 "linkStateId": "230.0.0.2",
728 "advertisingRouter": "1.0.0.0",
729 "lsaAge": 3600,
730 "lsaSeqNumber": "80000001",
731 "checksum": "8aa2",
732 "length": 24,
733 "opaqueId": 2,
734 "opaqueDataLength": 4,
735 },
736 ]
737 }
738 }
739 },
740 {
741 "areaLocalOpaqueLsa": {
742 "areas": {
743 "1.2.3.4": [
744 {
745 "linkStateId": "231.0.0.1",
746 "advertisingRouter": "1.0.0.0",
747 "lsaSeqNumber": "80000001",
748 "checksum": "5bd8",
749 "length": 20,
750 "opaqueDataLength": 0,
751 },
752 {
753 "lsaAge": 3600,
754 "linkStateId": "231.0.0.2",
755 "advertisingRouter": "1.0.0.0",
756 "lsaSeqNumber": "80000002",
757 "checksum": "4fe2",
758 # data removed
759 "length": 20,
760 "opaqueDataLength": 0,
761 },
762 ],
763 },
764 },
765 },
766 {
767 "asExternalOpaqueLsa": [
768 {
769 "linkStateId": "232.0.0.1",
770 "advertisingRouter": "1.0.0.0",
771 "lsaAge": 3600,
772 "lsaSeqNumber": "80000001",
773 "checksum": "5ed5",
774 "length": 20,
775 "opaqueDataLength": 0,
776 },
777 {
778 "linkStateId": "232.0.0.2",
779 "advertisingRouter": "1.0.0.0",
780 "lsaSeqNumber": "80000001",
781 "checksum": "d9bd",
782 "length": 24,
783 "opaqueDataLength": 4,
784 },
785 ],
786 },
787 ]
788 i = 0
789 while i < numcs:
790 step("reachable: check for delete LSA details: %s" % json_cmds[i])
791 assert (
792 verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
793 is None
794 )
795 assert (
796 verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
797 is None
798 )
799 i += 1
800
801 p.terminate()
802 if p.wait():
803 comm_error(p)
804
805 del_detail_input_dict = [
806 {
807 "linkLocalOpaqueLsa": {
808 "areas": {
809 "1.2.3.4": [
810 {
811 "linkStateId": "230.0.0.1",
812 "advertisingRouter": "1.0.0.0",
813 "lsaAge": 3600,
814 "lsaSeqNumber": "80000001",
815 "checksum": "76bf",
816 "length": 20,
817 "opaqueDataLength": 0,
818 },
819 {
820 "linkStateId": "230.0.0.2",
821 "advertisingRouter": "1.0.0.0",
822 "lsaAge": 3600,
823 "lsaSeqNumber": "80000001",
824 "checksum": "8aa2",
825 "length": 24,
826 "opaqueId": 2,
827 "opaqueDataLength": 4,
828 },
829 ]
830 }
831 }
832 },
833 {
834 "areaLocalOpaqueLsa": {
835 "areas": {
836 "1.2.3.4": [
837 {
838 "lsaAge": 3600,
839 "linkStateId": "231.0.0.1",
840 "advertisingRouter": "1.0.0.0",
841 "lsaSeqNumber": "80000001",
842 "checksum": "5bd8",
843 "length": 20,
844 "opaqueDataLength": 0,
845 },
846 {
847 "lsaAge": 3600,
848 "linkStateId": "231.0.0.2",
849 "advertisingRouter": "1.0.0.0",
850 "lsaSeqNumber": "80000002",
851 "checksum": "4fe2",
852 # data removed
853 "length": 20,
854 "opaqueDataLength": 0,
855 },
856 ],
857 },
858 },
859 },
860 {
861 "asExternalOpaqueLsa": [
862 {
863 "linkStateId": "232.0.0.1",
864 "advertisingRouter": "1.0.0.0",
865 "lsaAge": 3600,
866 "lsaSeqNumber": "80000001",
867 "checksum": "5ed5",
868 "length": 20,
869 "opaqueDataLength": 0,
870 },
871 {
872 "linkStateId": "232.0.0.2",
873 "advertisingRouter": "1.0.0.0",
874 "lsaAge": 3600,
875 "lsaSeqNumber": "80000001",
876 "checksum": "d9bd",
877 "length": 24,
878 "opaqueDataLength": 4,
879 },
880 ],
881 },
882 ]
883 i = 0
884 while i < numcs:
885 step(
886 "reachable: check for post API shutdown delete LSA details: %s"
887 % json_cmds[i]
888 )
889 assert (
890 verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
891 is None
892 )
893 assert (
894 verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
895 is None
896 )
897 i += 1
898
899 # step("reachable: check for flush/age out")
900 # # Wait for max age notification
901 # waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
902 # _wait_output(pread, waitfor)
903 ls_ids = [
904 "230.0.0.2",
905 "231.0.0.2",
906 "232.0.0.1",
907 "230.0.0.1",
908 "231.0.0.1",
909 "232.0.0.2",
910 ]
911 for ls_id in ls_ids:
912 step("reachable: check for API delete notification: %s" % ls_id)
913 waitfor = "RECV:.*delete msg.*LSA {}.*age".format(ls_id)
914 _ = _wait_output(pread, waitfor)
915 except Exception:
916 if p:
917 p.terminate()
918 if p.wait():
919 comm_error(p)
920 p = None
921 raise
922 finally:
923 if pread:
924 pread.terminate()
925 pread.wait()
926 if p:
927 p.terminate()
928 p.wait()
929
930
931@pytest.mark.parametrize("tgen", [2], indirect=True)
932def test_ospf_opaque_delete_data3(tgen):
933 apibin = os.path.join(CLIENTDIR, "ospfclient.py")
934 rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
935 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
936 _test_opaque_add_del(tgen, apibin)
937
938
939if __name__ == "__main__":
940 args = ["-s"] + sys.argv[1:]
941 sys.exit(pytest.main(args))