]> git.proxmox.com Git - mirror_frr.git/blame - 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
ad9c18f3
CH
1#!/usr/bin/env python
2# -*- coding: utf-8 eval: (blacken-mode 1) -*-
acddc0ed 3# SPDX-License-Identifier: GPL-2.0-or-later
ad9c18f3 4#
9495c405 5# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
ad9c18f3 6#
ad9c18f3
CH
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
663a0c96 24from lib.micronet import Timeout, comm_error
ad9c18f3
CH
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
663a0c96
CH
46#
47# r1 - r2
48# | |
49# r4 - r3
50#
51
ad9c18f3
CH
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
663a0c96
CH
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")
ad9c18f3
CH
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:
663a0c96 102 # logging.debug("READING 1 CHAR")
ad9c18f3
CH
103 c = f.read(1)
104 if not c:
105 return buf if buf else None
106 buf += c
663a0c96 107 # logging.debug("READ CHAR: '%s'", c)
ad9c18f3
CH
108 if c == b"\n":
109 return buf
110
111
663a0c96
CH
112def _wait_output(p, regex, maxwait=120):
113 timeout = Timeout(maxwait)
114 while not timeout.is_expired():
ad9c18f3
CH
115 # line = p.stdout.readline()
116 line = myreadline(p.stdout)
117 if not line:
663a0c96 118 assert None, "EOF waiting for '{}'".format(regex)
ad9c18f3
CH
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
663a0c96
CH
126 assert None, "Failed to get output matching '{}' withint {} actual {}s".format(
127 regex, maxwait, timeout.elapsed()
128 )
ad9c18f3
CH
129
130
131# -----
132# Tests
133# -----
134
135
136def _test_reachability(tgen, testbin):
137 waitlist = [
663a0c96
CH
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",
ad9c18f3
CH
141 ]
142 r2 = tgen.gears["r2"]
143 r3 = tgen.gears["r3"]
663a0c96 144 r4 = tgen.gears["r4"]
ad9c18f3
CH
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)
663a0c96 162 interface_set_status(r4, "r4-eth1", False)
ad9c18f3
CH
163 _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
164
165 step("reachable: check for restored reachability")
166 interface_set_status(r2, "r2-eth0", True)
663a0c96 167 interface_set_status(r4, "r4-eth1", True)
ad9c18f3
CH
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"])
663a0c96 182 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
ad9c18f3
CH
183 _test_reachability(tgen, testbin)
184
185
9495c405
CH
186def _test_router_id(tgen, testbin):
187 r1 = tgen.gears["r1"]
188 waitlist = [
663a0c96 189 "1.0.0.0",
9495c405 190 "1.1.1.1",
663a0c96 191 "1.0.0.0",
9495c405
CH
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")
663a0c96 213 r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0")
9495c405
CH
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"])
663a0c96 228 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
9495c405
CH
229 _test_router_id(tgen, testbin)
230
231
ad9c18f3
CH
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 = {
663a0c96 243 "routerId": "1.0.0.0",
ad9c18f3
CH
244 "areas": {
245 "1.2.3.4": {
246 "linkLocalOpaqueLsa": [
247 {
248 "lsId": "230.0.0.2",
663a0c96 249 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
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",
663a0c96 265 "advertisingRouter": "1.0.0.0",
ad9c18f3
CH
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 = {
663a0c96 285 "routerId": "1.0.0.0",
ad9c18f3
CH
286 "areas": {
287 "1.2.3.4": {
288 "linkLocalOpaqueLsa": [
289 {
290 "lsId": "230.0.0.2",
663a0c96 291 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
292 "sequenceNumber": "80000001",
293 "lsaAge": 3600,
294 }
295 ],
296 "areaLocalOpaqueLsa": [
297 {
298 "lsId": "231.0.0.1",
663a0c96 299 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
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",
663a0c96 315 "advertisingRouter": "1.0.0.0",
ad9c18f3
CH
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 = {
663a0c96 336 "routerId": "1.0.0.0",
ad9c18f3
CH
337 "areas": {
338 "1.2.3.4": {
339 "areaLocalOpaqueLsa": [
340 {
341 "lsId": "231.0.0.1",
663a0c96 342 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
343 "sequenceNumber": "80000001",
344 "lsaAge": 3600,
345 },
346 ],
347 }
348 },
349 "asExternalOpaqueLsa": [
350 {
351 "lsId": "232.0.0.3",
663a0c96 352 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
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",
663a0c96 364 "advertisingRouter": "1.0.0.0",
ad9c18f3
CH
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 = {
663a0c96 382 "routerId": "1.0.0.0",
ad9c18f3
CH
383 "asExternalOpaqueLsa": [
384 {
385 "lsId": "232.0.0.3",
663a0c96 386 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
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 = {
663a0c96 400 "routerId": "1.0.0.0",
ad9c18f3
CH
401 "asExternalOpaqueLsa": [
402 {
403 "lsId": "232.0.0.3",
663a0c96 404 "advertisedRouter": "1.0.0.0",
ad9c18f3
CH
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",
663a0c96 415 "advertisingRouter": "1.0.0.0",
ad9c18f3
CH
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
663a0c96 425 logging.debug("sending interrupt to writer api client")
ad9c18f3
CH
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:
663a0c96 440 logging.debug("cleanup: sending interrupt to writer api client")
ad9c18f3
CH
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"])
663a0c96 449 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
ad9c18f3
CH
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
663a0c96
CH
461 # Log to our stdin, stderr
462 pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
ad9c18f3
CH
463 try:
464 step("reachable: check for add notification")
465 pread = r2.popen(
663a0c96 466 ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
ad9c18f3
CH
467 encoding=None, # don't buffer
468 stdin=subprocess.DEVNULL,
469 stdout=subprocess.PIPE,
470 stderr=subprocess.STDOUT,
471 )
0b242b11
LB
472 p = r1.popen(
473 [
474 apibin,
475 "-v",
c4fff21b 476 "add,9,10.0.1.1,230,1",
0b242b11 477 "add,9,10.0.1.1,230,2,00000202",
0b242b11 478 "wait,1",
c4fff21b
LB
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",
0b242b11 484 "wait,20",
c4fff21b
LB
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",
0b242b11
LB
488 ]
489 )
0b242b11
LB
490 add_input_dict = {
491 "areas": {
492 "1.2.3.4": {
493 "linkLocalOpaqueLsa": [
c4fff21b
LB
494 {
495 "lsId": "230.0.0.1",
663a0c96 496 "advertisedRouter": "1.0.0.0",
c4fff21b 497 "sequenceNumber": "80000001",
663a0c96 498 "checksum": "76bf",
c4fff21b 499 },
0b242b11
LB
500 {
501 "lsId": "230.0.0.2",
663a0c96 502 "advertisedRouter": "1.0.0.0",
0b242b11 503 "sequenceNumber": "80000001",
663a0c96 504 "checksum": "8aa2",
c4fff21b 505 },
0b242b11 506 ],
c4fff21b 507 "linkLocalOpaqueLsaCount": 2,
0b242b11
LB
508 "areaLocalOpaqueLsa": [
509 {
510 "lsId": "231.0.0.1",
663a0c96 511 "advertisedRouter": "1.0.0.0",
0b242b11 512 "sequenceNumber": "80000001",
663a0c96 513 "checksum": "5bd8",
0b242b11
LB
514 },
515 {
516 "lsId": "231.0.0.2",
663a0c96 517 "advertisedRouter": "1.0.0.0",
0b242b11 518 "sequenceNumber": "80000001",
663a0c96 519 "checksum": "7690",
0b242b11
LB
520 },
521 ],
c4fff21b
LB
522 "areaLocalOpaqueLsaCount": 2,
523 },
0b242b11
LB
524 },
525 "asExternalOpaqueLsa": [
526 {
c4fff21b 527 "lsId": "232.0.0.1",
663a0c96 528 "advertisedRouter": "1.0.0.0",
0b242b11 529 "sequenceNumber": "80000001",
663a0c96 530 "checksum": "5ed5",
c4fff21b
LB
531 },
532 {
533 "lsId": "232.0.0.2",
663a0c96 534 "advertisedRouter": "1.0.0.0",
c4fff21b 535 "sequenceNumber": "80000001",
663a0c96 536 "checksum": "d9bd",
c4fff21b 537 },
0b242b11 538 ],
c4fff21b 539 "asExternalOpaqueLsaCount": 2,
0b242b11
LB
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": [
c4fff21b
LB
558 {
559 "linkStateId": "230.0.0.1",
663a0c96 560 "advertisingRouter": "1.0.0.0",
c4fff21b 561 "lsaSeqNumber": "80000001",
663a0c96 562 "checksum": "76bf",
c4fff21b
LB
563 "length": 20,
564 "opaqueDataLength": 0,
565 },
0b242b11
LB
566 {
567 "linkStateId": "230.0.0.2",
663a0c96 568 "advertisingRouter": "1.0.0.0",
0b242b11 569 "lsaSeqNumber": "80000001",
663a0c96 570 "checksum": "8aa2",
0b242b11
LB
571 "length": 24,
572 "opaqueId": 2,
573 "opaqueDataLength": 4,
c4fff21b 574 },
0b242b11
LB
575 ]
576 }
577 }
578 },
579 {
580 "areaLocalOpaqueLsa": {
581 "areas": {
582 "1.2.3.4": [
583 {
584 "linkStateId": "231.0.0.1",
663a0c96 585 "advertisingRouter": "1.0.0.0",
0b242b11 586 "lsaSeqNumber": "80000001",
663a0c96 587 "checksum": "5bd8",
c4fff21b
LB
588 "length": 20,
589 "opaqueDataLength": 0,
0b242b11
LB
590 },
591 {
592 "linkStateId": "231.0.0.2",
663a0c96 593 "advertisingRouter": "1.0.0.0",
0b242b11 594 "lsaSeqNumber": "80000001",
663a0c96 595 "checksum": "7690",
c4fff21b
LB
596 "length": 28,
597 "opaqueDataLength": 8,
0b242b11 598 },
c4fff21b
LB
599 ],
600 },
601 },
0b242b11
LB
602 },
603 {
604 "asExternalOpaqueLsa": [
605 {
c4fff21b 606 "linkStateId": "232.0.0.1",
663a0c96 607 "advertisingRouter": "1.0.0.0",
c4fff21b 608 "lsaSeqNumber": "80000001",
663a0c96 609 "checksum": "5ed5",
c4fff21b
LB
610 "length": 20,
611 "opaqueDataLength": 0,
612 },
613 {
614 "linkStateId": "232.0.0.2",
663a0c96 615 "advertisingRouter": "1.0.0.0",
0b242b11 616 "lsaSeqNumber": "80000001",
663a0c96 617 "checksum": "d9bd",
0b242b11
LB
618 "length": 24,
619 "opaqueDataLength": 4,
c4fff21b
LB
620 },
621 ],
0b242b11
LB
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
ad9c18f3
CH
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
c4fff21b
LB
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 ]
0b242b11 648 for ls_id in ls_ids:
c4fff21b 649 step("reachable: check for API add notification: %s" % ls_id)
0b242b11
LB
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": [
c4fff21b
LB
657 {
658 "lsId": "230.0.0.1",
663a0c96 659 "advertisedRouter": "1.0.0.0",
c4fff21b 660 "sequenceNumber": "80000001",
663a0c96 661 "checksum": "76bf",
c4fff21b 662 },
0b242b11
LB
663 {
664 "lsId": "230.0.0.2",
663a0c96 665 "advertisedRouter": "1.0.0.0",
c4fff21b 666 "lsaAge": 3600,
0b242b11 667 "sequenceNumber": "80000001",
663a0c96 668 "checksum": "8aa2",
c4fff21b 669 },
0b242b11 670 ],
c4fff21b 671 "linkLocalOpaqueLsaCount": 2,
0b242b11
LB
672 "areaLocalOpaqueLsa": [
673 {
0b242b11 674 "lsId": "231.0.0.1",
663a0c96 675 "advertisedRouter": "1.0.0.0",
0b242b11 676 "sequenceNumber": "80000001",
663a0c96 677 "checksum": "5bd8",
0b242b11
LB
678 },
679 {
0b242b11 680 "lsId": "231.0.0.2",
663a0c96 681 "advertisedRouter": "1.0.0.0",
c4fff21b
LB
682 "lsaAge": 3600,
683 "sequenceNumber": "80000002",
663a0c96 684 "checksum": "4fe2",
0b242b11
LB
685 },
686 ],
c4fff21b
LB
687 "areaLocalOpaqueLsaCount": 2,
688 },
0b242b11
LB
689 },
690 "asExternalOpaqueLsa": [
691 {
c4fff21b 692 "lsId": "232.0.0.1",
663a0c96 693 "advertisedRouter": "1.0.0.0",
c4fff21b 694 "lsaAge": 3600,
0b242b11 695 "sequenceNumber": "80000001",
663a0c96 696 "checksum": "5ed5",
c4fff21b
LB
697 },
698 {
699 "lsId": "232.0.0.2",
663a0c96 700 "advertisedRouter": "1.0.0.0",
c4fff21b 701 "sequenceNumber": "80000001",
663a0c96 702 "checksum": "d9bd",
c4fff21b 703 },
0b242b11 704 ],
c4fff21b 705 "asExternalOpaqueLsaCount": 2,
0b242b11
LB
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
ad9c18f3 712
c4fff21b
LB
713 del_detail_input_dict = [
714 {
715 "linkLocalOpaqueLsa": {
716 "areas": {
717 "1.2.3.4": [
718 {
719 "linkStateId": "230.0.0.1",
663a0c96 720 "advertisingRouter": "1.0.0.0",
c4fff21b 721 "lsaSeqNumber": "80000001",
663a0c96 722 "checksum": "76bf",
c4fff21b
LB
723 "length": 20,
724 "opaqueDataLength": 0,
725 },
726 {
727 "linkStateId": "230.0.0.2",
663a0c96 728 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
729 "lsaAge": 3600,
730 "lsaSeqNumber": "80000001",
663a0c96 731 "checksum": "8aa2",
c4fff21b
LB
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",
663a0c96 746 "advertisingRouter": "1.0.0.0",
c4fff21b 747 "lsaSeqNumber": "80000001",
663a0c96 748 "checksum": "5bd8",
c4fff21b
LB
749 "length": 20,
750 "opaqueDataLength": 0,
751 },
752 {
753 "lsaAge": 3600,
754 "linkStateId": "231.0.0.2",
663a0c96 755 "advertisingRouter": "1.0.0.0",
c4fff21b 756 "lsaSeqNumber": "80000002",
663a0c96 757 "checksum": "4fe2",
c4fff21b
LB
758 # data removed
759 "length": 20,
760 "opaqueDataLength": 0,
761 },
762 ],
763 },
764 },
765 },
766 {
767 "asExternalOpaqueLsa": [
768 {
769 "linkStateId": "232.0.0.1",
663a0c96 770 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
771 "lsaAge": 3600,
772 "lsaSeqNumber": "80000001",
663a0c96 773 "checksum": "5ed5",
c4fff21b
LB
774 "length": 20,
775 "opaqueDataLength": 0,
776 },
777 {
778 "linkStateId": "232.0.0.2",
663a0c96 779 "advertisingRouter": "1.0.0.0",
c4fff21b 780 "lsaSeqNumber": "80000001",
663a0c96 781 "checksum": "d9bd",
c4fff21b
LB
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
ad9c18f3
CH
801 p.terminate()
802 if p.wait():
803 comm_error(p)
c4fff21b
LB
804
805 del_detail_input_dict = [
806 {
807 "linkLocalOpaqueLsa": {
808 "areas": {
809 "1.2.3.4": [
810 {
811 "linkStateId": "230.0.0.1",
663a0c96 812 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
813 "lsaAge": 3600,
814 "lsaSeqNumber": "80000001",
663a0c96 815 "checksum": "76bf",
c4fff21b
LB
816 "length": 20,
817 "opaqueDataLength": 0,
818 },
819 {
820 "linkStateId": "230.0.0.2",
663a0c96 821 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
822 "lsaAge": 3600,
823 "lsaSeqNumber": "80000001",
663a0c96 824 "checksum": "8aa2",
c4fff21b
LB
825 "length": 24,
826 "opaqueId": 2,
827 "opaqueDataLength": 4,
828 },
829 ]
830 }
0b242b11
LB
831 }
832 },
c4fff21b
LB
833 {
834 "areaLocalOpaqueLsa": {
835 "areas": {
836 "1.2.3.4": [
837 {
838 "lsaAge": 3600,
839 "linkStateId": "231.0.0.1",
663a0c96 840 "advertisingRouter": "1.0.0.0",
c4fff21b 841 "lsaSeqNumber": "80000001",
663a0c96 842 "checksum": "5bd8",
c4fff21b
LB
843 "length": 20,
844 "opaqueDataLength": 0,
845 },
846 {
847 "lsaAge": 3600,
848 "linkStateId": "231.0.0.2",
663a0c96 849 "advertisingRouter": "1.0.0.0",
c4fff21b 850 "lsaSeqNumber": "80000002",
663a0c96 851 "checksum": "4fe2",
c4fff21b
LB
852 # data removed
853 "length": 20,
854 "opaqueDataLength": 0,
855 },
856 ],
857 },
858 },
859 },
860 {
861 "asExternalOpaqueLsa": [
862 {
863 "linkStateId": "232.0.0.1",
663a0c96 864 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
865 "lsaAge": 3600,
866 "lsaSeqNumber": "80000001",
663a0c96 867 "checksum": "5ed5",
c4fff21b
LB
868 "length": 20,
869 "opaqueDataLength": 0,
870 },
871 {
872 "linkStateId": "232.0.0.2",
663a0c96 873 "advertisingRouter": "1.0.0.0",
c4fff21b
LB
874 "lsaAge": 3600,
875 "lsaSeqNumber": "80000001",
663a0c96 876 "checksum": "d9bd",
c4fff21b
LB
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
ad9c18f3
CH
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)
c4fff21b
LB
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 ]
0b242b11 911 for ls_id in ls_ids:
c4fff21b
LB
912 step("reachable: check for API delete notification: %s" % ls_id)
913 waitfor = "RECV:.*delete msg.*LSA {}.*age".format(ls_id)
0b242b11 914 _ = _wait_output(pread, waitfor)
ad9c18f3
CH
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"])
663a0c96 935 logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
ad9c18f3
CH
936 _test_opaque_add_del(tgen, apibin)
937
938
939if __name__ == "__main__":
940 args = ["-s"] + sys.argv[1:]
941 sys.exit(pytest.main(args))