]>
Commit | Line | Data |
---|---|---|
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 | """ | |
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 | |
663a0c96 | 24 | from lib.micronet import Timeout, comm_error |
ad9c18f3 CH |
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 | ||
663a0c96 CH |
46 | # |
47 | # r1 - r2 | |
48 | # | | | |
49 | # r4 - r3 | |
50 | # | |
51 | ||
ad9c18f3 CH |
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 | |
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) | |
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: | |
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 |
112 | def _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 | ||
136 | def _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) | |
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"]) | |
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 |
186 | def _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) | |
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"]) | |
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 |
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 = { | |
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) | |
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"]) | |
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 | ||
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 | |
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) | |
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"]) | |
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 | ||
939 | if __name__ == "__main__": | |
940 | args = ["-s"] + sys.argv[1:] | |
941 | sys.exit(pytest.main(args)) |