]>
Commit | Line | Data |
---|---|---|
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 | """ | |
9 | test_ospf_clientapi.py: Test the OSPF client API. | |
10 | """ | |
11 | ||
12 | import logging | |
13 | import os | |
14 | import re | |
15 | import signal | |
16 | import subprocess | |
17 | import sys | |
18 | import time | |
19 | from datetime import datetime, timedelta | |
20 | ||
21 | import pytest | |
22 | ||
23 | from lib.common_config import retry, run_frr_cmd, step | |
24 | from lib.micronet import Timeout, comm_error | |
25 | from lib.topogen import Topogen, TopoRouter | |
26 | from lib.topotest import interface_set_status, json_cmp | |
27 | ||
28 | pytestmark = [pytest.mark.ospfd] | |
29 | ||
30 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
31 | TESTDIR = os.path.abspath(CWD) | |
32 | ||
33 | CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient")) | |
34 | if not os.path.exists(CLIENTDIR): | |
35 | CLIENTDIR = os.path.join(CWD, "/usr/lib/frr") | |
36 | ||
37 | assert 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") | |
54 | def _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) | |
79 | def 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) | |
90 | def 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 | ||
99 | def 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 | ||
112 | def _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 | ||
136 | def _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) | |
179 | def 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 | ||
186 | def _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) | |
225 | def 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 | ||
232 | def _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) | |
446 | def 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 | ||
453 | def _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) | |
932 | def 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 | ||
939 | if __name__ == "__main__": | |
940 | args = ["-s"] + sys.argv[1:] | |
941 | sys.exit(pytest.main(args)) |