]>
Commit | Line | Data |
---|---|---|
e8cd26fd | 1 | # Copyright (c) 2019 by VMware, Inc. ("VMware") |
2 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. | |
3 | # ("NetDEF") in this file. | |
4 | # | |
5 | # Permission to use, copy, modify, and/or distribute this software | |
6 | # for any purpose with or without fee is hereby granted, provided | |
7 | # that the above copyright notice and this permission notice appear | |
8 | # in all copies. | |
9 | # | |
10 | # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES | |
11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR | |
13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
14 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
15 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
16 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
17 | # OF THIS SOFTWARE. | |
18 | ||
49581587 | 19 | import datetime |
e8cd26fd | 20 | import os |
21 | import re | |
49581587 | 22 | import sys |
e8cd26fd | 23 | import traceback |
3d7d6e9a | 24 | import functools |
e8cd26fd | 25 | from copy import deepcopy |
49581587 | 26 | from time import sleep |
3d7d6e9a | 27 | from lib import topotest |
49581587 | 28 | |
e8cd26fd | 29 | |
30 | # Import common_config to use commomnly used APIs | |
31 | from lib.common_config import ( | |
4f99894d | 32 | create_common_configurations, |
a5124c49 CH |
33 | HostApplicationHelper, |
34 | InvalidCLIError, | |
49581587 | 35 | create_common_configuration, |
e8cd26fd | 36 | InvalidCLIError, |
37 | retry, | |
38 | run_frr_cmd, | |
39 | ) | |
4953ca97 | 40 | from lib.micronet import get_exec_path |
49581587 | 41 | from lib.topolog import logger |
a5124c49 | 42 | from lib.topotest import frr_unicode |
e8cd26fd | 43 | |
44 | #### | |
45 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
46 | ||
47 | ||
48 | def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True): | |
49 | """ | |
50 | API to configure pim on router | |
51 | ||
52 | Parameters | |
53 | ---------- | |
54 | * `tgen` : Topogen object | |
55 | * `topo` : json file data | |
56 | * `input_dict` : Input dict data, required when configuring from | |
57 | testcase | |
58 | * `build` : Only for initial setup phase this is set as True. | |
59 | ||
60 | Usage | |
61 | ----- | |
62 | input_dict = { | |
63 | "r1": { | |
64 | "pim": { | |
0a76e764 | 65 | "join-prune-interval": "5", |
e8cd26fd | 66 | "rp": [{ |
67 | "rp_addr" : "1.0.3.17". | |
68 | "keep-alive-timer": "100" | |
69 | "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"] | |
70 | "prefix-list": "pf_list_1" | |
71 | "delete": True | |
72 | }] | |
73 | } | |
74 | } | |
75 | } | |
76 | ||
77 | ||
78 | Returns | |
79 | ------- | |
80 | True or False | |
81 | """ | |
82 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
83 | result = False | |
84 | if not input_dict: | |
85 | input_dict = deepcopy(topo) | |
86 | else: | |
87 | topo = topo["routers"] | |
88 | input_dict = deepcopy(input_dict) | |
4f99894d CH |
89 | |
90 | config_data_dict = {} | |
91 | ||
e8cd26fd | 92 | for router in input_dict.keys(): |
4f99894d | 93 | config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build) |
e8cd26fd | 94 | |
4f99894d CH |
95 | if config_data: |
96 | config_data_dict[router] = config_data | |
97 | ||
98 | # Now add RP config to all routers | |
99 | for router in input_dict.keys(): | |
e8cd26fd | 100 | if "pim" not in input_dict[router]: |
e8cd26fd | 101 | continue |
4f99894d CH |
102 | if "rp" not in input_dict[router]["pim"]: |
103 | continue | |
a53c08bc | 104 | _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict) |
e8cd26fd | 105 | |
4f99894d CH |
106 | try: |
107 | result = create_common_configurations( | |
108 | tgen, config_data_dict, "pim", build, load_config | |
109 | ) | |
110 | except InvalidCLIError: | |
111 | logger.error("create_pim_config", exc_info=True) | |
112 | result = False | |
e8cd26fd | 113 | |
114 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
115 | return result | |
116 | ||
117 | ||
4f99894d | 118 | def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict): |
e8cd26fd | 119 | """ |
0a76e764 | 120 | Helper API to create pim RP configurations. |
e8cd26fd | 121 | |
122 | Parameters | |
123 | ---------- | |
124 | * `tgen` : Topogen object | |
125 | * `topo` : json file data | |
126 | * `input_dict` : Input dict data, required when configuring from testcase | |
127 | * `router` : router id to be configured. | |
128 | * `build` : Only for initial setup phase this is set as True. | |
4f99894d | 129 | * `config_data_dict` : OUT: adds `router` config to dictinary |
e8cd26fd | 130 | Returns |
131 | ------- | |
4f99894d | 132 | None |
e8cd26fd | 133 | """ |
134 | ||
e8cd26fd | 135 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) |
e8cd26fd | 136 | |
0a76e764 CH |
137 | pim_data = input_dict[router]["pim"] |
138 | rp_data = pim_data["rp"] | |
e8cd26fd | 139 | |
0a76e764 CH |
140 | # Configure this RP on every router. |
141 | for dut in tgen.routers(): | |
0a76e764 CH |
142 | # At least one interface must be enabled for PIM on the router |
143 | pim_if_enabled = False | |
144 | for destLink, data in topo[dut]["links"].items(): | |
145 | if "pim" in data: | |
146 | pim_if_enabled = True | |
147 | if not pim_if_enabled: | |
148 | continue | |
e8cd26fd | 149 | |
0a76e764 | 150 | config_data = [] |
e8cd26fd | 151 | |
0a76e764 CH |
152 | for rp_dict in deepcopy(rp_data): |
153 | # ip address of RP | |
154 | if "rp_addr" not in rp_dict and build: | |
155 | logger.error( | |
a53c08bc | 156 | "Router %s: 'ip address of RP' not " "present in input_dict/JSON", |
0a76e764 CH |
157 | router, |
158 | ) | |
e8cd26fd | 159 | |
0a76e764 CH |
160 | return False |
161 | rp_addr = rp_dict.setdefault("rp_addr", None) | |
e8cd26fd | 162 | |
0a76e764 CH |
163 | # Keep alive Timer |
164 | keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None) | |
e8cd26fd | 165 | |
0a76e764 CH |
166 | # Group Address range to cover |
167 | if "group_addr_range" not in rp_dict and build: | |
168 | logger.error( | |
169 | "Router %s:'Group Address range to cover'" | |
170 | " not present in input_dict/JSON", | |
171 | router, | |
172 | ) | |
e8cd26fd | 173 | |
0a76e764 CH |
174 | return False |
175 | group_addr_range = rp_dict.setdefault("group_addr_range", None) | |
e8cd26fd | 176 | |
0a76e764 CH |
177 | # Group prefix-list filter |
178 | prefix_list = rp_dict.setdefault("prefix_list", None) | |
e8cd26fd | 179 | |
0a76e764 CH |
180 | # Delete rp config |
181 | del_action = rp_dict.setdefault("delete", False) | |
e8cd26fd | 182 | |
0a76e764 CH |
183 | if keep_alive_timer: |
184 | cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer) | |
185 | if del_action: | |
186 | cmd = "no {}".format(cmd) | |
187 | config_data.append(cmd) | |
e8cd26fd | 188 | |
0a76e764 CH |
189 | if rp_addr: |
190 | if group_addr_range: | |
191 | if type(group_addr_range) is not list: | |
192 | group_addr_range = [group_addr_range] | |
193 | ||
194 | for grp_addr in group_addr_range: | |
195 | cmd = "ip pim rp {} {}".format(rp_addr, grp_addr) | |
e8cd26fd | 196 | if del_action: |
197 | cmd = "no {}".format(cmd) | |
0a76e764 | 198 | config_data.append(cmd) |
e8cd26fd | 199 | |
0a76e764 | 200 | if prefix_list: |
a53c08bc | 201 | cmd = "ip pim rp {} prefix-list {}".format(rp_addr, prefix_list) |
0a76e764 CH |
202 | if del_action: |
203 | cmd = "no {}".format(cmd) | |
204 | config_data.append(cmd) | |
e8cd26fd | 205 | |
4f99894d CH |
206 | if config_data: |
207 | if dut not in config_data_dict: | |
208 | config_data_dict[dut] = config_data | |
209 | else: | |
210 | config_data_dict[dut].extend(config_data) | |
e8cd26fd | 211 | |
212 | ||
213 | def create_igmp_config(tgen, topo, input_dict=None, build=False): | |
214 | """ | |
215 | API to configure igmp on router | |
216 | ||
217 | Parameters | |
218 | ---------- | |
219 | * `tgen` : Topogen object | |
220 | * `topo` : json file data | |
221 | * `input_dict` : Input dict data, required when configuring from | |
222 | testcase | |
223 | * `build` : Only for initial setup phase this is set as True. | |
224 | ||
225 | Usage | |
226 | ----- | |
227 | input_dict = { | |
228 | "r1": { | |
229 | "igmp": { | |
230 | "interfaces": { | |
231 | "r1-r0-eth0" :{ | |
232 | "igmp":{ | |
233 | "version": "2", | |
234 | "delete": True | |
235 | "query": { | |
236 | "query-interval" : 100, | |
237 | "query-max-response-time": 200 | |
238 | } | |
239 | } | |
240 | } | |
241 | } | |
242 | } | |
243 | } | |
244 | } | |
245 | ||
246 | Returns | |
247 | ------- | |
248 | True or False | |
249 | """ | |
250 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
251 | result = False | |
252 | if not input_dict: | |
253 | input_dict = deepcopy(topo) | |
254 | else: | |
255 | topo = topo["routers"] | |
256 | input_dict = deepcopy(input_dict) | |
4f99894d CH |
257 | |
258 | config_data_dict = {} | |
259 | ||
e8cd26fd | 260 | for router in input_dict.keys(): |
261 | if "igmp" not in input_dict[router]: | |
262 | logger.debug("Router %s: 'igmp' is not present in " "input_dict", router) | |
263 | continue | |
264 | ||
265 | igmp_data = input_dict[router]["igmp"] | |
266 | ||
267 | if "interfaces" in igmp_data: | |
268 | config_data = [] | |
269 | intf_data = igmp_data["interfaces"] | |
270 | ||
271 | for intf_name in intf_data.keys(): | |
272 | cmd = "interface {}".format(intf_name) | |
273 | config_data.append(cmd) | |
274 | protocol = "igmp" | |
275 | del_action = intf_data[intf_name]["igmp"].setdefault("delete", False) | |
276 | cmd = "ip igmp" | |
277 | if del_action: | |
278 | cmd = "no {}".format(cmd) | |
279 | config_data.append(cmd) | |
280 | ||
281 | del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False) | |
282 | for attribute, data in intf_data[intf_name]["igmp"].items(): | |
283 | if attribute == "version": | |
284 | cmd = "ip {} {} {}".format(protocol, attribute, data) | |
285 | if del_action: | |
286 | cmd = "no {}".format(cmd) | |
287 | config_data.append(cmd) | |
288 | ||
289 | if attribute == "join": | |
290 | for group in data: | |
291 | cmd = "ip {} {} {}".format(protocol, attribute, group) | |
292 | if del_attr: | |
293 | cmd = "no {}".format(cmd) | |
294 | config_data.append(cmd) | |
295 | ||
296 | if attribute == "query": | |
297 | for query, value in data.items(): | |
298 | if query != "delete": | |
299 | cmd = "ip {} {} {}".format(protocol, query, value) | |
300 | ||
301 | if "delete" in intf_data[intf_name][protocol]["query"]: | |
302 | cmd = "no {}".format(cmd) | |
303 | ||
304 | config_data.append(cmd) | |
4f99894d CH |
305 | if config_data: |
306 | config_data_dict[router] = config_data | |
e8cd26fd | 307 | |
4f99894d CH |
308 | try: |
309 | result = create_common_configurations( | |
310 | tgen, config_data_dict, "interface_config", build=build | |
311 | ) | |
312 | except InvalidCLIError: | |
313 | logger.error("create_igmp_config", exc_info=True) | |
314 | result = False | |
e8cd26fd | 315 | |
316 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
317 | return result | |
318 | ||
319 | ||
4f99894d | 320 | def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False): |
e8cd26fd | 321 | """ |
322 | Helper API to enable or disable pim on interfaces | |
323 | ||
324 | Parameters | |
325 | ---------- | |
326 | * `tgen` : Topogen object | |
327 | * `topo` : json file data | |
328 | * `input_dict` : Input dict data, required when configuring from testcase | |
329 | * `router` : router id to be configured. | |
330 | * `build` : Only for initial setup phase this is set as True. | |
331 | ||
332 | Returns | |
333 | ------- | |
4f99894d | 334 | list of config |
e8cd26fd | 335 | """ |
e8cd26fd | 336 | |
4f99894d | 337 | config_data = [] |
e8cd26fd | 338 | |
4f99894d CH |
339 | # Enable pim on interfaces |
340 | for destRouterLink, data in sorted(topo[router]["links"].items()): | |
341 | if "pim" in data and data["pim"] == "enable": | |
342 | # Loopback interfaces | |
343 | if "type" in data and data["type"] == "loopback": | |
344 | interface_name = destRouterLink | |
345 | else: | |
346 | interface_name = data["interface"] | |
347 | ||
348 | cmd = "interface {}".format(interface_name) | |
349 | config_data.append(cmd) | |
350 | config_data.append("ip pim") | |
351 | ||
352 | # pim global config | |
353 | if "pim" in input_dict[router]: | |
354 | pim_data = input_dict[router]["pim"] | |
355 | del_action = pim_data.setdefault("delete", False) | |
356 | for t in [ | |
a53c08bc CH |
357 | "join-prune-interval", |
358 | "keep-alive-timer", | |
359 | "register-suppress-time", | |
4f99894d CH |
360 | ]: |
361 | if t in pim_data: | |
362 | cmd = "ip pim {} {}".format(t, pim_data[t]) | |
363 | if del_action: | |
364 | cmd = "no {}".format(cmd) | |
0a76e764 | 365 | config_data.append(cmd) |
e8cd26fd | 366 | |
4f99894d | 367 | return config_data |
e8cd26fd | 368 | |
369 | ||
e8cd26fd | 370 | def find_rp_details(tgen, topo): |
371 | """ | |
372 | Find who is RP in topology and returns list of RPs | |
373 | ||
374 | Parameters: | |
375 | ----------- | |
376 | * `tgen` : Topogen object | |
377 | * `topo` : json file data | |
378 | ||
379 | returns: | |
380 | -------- | |
381 | errormsg or True | |
382 | """ | |
383 | ||
384 | rp_details = {} | |
385 | ||
386 | router_list = tgen.routers() | |
387 | topo_data = topo["routers"] | |
388 | ||
389 | for router in router_list.keys(): | |
390 | ||
391 | if "pim" not in topo_data[router]: | |
392 | continue | |
393 | ||
394 | pim_data = topo_data[router]["pim"] | |
395 | if "rp" in pim_data: | |
396 | rp_data = pim_data["rp"] | |
397 | for rp_dict in rp_data: | |
398 | # ip address of RP | |
399 | rp_addr = rp_dict["rp_addr"] | |
400 | ||
401 | for link, data in topo["routers"][router]["links"].items(): | |
402 | if data["ipv4"].split("/")[0] == rp_addr: | |
403 | rp_details[router] = rp_addr | |
404 | ||
405 | return rp_details | |
406 | ||
407 | ||
408 | def configure_pim_force_expire(tgen, topo, input_dict, build=False): | |
409 | """ | |
410 | Helper API to create pim configuration. | |
411 | ||
412 | Parameters | |
413 | ---------- | |
414 | * `tgen` : Topogen object | |
415 | * `topo` : json file data | |
416 | * `input_dict` : Input dict data, required when configuring from testcase | |
417 | * `build` : Only for initial setup phase this is set as True. | |
418 | ||
419 | Usage | |
420 | ----- | |
421 | input_dict ={ | |
422 | "l1": { | |
423 | "pim": { | |
424 | "force_expire":{ | |
425 | "10.0.10.1": ["255.1.1.1"] | |
426 | } | |
427 | } | |
428 | } | |
429 | } | |
430 | ||
431 | result = create_pim_config(tgen, topo, input_dict) | |
432 | ||
433 | Returns | |
434 | ------- | |
435 | True or False | |
436 | """ | |
437 | ||
438 | result = False | |
439 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
4f99894d | 440 | |
e8cd26fd | 441 | try: |
4f99894d | 442 | config_data_dict = {} |
e8cd26fd | 443 | |
444 | for dut in input_dict.keys(): | |
445 | if "pim" not in input_dict[dut]: | |
446 | continue | |
447 | ||
448 | pim_data = input_dict[dut]["pim"] | |
449 | ||
4f99894d | 450 | config_data = [] |
e8cd26fd | 451 | if "force_expire" in pim_data: |
e8cd26fd | 452 | force_expire_data = pim_data["force_expire"] |
453 | ||
454 | for source, groups in force_expire_data.items(): | |
455 | if type(groups) is not list: | |
456 | groups = [groups] | |
457 | ||
458 | for group in groups: | |
459 | cmd = "ip pim force-expire source {} group {}".format( | |
460 | source, group | |
461 | ) | |
462 | config_data.append(cmd) | |
463 | ||
4f99894d CH |
464 | if config_data: |
465 | config_data_dict[dut] = config_data | |
e8cd26fd | 466 | |
4f99894d CH |
467 | result = create_common_configurations( |
468 | tgen, config_data_dict, "pim", build=build | |
469 | ) | |
e8cd26fd | 470 | except InvalidCLIError: |
4f99894d CH |
471 | logger.error("configure_pim_force_expire", exc_info=True) |
472 | result = False | |
e8cd26fd | 473 | |
474 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
475 | return result | |
476 | ||
477 | ||
478 | ############################################# | |
479 | # Verification APIs | |
480 | ############################################# | |
ed776e38 | 481 | @retry(retry_timeout=12) |
3c41ebf8 | 482 | def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True): |
e8cd26fd | 483 | """ |
484 | Verify all PIM neighbors are up and running, config is verified | |
485 | using "show ip pim neighbor" cli | |
486 | ||
487 | Parameters | |
488 | ---------- | |
489 | * `tgen`: topogen object | |
490 | * `topo` : json file data | |
491 | * `dut` : dut info | |
492 | * `iface` : link for which PIM nbr need to check | |
eab72dc8 | 493 | * `nbr_ip` : neighbor ip of interface |
3c41ebf8 | 494 | * `expected` : expected results from API, by-default True |
e8cd26fd | 495 | |
496 | Usage | |
497 | ----- | |
eab72dc8 | 498 | result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2) |
e8cd26fd | 499 | |
500 | Returns | |
501 | ------- | |
502 | errormsg(str) or True | |
503 | """ | |
504 | ||
505 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
506 | ||
507 | for router in tgen.routers(): | |
508 | if dut is not None and dut != router: | |
509 | continue | |
510 | ||
511 | rnode = tgen.routers()[router] | |
512 | show_ip_pim_neighbor_json = rnode.vtysh_cmd( | |
513 | "show ip pim neighbor json", isjson=True | |
514 | ) | |
515 | ||
516 | for destLink, data in topo["routers"][router]["links"].items(): | |
517 | if iface is not None and iface != data["interface"]: | |
518 | continue | |
519 | ||
520 | if "type" in data and data["type"] == "loopback": | |
521 | continue | |
522 | ||
523 | if "pim" not in data: | |
524 | continue | |
525 | ||
526 | if "pim" in data and data["pim"] == "enable": | |
527 | local_interface = data["interface"] | |
528 | ||
529 | if "-" in destLink: | |
530 | # Spliting and storing destRouterLink data in tempList | |
531 | tempList = destLink.split("-") | |
532 | ||
533 | # destRouter | |
534 | destLink = tempList.pop(0) | |
535 | ||
536 | # Current Router Link | |
537 | tempList.insert(0, router) | |
538 | curRouter = "-".join(tempList) | |
539 | else: | |
540 | curRouter = router | |
541 | if destLink not in topo["routers"]: | |
542 | continue | |
543 | data = topo["routers"][destLink]["links"][curRouter] | |
544 | if "type" in data and data["type"] == "loopback": | |
545 | continue | |
546 | ||
547 | if "pim" not in data: | |
548 | continue | |
549 | ||
550 | logger.info("[DUT: %s]: Verifying PIM neighbor status:", router) | |
551 | ||
552 | if "pim" in data and data["pim"] == "enable": | |
553 | pim_nh_intf_ip = data["ipv4"].split("/")[0] | |
554 | ||
555 | # Verifying PIM neighbor | |
556 | if local_interface in show_ip_pim_neighbor_json: | |
557 | if show_ip_pim_neighbor_json[local_interface]: | |
558 | if ( | |
559 | show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][ | |
560 | "neighbor" | |
561 | ] | |
562 | != pim_nh_intf_ip | |
563 | ): | |
564 | errormsg = ( | |
565 | "[DUT %s]: Local interface: %s, PIM" | |
566 | " neighbor check failed " | |
567 | "Expected neighbor: %s, Found neighbor:" | |
568 | " %s" | |
569 | % ( | |
570 | router, | |
571 | local_interface, | |
572 | pim_nh_intf_ip, | |
573 | show_ip_pim_neighbor_json[local_interface][ | |
574 | pim_nh_intf_ip | |
575 | ]["neighbor"], | |
576 | ) | |
577 | ) | |
578 | return errormsg | |
579 | ||
580 | logger.info( | |
581 | "[DUT %s]: Local interface: %s, Found" | |
582 | " expected PIM neighbor %s", | |
583 | router, | |
584 | local_interface, | |
585 | pim_nh_intf_ip, | |
586 | ) | |
587 | else: | |
588 | errormsg = ( | |
589 | "[DUT %s]: Local interface: %s, and" | |
590 | "interface ip: %s is not found in " | |
591 | "PIM neighbor " % (router, local_interface, pim_nh_intf_ip) | |
592 | ) | |
593 | return errormsg | |
594 | else: | |
595 | errormsg = ( | |
596 | "[DUT %s]: Local interface: %s, is not " | |
597 | "present in PIM neighbor " % (router, local_interface) | |
598 | ) | |
599 | return errormsg | |
600 | ||
601 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
602 | return True | |
603 | ||
604 | ||
ed776e38 | 605 | @retry(retry_timeout=40) |
3c41ebf8 | 606 | def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True): |
e8cd26fd | 607 | """ |
608 | Verify IGMP groups are received from an intended interface | |
609 | by running "show ip igmp groups" command | |
610 | ||
611 | Parameters | |
612 | ---------- | |
613 | * `tgen`: topogen object | |
614 | * `dut`: device under test | |
615 | * `interface`: interface, from which IGMP groups would be received | |
616 | * `group_addresses`: IGMP group address | |
3c41ebf8 | 617 | * `expected` : expected results from API, by-default True |
e8cd26fd | 618 | |
619 | Usage | |
620 | ----- | |
621 | dut = "r1" | |
622 | interface = "r1-r0-eth0" | |
623 | group_address = "225.1.1.1" | |
624 | result = verify_igmp_groups(tgen, dut, interface, group_address) | |
625 | ||
626 | Returns | |
627 | ------- | |
628 | errormsg(str) or True | |
629 | """ | |
630 | ||
631 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
632 | ||
633 | if dut not in tgen.routers(): | |
634 | return False | |
635 | ||
636 | rnode = tgen.routers()[dut] | |
637 | ||
638 | logger.info("[DUT: %s]: Verifying IGMP groups received:", dut) | |
639 | show_ip_igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
640 | ||
641 | if type(group_addresses) is not list: | |
642 | group_addresses = [group_addresses] | |
643 | ||
644 | if interface in show_ip_igmp_json: | |
645 | show_ip_igmp_json = show_ip_igmp_json[interface]["groups"] | |
646 | else: | |
647 | errormsg = ( | |
648 | "[DUT %s]: Verifying IGMP group received" | |
649 | " from interface %s [FAILED]!! " % (dut, interface) | |
650 | ) | |
651 | return errormsg | |
652 | ||
653 | found = False | |
654 | for grp_addr in group_addresses: | |
655 | for index in show_ip_igmp_json: | |
656 | if index["group"] == grp_addr: | |
657 | found = True | |
658 | break | |
659 | if found is not True: | |
660 | errormsg = ( | |
661 | "[DUT %s]: Verifying IGMP group received" | |
662 | " from interface %s [FAILED]!! " | |
663 | " Expected not found: %s" % (dut, interface, grp_addr) | |
664 | ) | |
665 | return errormsg | |
666 | ||
667 | logger.info( | |
668 | "[DUT %s]: Verifying IGMP group %s received " | |
669 | "from interface %s [PASSED]!! ", | |
670 | dut, | |
671 | grp_addr, | |
672 | interface, | |
673 | ) | |
674 | ||
675 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
676 | return True | |
677 | ||
678 | ||
ed776e38 | 679 | @retry(retry_timeout=60) |
e8cd26fd | 680 | def verify_upstream_iif( |
a53c08bc CH |
681 | tgen, |
682 | dut, | |
683 | iif, | |
684 | src_address, | |
685 | group_addresses, | |
686 | joinState=None, | |
687 | refCount=1, | |
688 | expected=True, | |
e8cd26fd | 689 | ): |
690 | """ | |
691 | Verify upstream inbound interface is updated correctly | |
692 | by running "show ip pim upstream" cli | |
693 | ||
694 | Parameters | |
695 | ---------- | |
696 | * `tgen`: topogen object | |
697 | * `dut`: device under test | |
698 | * `iif`: inbound interface | |
699 | * `src_address`: source address | |
700 | * `group_addresses`: IGMP group address | |
701 | * `joinState`: upstream join state | |
702 | * `refCount`: refCount value | |
3c41ebf8 | 703 | * `expected` : expected results from API, by-default True |
e8cd26fd | 704 | |
705 | Usage | |
706 | ----- | |
707 | dut = "r1" | |
708 | iif = "r1-r0-eth0" | |
709 | src_address = "*" | |
710 | group_address = "225.1.1.1" | |
711 | result = verify_upstream_iif(tgen, dut, iif, src_address, group_address, | |
712 | state, refCount) | |
713 | ||
714 | Returns | |
715 | ------- | |
716 | errormsg(str) or True | |
717 | """ | |
718 | ||
719 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
720 | ||
721 | if dut not in tgen.routers(): | |
722 | return False | |
723 | ||
724 | rnode = tgen.routers()[dut] | |
725 | ||
726 | logger.info( | |
727 | "[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:", | |
728 | dut, | |
729 | ) | |
730 | show_ip_pim_upstream_json = run_frr_cmd( | |
731 | rnode, "show ip pim upstream json", isjson=True | |
732 | ) | |
733 | ||
734 | if type(group_addresses) is not list: | |
735 | group_addresses = [group_addresses] | |
736 | ||
737 | if type(iif) is not list: | |
738 | iif = [iif] | |
739 | ||
740 | for grp_addr in group_addresses: | |
741 | # Verify group address | |
742 | if grp_addr not in show_ip_pim_upstream_json: | |
743 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
744 | dut, | |
745 | grp_addr, | |
746 | ) | |
747 | return errormsg | |
748 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
749 | ||
750 | # Verify source address | |
751 | if src_address not in group_addr_json: | |
752 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
753 | dut, | |
754 | src_address, | |
755 | grp_addr, | |
756 | ) | |
757 | return errormsg | |
758 | ||
759 | # Verify Inbound Interface | |
760 | found = False | |
761 | for in_interface in iif: | |
762 | if group_addr_json[src_address]["inboundInterface"] == in_interface: | |
763 | if refCount > 0: | |
764 | logger.info( | |
765 | "[DUT %s]: Verifying refCount " | |
766 | "for (%s,%s) [PASSED]!! " | |
767 | " Found Expected: %s", | |
768 | dut, | |
769 | src_address, | |
770 | grp_addr, | |
771 | group_addr_json[src_address]["refCount"], | |
772 | ) | |
773 | found = True | |
774 | if found: | |
775 | if joinState is None: | |
776 | if group_addr_json[src_address]["joinState"] != "Joined": | |
777 | errormsg = ( | |
778 | "[DUT %s]: Verifying iif " | |
779 | "(Inbound Interface) for (%s,%s) and" | |
780 | " joinState :%s [FAILED]!! " | |
781 | " Expected: %s, Found: %s" | |
782 | % ( | |
783 | dut, | |
784 | src_address, | |
785 | grp_addr, | |
786 | group_addr_json[src_address]["joinState"], | |
787 | in_interface, | |
788 | group_addr_json[src_address]["inboundInterface"], | |
789 | ) | |
790 | ) | |
791 | return errormsg | |
792 | ||
793 | elif group_addr_json[src_address]["joinState"] != joinState: | |
794 | errormsg = ( | |
795 | "[DUT %s]: Verifying iif " | |
796 | "(Inbound Interface) for (%s,%s) and" | |
797 | " joinState :%s [FAILED]!! " | |
798 | " Expected: %s, Found: %s" | |
799 | % ( | |
800 | dut, | |
801 | src_address, | |
802 | grp_addr, | |
803 | group_addr_json[src_address]["joinState"], | |
804 | in_interface, | |
805 | group_addr_json[src_address]["inboundInterface"], | |
806 | ) | |
807 | ) | |
808 | return errormsg | |
809 | ||
810 | logger.info( | |
811 | "[DUT %s]: Verifying iif(Inbound Interface)" | |
812 | " for (%s,%s) and joinState is %s [PASSED]!! " | |
813 | " Found Expected: (%s)", | |
814 | dut, | |
815 | src_address, | |
816 | grp_addr, | |
817 | group_addr_json[src_address]["joinState"], | |
818 | group_addr_json[src_address]["inboundInterface"], | |
819 | ) | |
820 | if not found: | |
821 | errormsg = ( | |
822 | "[DUT %s]: Verifying iif " | |
823 | "(Inbound Interface) for (%s, %s) " | |
824 | "[FAILED]!! " | |
825 | " Expected: %s, Found: %s" | |
826 | % ( | |
827 | dut, | |
828 | src_address, | |
829 | grp_addr, | |
830 | in_interface, | |
831 | group_addr_json[src_address]["inboundInterface"], | |
832 | ) | |
833 | ) | |
834 | return errormsg | |
835 | ||
836 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
837 | return True | |
838 | ||
839 | ||
ed776e38 | 840 | @retry(retry_timeout=12) |
a53c08bc CH |
841 | def verify_join_state_and_timer( |
842 | tgen, dut, iif, src_address, group_addresses, expected=True | |
843 | ): | |
e8cd26fd | 844 | """ |
845 | Verify join state is updated correctly and join timer is | |
846 | running with the help of "show ip pim upstream" cli | |
847 | ||
848 | Parameters | |
849 | ---------- | |
850 | * `tgen`: topogen object | |
851 | * `dut`: device under test | |
852 | * `iif`: inbound interface | |
853 | * `src_address`: source address | |
854 | * `group_addresses`: IGMP group address | |
3c41ebf8 | 855 | * `expected` : expected results from API, by-default True |
e8cd26fd | 856 | |
857 | Usage | |
858 | ----- | |
859 | dut = "r1" | |
860 | iif = "r1-r0-eth0" | |
861 | group_address = "225.1.1.1" | |
862 | result = verify_join_state_and_timer(tgen, dut, iif, group_address) | |
863 | ||
864 | Returns | |
865 | ------- | |
866 | errormsg(str) or True | |
867 | """ | |
868 | ||
869 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
870 | errormsg = "" | |
871 | ||
872 | if dut not in tgen.routers(): | |
873 | return False | |
874 | ||
875 | rnode = tgen.routers()[dut] | |
876 | ||
877 | logger.info( | |
878 | "[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:", | |
879 | dut, | |
880 | ) | |
881 | show_ip_pim_upstream_json = run_frr_cmd( | |
882 | rnode, "show ip pim upstream json", isjson=True | |
883 | ) | |
884 | ||
885 | if type(group_addresses) is not list: | |
886 | group_addresses = [group_addresses] | |
887 | ||
888 | for grp_addr in group_addresses: | |
889 | # Verify group address | |
890 | if grp_addr not in show_ip_pim_upstream_json: | |
891 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
892 | dut, | |
893 | grp_addr, | |
894 | ) | |
895 | return errormsg | |
896 | ||
897 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
898 | ||
899 | # Verify source address | |
900 | if src_address not in group_addr_json: | |
901 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
902 | dut, | |
903 | src_address, | |
904 | grp_addr, | |
905 | ) | |
906 | return errormsg | |
907 | ||
908 | # Verify join state | |
909 | joinState = group_addr_json[src_address]["joinState"] | |
910 | if joinState != "Joined": | |
911 | error = ( | |
912 | "[DUT %s]: Verifying join state for" | |
913 | " (%s,%s) [FAILED]!! " | |
914 | " Expected: %s, Found: %s" | |
915 | % (dut, src_address, grp_addr, "Joined", joinState) | |
916 | ) | |
917 | errormsg = errormsg + "\n" + str(error) | |
918 | else: | |
919 | logger.info( | |
920 | "[DUT %s]: Verifying join state for" | |
921 | " (%s,%s) [PASSED]!! " | |
922 | " Found Expected: %s", | |
923 | dut, | |
924 | src_address, | |
925 | grp_addr, | |
926 | joinState, | |
927 | ) | |
928 | ||
929 | # Verify join timer | |
930 | joinTimer = group_addr_json[src_address]["joinTimer"] | |
931 | if not re.match(r"(\d{2}):(\d{2}):(\d{2})", joinTimer): | |
932 | error = ( | |
933 | "[DUT %s]: Verifying join timer for" | |
934 | " (%s,%s) [FAILED]!! " | |
49581587 CH |
935 | " Expected: %s, Found: %s" |
936 | ) % ( | |
e8cd26fd | 937 | dut, |
938 | src_address, | |
939 | grp_addr, | |
940 | "join timer should be running", | |
941 | joinTimer, | |
942 | ) | |
943 | errormsg = errormsg + "\n" + str(error) | |
944 | else: | |
945 | logger.info( | |
946 | "[DUT %s]: Verifying join timer is running" | |
947 | " for (%s,%s) [PASSED]!! " | |
948 | " Found Expected: %s", | |
949 | dut, | |
950 | src_address, | |
951 | grp_addr, | |
952 | joinTimer, | |
953 | ) | |
954 | ||
955 | if errormsg != "": | |
956 | return errormsg | |
957 | ||
958 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
959 | return True | |
960 | ||
961 | ||
4f99894d | 962 | @retry(retry_timeout=120) |
e8cd26fd | 963 | def verify_ip_mroutes( |
a53c08bc CH |
964 | tgen, |
965 | dut, | |
966 | src_address, | |
967 | group_addresses, | |
968 | iif, | |
969 | oil, | |
970 | return_uptime=False, | |
971 | mwait=0, | |
972 | expected=True, | |
e8cd26fd | 973 | ): |
974 | """ | |
975 | Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes | |
976 | by running "show ip pim upstream" cli | |
977 | ||
978 | Parameters | |
979 | ---------- | |
980 | * `tgen`: topogen object | |
981 | * `dut`: device under test | |
982 | * `src_address`: source address | |
983 | * `group_addresses`: IGMP group address | |
984 | * `iif`: Incoming interface | |
985 | * `oil`: Outgoing interface | |
986 | * `return_uptime`: If True, return uptime dict, default is False | |
987 | * `mwait`: Wait time, default is 0 | |
3c41ebf8 | 988 | * `expected` : expected results from API, by-default True |
e8cd26fd | 989 | |
990 | Usage | |
991 | ----- | |
992 | dut = "r1" | |
993 | group_address = "225.1.1.1" | |
994 | result = verify_ip_mroutes(tgen, dut, src_address, group_address) | |
995 | ||
996 | Returns | |
997 | ------- | |
998 | errormsg(str) or True | |
999 | """ | |
1000 | ||
1001 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1002 | ||
1003 | if dut not in tgen.routers(): | |
1004 | return False | |
1005 | ||
1006 | rnode = tgen.routers()[dut] | |
1007 | ||
1008 | if return_uptime: | |
1009 | logger.info("Sleeping for %s sec..", mwait) | |
1010 | sleep(mwait) | |
1011 | ||
1012 | logger.info("[DUT: %s]: Verifying ip mroutes", dut) | |
1013 | show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
1014 | ||
1015 | if return_uptime: | |
1016 | uptime_dict = {} | |
1017 | ||
1018 | if bool(show_ip_mroute_json) == False: | |
1019 | error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) | |
1020 | return error_msg | |
1021 | ||
1022 | if not isinstance(group_addresses, list): | |
1023 | group_addresses = [group_addresses] | |
1024 | ||
94e8feaa | 1025 | if not isinstance(iif, list) and iif != "none": |
e8cd26fd | 1026 | iif = [iif] |
1027 | ||
94e8feaa | 1028 | if not isinstance(oil, list) and oil != "none": |
e8cd26fd | 1029 | oil = [oil] |
1030 | ||
1031 | for grp_addr in group_addresses: | |
1032 | if grp_addr not in show_ip_mroute_json: | |
1033 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
1034 | dut, | |
1035 | src_address, | |
1036 | grp_addr, | |
1037 | ) | |
1038 | return errormsg | |
1039 | else: | |
1040 | if return_uptime: | |
1041 | uptime_dict[grp_addr] = {} | |
1042 | ||
1043 | group_addr_json = show_ip_mroute_json[grp_addr] | |
1044 | ||
1045 | if src_address not in group_addr_json: | |
1046 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
1047 | dut, | |
1048 | src_address, | |
1049 | grp_addr, | |
1050 | ) | |
1051 | return errormsg | |
1052 | else: | |
1053 | if return_uptime: | |
1054 | uptime_dict[grp_addr][src_address] = {} | |
1055 | ||
1056 | mroutes = group_addr_json[src_address] | |
1057 | ||
1058 | if mroutes["installed"] != 0: | |
1059 | logger.info( | |
1060 | "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr | |
1061 | ) | |
1062 | ||
1063 | if "oil" not in mroutes: | |
1064 | if oil == "none" and mroutes["iif"] in iif: | |
1065 | logger.info( | |
1066 | "[DUT %s]: Verifying (%s, %s) mroute," | |
1067 | " [PASSED]!! Found Expected: " | |
1068 | "(iif: %s, oil: %s, installed: (%s,%s))", | |
1069 | dut, | |
1070 | src_address, | |
1071 | grp_addr, | |
1072 | mroutes["iif"], | |
1073 | oil, | |
1074 | src_address, | |
1075 | grp_addr, | |
1076 | ) | |
1077 | else: | |
1078 | errormsg = ( | |
1079 | "[DUT %s]: Verifying (%s, %s) mroute," | |
1080 | " [FAILED]!! " | |
1081 | "Expected: (oil: %s, installed:" | |
1082 | " (%s,%s)) Found: ( oil: none, " | |
1083 | "installed: (%s,%s))" | |
1084 | % ( | |
1085 | dut, | |
1086 | src_address, | |
1087 | grp_addr, | |
1088 | oil, | |
1089 | src_address, | |
1090 | grp_addr, | |
1091 | src_address, | |
1092 | grp_addr, | |
1093 | ) | |
1094 | ) | |
1095 | ||
1096 | return errormsg | |
1097 | ||
1098 | else: | |
1099 | found = False | |
1100 | for route, data in mroutes["oil"].items(): | |
1101 | if route in oil and route != "pimreg": | |
1102 | if ( | |
1103 | data["source"] == src_address | |
1104 | and data["group"] == grp_addr | |
1105 | and data["inboundInterface"] in iif | |
1106 | and data["outboundInterface"] in oil | |
1107 | ): | |
1108 | if return_uptime: | |
1109 | ||
1110 | uptime_dict[grp_addr][src_address] = data["upTime"] | |
1111 | ||
1112 | logger.info( | |
1113 | "[DUT %s]: Verifying (%s, %s)" | |
1114 | " mroute, [PASSED]!! " | |
1115 | "Found Expected: " | |
1116 | "(iif: %s, oil: %s, installed:" | |
1117 | " (%s,%s)", | |
1118 | dut, | |
1119 | src_address, | |
1120 | grp_addr, | |
1121 | data["inboundInterface"], | |
1122 | data["outboundInterface"], | |
1123 | data["source"], | |
1124 | data["group"], | |
1125 | ) | |
1126 | found = True | |
1127 | break | |
1128 | else: | |
1129 | continue | |
1130 | ||
1131 | if not found: | |
1132 | errormsg = ( | |
1133 | "[DUT %s]: Verifying (%s, %s)" | |
1134 | " mroute [FAILED]!! " | |
1135 | "Expected in: (iif: %s, oil: %s," | |
1136 | " installed: (%s,%s)) Found: " | |
1137 | "(iif: %s, oil: %s, " | |
1138 | "installed: (%s,%s))" | |
1139 | % ( | |
1140 | dut, | |
1141 | src_address, | |
1142 | grp_addr, | |
1143 | iif, | |
1144 | oil, | |
1145 | src_address, | |
1146 | grp_addr, | |
1147 | data["inboundInterface"], | |
1148 | data["outboundInterface"], | |
1149 | data["source"], | |
1150 | data["group"], | |
1151 | ) | |
1152 | ) | |
1153 | return errormsg | |
1154 | ||
1155 | else: | |
1156 | errormsg = "[DUT %s]: mroute (%s,%s) is not installed" % ( | |
1157 | dut, | |
1158 | src_address, | |
1159 | grp_addr, | |
1160 | ) | |
1161 | return errormsg | |
1162 | ||
1163 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1164 | return True if return_uptime == False else uptime_dict | |
1165 | ||
1166 | ||
ed776e38 | 1167 | @retry(retry_timeout=60) |
e8cd26fd | 1168 | def verify_pim_rp_info( |
a53c08bc CH |
1169 | tgen, |
1170 | topo, | |
1171 | dut, | |
1172 | group_addresses, | |
1173 | oif=None, | |
1174 | rp=None, | |
1175 | source=None, | |
1176 | iamrp=None, | |
1177 | expected=True, | |
e8cd26fd | 1178 | ): |
1179 | """ | |
1180 | Verify pim rp info by running "show ip pim rp-info" cli | |
1181 | ||
1182 | Parameters | |
1183 | ---------- | |
1184 | * `tgen`: topogen object | |
1185 | * `topo`: JSON file handler | |
1186 | * `dut`: device under test | |
1187 | * `group_addresses`: IGMP group address | |
1188 | * `oif`: outbound interface name | |
1189 | * `rp`: RP address | |
1190 | * `source`: Source of RP | |
1191 | * `iamrp`: User defined RP | |
3c41ebf8 | 1192 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1193 | |
1194 | Usage | |
1195 | ----- | |
1196 | dut = "r1" | |
1197 | result = verify_pim_rp_info(tgen, topo, dut, group_address, | |
1198 | rp=rp, source="BSR") | |
1199 | ||
1200 | Returns | |
1201 | ------- | |
1202 | errormsg(str) or True | |
1203 | """ | |
1204 | ||
1205 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1206 | ||
1207 | if dut not in tgen.routers(): | |
1208 | return False | |
1209 | ||
1210 | rnode = tgen.routers()[dut] | |
1211 | ||
1212 | logger.info("[DUT: %s]: Verifying ip rp info", dut) | |
1213 | show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True) | |
1214 | ||
1215 | if type(group_addresses) is not list: | |
1216 | group_addresses = [group_addresses] | |
1217 | ||
1218 | if type(oif) is not list: | |
1219 | oif = [oif] | |
1220 | ||
1221 | for grp_addr in group_addresses: | |
1222 | if rp is None: | |
1223 | rp_details = find_rp_details(tgen, topo) | |
1224 | ||
1225 | if dut in rp_details: | |
1226 | iamRP = True | |
1227 | else: | |
1228 | iamRP = False | |
1229 | else: | |
1230 | show_ip_route_json = run_frr_cmd( | |
1231 | rnode, "show ip route connected json", isjson=True | |
1232 | ) | |
1233 | for _rp in show_ip_route_json.keys(): | |
1234 | if rp == _rp.split("/")[0]: | |
1235 | iamRP = True | |
1236 | break | |
1237 | else: | |
1238 | iamRP = False | |
1239 | ||
1240 | if rp not in show_ip_rp_info_json: | |
1241 | errormsg = "[DUT %s]: Verifying rp-info" "for rp_address %s [FAILED]!! " % ( | |
1242 | dut, | |
1243 | rp, | |
1244 | ) | |
1245 | return errormsg | |
1246 | else: | |
1247 | group_addr_json = show_ip_rp_info_json[rp] | |
1248 | ||
1249 | for rp_json in group_addr_json: | |
1250 | if oif is not None: | |
1251 | found = False | |
1252 | if rp_json["outboundInterface"] not in oif: | |
1253 | errormsg = ( | |
1254 | "[DUT %s]: Verifying OIF " | |
1255 | "for group %s and RP %s [FAILED]!! " | |
1256 | "Expected interfaces: (%s)," | |
1257 | " Found: (%s)" | |
1258 | % (dut, grp_addr, rp, oif, rp_json["outboundInterface"]) | |
1259 | ) | |
1260 | return errormsg | |
1261 | ||
1262 | logger.info( | |
1263 | "[DUT %s]: Verifying OIF " | |
1264 | "for group %s and RP %s [PASSED]!! " | |
1265 | "Found Expected: (%s)" | |
1266 | % (dut, grp_addr, rp, rp_json["outboundInterface"]) | |
1267 | ) | |
1268 | ||
1269 | if source is not None: | |
1270 | if rp_json["source"] != source: | |
1271 | errormsg = ( | |
1272 | "[DUT %s]: Verifying SOURCE " | |
1273 | "for group %s and RP %s [FAILED]!! " | |
1274 | "Expected: (%s)," | |
1275 | " Found: (%s)" % (dut, grp_addr, rp, source, rp_json["source"]) | |
1276 | ) | |
1277 | return errormsg | |
1278 | ||
1279 | logger.info( | |
1280 | "[DUT %s]: Verifying SOURCE " | |
1281 | "for group %s and RP %s [PASSED]!! " | |
1282 | "Found Expected: (%s)" % (dut, grp_addr, rp, rp_json["source"]) | |
1283 | ) | |
1284 | ||
1285 | if rp_json["group"] == grp_addr and iamrp is not None: | |
1286 | if iamRP: | |
1287 | if rp_json["iAmRP"]: | |
1288 | logger.info( | |
1289 | "[DUT %s]: Verifying group " | |
1290 | "and iAmRP [PASSED]!!" | |
1291 | " Found Expected: (%s, %s:%s)", | |
1292 | dut, | |
1293 | grp_addr, | |
1294 | "iAmRP", | |
1295 | rp_json["iAmRP"], | |
1296 | ) | |
1297 | else: | |
1298 | errormsg = ( | |
1299 | "[DUT %s]: Verifying group" | |
1300 | "%s and iAmRP [FAILED]!! " | |
1301 | "Expected: (iAmRP: %s)," | |
1302 | " Found: (iAmRP: %s)" | |
1303 | % (dut, grp_addr, "true", rp_json["iAmRP"]) | |
1304 | ) | |
1305 | return errormsg | |
1306 | ||
1307 | if not iamRP: | |
1308 | if rp_json["iAmRP"] == False: | |
1309 | logger.info( | |
1310 | "[DUT %s]: Verifying group " | |
1311 | "and iAmNotRP [PASSED]!!" | |
1312 | " Found Expected: (%s, %s:%s)", | |
1313 | dut, | |
1314 | grp_addr, | |
1315 | "iAmRP", | |
1316 | rp_json["iAmRP"], | |
1317 | ) | |
1318 | else: | |
1319 | errormsg = ( | |
1320 | "[DUT %s]: Verifying group" | |
1321 | "%s and iAmRP [FAILED]!! " | |
1322 | "Expected: (iAmRP: %s)," | |
1323 | " Found: (iAmRP: %s)" | |
1324 | % (dut, grp_addr, "false", rp_json["iAmRP"]) | |
1325 | ) | |
1326 | return errormsg | |
1327 | ||
1328 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1329 | return True | |
1330 | ||
1331 | ||
ed776e38 | 1332 | @retry(retry_timeout=60) |
e8cd26fd | 1333 | def verify_pim_state( |
a53c08bc CH |
1334 | tgen, |
1335 | dut, | |
1336 | iif, | |
1337 | oil, | |
1338 | group_addresses, | |
1339 | src_address=None, | |
1340 | installed_fl=None, | |
1341 | expected=True, | |
e8cd26fd | 1342 | ): |
1343 | """ | |
1344 | Verify pim state by running "show ip pim state" cli | |
1345 | ||
1346 | Parameters | |
1347 | ---------- | |
1348 | * `tgen`: topogen object | |
1349 | * `dut`: device under test | |
1350 | * `iif`: inbound interface | |
1351 | * `oil`: outbound interface | |
1352 | * `group_addresses`: IGMP group address | |
1353 | * `src_address`: source address, default = None | |
1354 | * installed_fl` : Installed flag | |
3c41ebf8 | 1355 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1356 | |
1357 | Usage | |
1358 | ----- | |
1359 | dut = "r1" | |
1360 | iif = "r1-r3-eth1" | |
1361 | oil = "r1-r0-eth0" | |
1362 | group_address = "225.1.1.1" | |
1363 | result = verify_pim_state(tgen, dut, iif, oil, group_address) | |
1364 | ||
1365 | Returns | |
1366 | ------- | |
1367 | errormsg(str) or True | |
1368 | """ | |
1369 | ||
1370 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1371 | ||
1372 | if dut not in tgen.routers(): | |
1373 | return False | |
1374 | ||
1375 | rnode = tgen.routers()[dut] | |
1376 | ||
1377 | logger.info("[DUT: %s]: Verifying pim state", dut) | |
1378 | show_pim_state_json = run_frr_cmd(rnode, "show ip pim state json", isjson=True) | |
1379 | ||
1380 | if installed_fl is None: | |
1381 | installed_fl = 1 | |
1382 | ||
1383 | if type(group_addresses) is not list: | |
1384 | group_addresses = [group_addresses] | |
1385 | ||
1386 | for grp_addr in group_addresses: | |
1387 | if src_address is None: | |
1388 | src_address = "*" | |
1389 | pim_state_json = show_pim_state_json[grp_addr][src_address] | |
1390 | else: | |
1391 | pim_state_json = show_pim_state_json[grp_addr][src_address] | |
1392 | ||
1393 | if pim_state_json["Installed"] == installed_fl: | |
1394 | logger.info( | |
1395 | "[DUT %s]: group %s is installed flag: %s", | |
1396 | dut, | |
1397 | grp_addr, | |
1398 | pim_state_json["Installed"], | |
1399 | ) | |
1400 | for interface, data in pim_state_json[iif].items(): | |
1401 | if interface != oil: | |
1402 | continue | |
1403 | ||
1404 | # Verify iif, oil and installed state | |
1405 | if ( | |
1406 | data["group"] == grp_addr | |
1407 | and data["installed"] == installed_fl | |
1408 | and data["inboundInterface"] == iif | |
1409 | and data["outboundInterface"] == oil | |
1410 | ): | |
1411 | logger.info( | |
1412 | "[DUT %s]: Verifying pim state for group" | |
1413 | " %s [PASSED]!! Found Expected: " | |
1414 | "(iif: %s, oil: %s, installed: %s) ", | |
1415 | dut, | |
1416 | grp_addr, | |
1417 | data["inboundInterface"], | |
1418 | data["outboundInterface"], | |
1419 | data["installed"], | |
1420 | ) | |
1421 | else: | |
1422 | errormsg = ( | |
1423 | "[DUT %s]: Verifying pim state for group" | |
1424 | " %s, [FAILED]!! Expected: " | |
5aab262b QY |
1425 | "(iif: %s, oil: %s, installed: %s) " |
1426 | % (dut, grp_addr, iif, oil, "1"), | |
e8cd26fd | 1427 | "Found: (iif: %s, oil: %s, installed: %s)" |
1428 | % ( | |
e8cd26fd | 1429 | data["inboundInterface"], |
1430 | data["outboundInterface"], | |
1431 | data["installed"], | |
1432 | ), | |
1433 | ) | |
1434 | return errormsg | |
1435 | else: | |
1436 | errormsg = "[DUT %s]: %s install flag value not as expected" % ( | |
1437 | dut, | |
1438 | grp_addr, | |
1439 | ) | |
1440 | return errormsg | |
1441 | ||
1442 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1443 | return True | |
1444 | ||
1445 | ||
1446 | def verify_pim_interface_traffic(tgen, input_dict): | |
1447 | """ | |
1448 | Verify ip pim interface traffice by running | |
1449 | "show ip pim interface traffic" cli | |
1450 | ||
1451 | Parameters | |
1452 | ---------- | |
1453 | * `tgen`: topogen object | |
1454 | * `input_dict(dict)`: defines DUT, what and from which interfaces | |
1455 | traffic needs to be verified | |
1456 | Usage | |
1457 | ----- | |
1458 | input_dict = { | |
1459 | "r1": { | |
1460 | "r1-r0-eth0": { | |
1461 | "helloRx": 0, | |
1462 | "helloTx": 1, | |
1463 | "joinRx": 0, | |
1464 | "joinTx": 0 | |
1465 | } | |
1466 | } | |
1467 | } | |
1468 | ||
1469 | result = verify_pim_interface_traffic(tgen, input_dict) | |
1470 | ||
1471 | Returns | |
1472 | ------- | |
1473 | errormsg(str) or True | |
1474 | """ | |
1475 | ||
1476 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1477 | ||
1478 | output_dict = {} | |
1479 | for dut in input_dict.keys(): | |
1480 | if dut not in tgen.routers(): | |
1481 | continue | |
1482 | ||
1483 | rnode = tgen.routers()[dut] | |
1484 | ||
1485 | logger.info("[DUT: %s]: Verifying pim interface traffic", dut) | |
e8cd26fd | 1486 | |
3d7d6e9a DS |
1487 | def show_pim_intf_traffic(rnode, dut, input_dict, output_dict): |
1488 | show_pim_intf_traffic_json = run_frr_cmd( | |
1489 | rnode, "show ip pim interface traffic json", isjson=True | |
1490 | ) | |
e8cd26fd | 1491 | |
3d7d6e9a DS |
1492 | output_dict[dut] = {} |
1493 | for intf, data in input_dict[dut].items(): | |
1494 | interface_json = show_pim_intf_traffic_json[intf] | |
1495 | for state in data: | |
1496 | ||
1497 | # Verify Tx/Rx | |
1498 | if state in interface_json: | |
1499 | output_dict[dut][state] = interface_json[state] | |
1500 | else: | |
1501 | errormsg = ( | |
1502 | "[DUT %s]: %s is not present" | |
1503 | "for interface %s [FAILED]!! " % (dut, state, intf) | |
1504 | ) | |
1505 | return errormsg | |
1506 | return None | |
1507 | ||
1508 | test_func = functools.partial( | |
1509 | show_pim_intf_traffic, rnode, dut, input_dict, output_dict | |
1510 | ) | |
1511 | (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1) | |
1512 | if not result: | |
1513 | return out | |
e8cd26fd | 1514 | |
1515 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1516 | return output_dict | |
1517 | ||
1518 | ||
ed776e38 | 1519 | @retry(retry_timeout=40) |
a53c08bc CH |
1520 | def verify_pim_interface( |
1521 | tgen, topo, dut, interface=None, interface_ip=None, expected=True | |
1522 | ): | |
e8cd26fd | 1523 | """ |
1524 | Verify all PIM interface are up and running, config is verified | |
1525 | using "show ip pim interface" cli | |
1526 | ||
1527 | Parameters | |
1528 | ---------- | |
1529 | * `tgen`: topogen object | |
1530 | * `topo` : json file data | |
1531 | * `dut` : device under test | |
eab72dc8 | 1532 | * `interface` : interface name |
1533 | * `interface_ip` : interface ip address | |
3c41ebf8 | 1534 | * `expected` : expected results from API, by-default True |
e8cd26fd | 1535 | |
1536 | Usage | |
1537 | ----- | |
eab72dc8 | 1538 | result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1) |
e8cd26fd | 1539 | |
1540 | Returns | |
1541 | ------- | |
1542 | errormsg(str) or True | |
1543 | """ | |
1544 | ||
1545 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1546 | ||
1547 | for router in tgen.routers(): | |
1548 | if router != dut: | |
1549 | continue | |
1550 | ||
1551 | logger.info("[DUT: %s]: Verifying PIM interface status:", dut) | |
1552 | ||
1553 | rnode = tgen.routers()[dut] | |
5980ad0a DS |
1554 | show_ip_pim_interface_json = rnode.vtysh_cmd( |
1555 | "show ip pim interface json", isjson=True | |
1556 | ) | |
eab72dc8 | 1557 | |
5980ad0a | 1558 | logger.info("show_ip_pim_interface_json: \n %s", show_ip_pim_interface_json) |
eab72dc8 | 1559 | |
1560 | if interface_ip: | |
1561 | if interface in show_ip_pim_interface_json: | |
1562 | pim_intf_json = show_ip_pim_interface_json[interface] | |
1563 | if pim_intf_json["address"] != interface_ip: | |
5980ad0a DS |
1564 | errormsg = ( |
1565 | "[DUT %s]: PIM interface " | |
1566 | "ip is not correct " | |
1567 | "[FAILED]!! Expected : %s, Found : %s" | |
1568 | % (dut, pim_intf_json["address"], interface_ip) | |
1569 | ) | |
eab72dc8 | 1570 | return errormsg |
1571 | else: | |
5980ad0a DS |
1572 | logger.info( |
1573 | "[DUT %s]: PIM interface " | |
1574 | "ip is correct " | |
1575 | "[Passed]!! Expected : %s, Found : %s" | |
1576 | % (dut, pim_intf_json["address"], interface_ip) | |
1577 | ) | |
eab72dc8 | 1578 | return True |
1579 | else: | |
1580 | for destLink, data in topo["routers"][dut]["links"].items(): | |
1581 | if "type" in data and data["type"] == "loopback": | |
1582 | continue | |
e8cd26fd | 1583 | |
eab72dc8 | 1584 | if "pim" in data and data["pim"] == "enable": |
1585 | pim_interface = data["interface"] | |
1586 | pim_intf_ip = data["ipv4"].split("/")[0] | |
e8cd26fd | 1587 | |
eab72dc8 | 1588 | if pim_interface in show_ip_pim_interface_json: |
5980ad0a | 1589 | pim_intf_json = show_ip_pim_interface_json[pim_interface] |
e8cd26fd | 1590 | |
1591 | # Verifying PIM interface | |
5980ad0a DS |
1592 | if ( |
1593 | pim_intf_json["address"] != pim_intf_ip | |
1594 | and pim_intf_json["state"] != "up" | |
1595 | ): | |
1596 | errormsg = ( | |
1597 | "[DUT %s]: PIM interface: %s " | |
1598 | "PIM interface ip: %s, status check " | |
1599 | "[FAILED]!! Expected : %s, Found : %s" | |
1600 | % ( | |
1601 | dut, | |
1602 | pim_interface, | |
1603 | pim_intf_ip, | |
1604 | pim_interface, | |
1605 | pim_intf_json["state"], | |
1606 | ) | |
1607 | ) | |
e8cd26fd | 1608 | return errormsg |
1609 | ||
5980ad0a DS |
1610 | logger.info( |
1611 | "[DUT %s]: PIM interface: %s, " | |
1612 | "interface ip: %s, status: %s" | |
1613 | " [PASSED]!!", | |
1614 | dut, | |
1615 | pim_interface, | |
1616 | pim_intf_ip, | |
1617 | pim_intf_json["state"], | |
1618 | ) | |
e8cd26fd | 1619 | |
1620 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1621 | return True | |
1622 | ||
1623 | ||
1624 | def clear_ip_pim_interface_traffic(tgen, topo): | |
1625 | """ | |
1626 | Clear ip pim interface traffice by running | |
1627 | "clear ip pim interface traffic" cli | |
1628 | ||
1629 | Parameters | |
1630 | ---------- | |
1631 | * `tgen`: topogen object | |
1632 | Usage | |
1633 | ----- | |
1634 | ||
1635 | result = clear_ip_pim_interface_traffic(tgen, topo) | |
1636 | ||
1637 | Returns | |
1638 | ------- | |
1639 | errormsg(str) or True | |
1640 | """ | |
1641 | ||
1642 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1643 | ||
1644 | for dut in tgen.routers(): | |
1645 | if "pim" not in topo["routers"][dut]: | |
1646 | continue | |
1647 | ||
1648 | rnode = tgen.routers()[dut] | |
1649 | ||
1650 | logger.info("[DUT: %s]: Clearing pim interface traffic", dut) | |
1651 | result = run_frr_cmd(rnode, "clear ip pim interface traffic") | |
1652 | ||
1653 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1654 | ||
1655 | return True | |
1656 | ||
1657 | ||
1658 | def clear_ip_pim_interfaces(tgen, dut): | |
1659 | """ | |
1660 | Clear ip pim interface by running | |
1661 | "clear ip pim interfaces" cli | |
1662 | ||
1663 | Parameters | |
1664 | ---------- | |
1665 | * `tgen`: topogen object | |
1666 | * `dut`: Device Under Test | |
1667 | Usage | |
1668 | ----- | |
1669 | ||
1670 | result = clear_ip_pim_interfaces(tgen, dut) | |
1671 | ||
1672 | Returns | |
1673 | ------- | |
1674 | errormsg(str) or True | |
1675 | """ | |
1676 | ||
1677 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1678 | ||
1679 | nh_before_clear = {} | |
1680 | nh_after_clear = {} | |
1681 | ||
1682 | rnode = tgen.routers()[dut] | |
1683 | ||
1684 | logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut) | |
1685 | # To add uptime initially | |
1686 | sleep(10) | |
1687 | run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) | |
1688 | ||
1689 | for key, value in run_json_before.items(): | |
1690 | if bool(value): | |
1691 | for _key, _value in value.items(): | |
1692 | nh_before_clear[key] = _value["upTime"] | |
1693 | ||
1694 | # Clearing PIM neighbors | |
1695 | logger.info("[DUT: %s]: Clearing pim interfaces", dut) | |
1696 | run_frr_cmd(rnode, "clear ip pim interfaces") | |
1697 | ||
1698 | logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut) | |
1699 | ||
1700 | found = False | |
1701 | ||
1702 | # Waiting for maximum 60 sec | |
1703 | fail_intf = [] | |
1704 | for retry in range(1, 13): | |
1705 | logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut) | |
1706 | sleep(5) | |
1707 | run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True) | |
1708 | found = True | |
1709 | for pim_intf in nh_before_clear.keys(): | |
1710 | if pim_intf not in run_json_after or not run_json_after[pim_intf]: | |
1711 | found = False | |
1712 | fail_intf.append(pim_intf) | |
1713 | ||
1714 | if found is True: | |
1715 | break | |
1716 | else: | |
1717 | errormsg = ( | |
1718 | "[DUT: %s]: pim neighborship is not formed for %s" | |
1719 | "after clear_ip_pim_interfaces %s [FAILED!!]", | |
1720 | dut, | |
1721 | fail_intf, | |
1722 | ) | |
1723 | return errormsg | |
1724 | ||
1725 | for key, value in run_json_after.items(): | |
1726 | if bool(value): | |
1727 | for _key, _value in value.items(): | |
1728 | nh_after_clear[key] = _value["upTime"] | |
1729 | ||
1730 | # Verify uptime for neighbors | |
1731 | for pim_intf in nh_before_clear.keys(): | |
1732 | d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S") | |
1733 | d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S") | |
1734 | if d2 >= d1: | |
1735 | errormsg = ( | |
1736 | "[DUT: %s]: PIM neighborship is not cleared for", | |
1737 | " interface %s [FAILED!!]", | |
1738 | dut, | |
1739 | pim_intf, | |
1740 | ) | |
1741 | ||
1742 | logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]") | |
1743 | ||
1744 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1745 | ||
1746 | return True | |
1747 | ||
1748 | ||
1749 | def clear_ip_igmp_interfaces(tgen, dut): | |
1750 | """ | |
1751 | Clear ip igmp interfaces by running | |
1752 | "clear ip igmp interfaces" cli | |
1753 | ||
1754 | Parameters | |
1755 | ---------- | |
1756 | * `tgen`: topogen object | |
1757 | * `dut`: device under test | |
1758 | ||
1759 | Usage | |
1760 | ----- | |
1761 | dut = "r1" | |
1762 | result = clear_ip_igmp_interfaces(tgen, dut) | |
1763 | Returns | |
1764 | ------- | |
1765 | errormsg(str) or True | |
1766 | """ | |
1767 | ||
1768 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1769 | ||
1770 | group_before_clear = {} | |
1771 | group_after_clear = {} | |
1772 | ||
1773 | rnode = tgen.routers()[dut] | |
1774 | ||
1775 | logger.info("[DUT: %s]: IGMP group uptime before clear" " igmp groups:", dut) | |
1776 | igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
1777 | ||
1778 | total_groups_before_clear = igmp_json["totalGroups"] | |
1779 | ||
1780 | for key, value in igmp_json.items(): | |
1781 | if type(value) is not dict: | |
1782 | continue | |
1783 | ||
1784 | groups = value["groups"] | |
1785 | group = groups[0]["group"] | |
1786 | uptime = groups[0]["uptime"] | |
1787 | group_before_clear[group] = uptime | |
1788 | ||
1789 | logger.info("[DUT: %s]: Clearing ip igmp interfaces", dut) | |
1790 | result = run_frr_cmd(rnode, "clear ip igmp interfaces") | |
1791 | ||
1792 | # Waiting for maximum 60 sec | |
1793 | for retry in range(1, 13): | |
1794 | logger.info( | |
1795 | "[DUT: %s]: Waiting for 5 sec for igmp interfaces" " to come up", dut | |
1796 | ) | |
1797 | sleep(5) | |
1798 | igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True) | |
1799 | ||
1800 | total_groups_after_clear = igmp_json["totalGroups"] | |
1801 | ||
1802 | if total_groups_before_clear == total_groups_after_clear: | |
1803 | break | |
1804 | ||
1805 | for key, value in igmp_json.items(): | |
1806 | if type(value) is not dict: | |
1807 | continue | |
1808 | ||
1809 | groups = value["groups"] | |
1810 | group = groups[0]["group"] | |
1811 | uptime = groups[0]["uptime"] | |
1812 | group_after_clear[group] = uptime | |
1813 | ||
1814 | # Verify uptime for groups | |
1815 | for group in group_before_clear.keys(): | |
1816 | d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S") | |
1817 | d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S") | |
1818 | if d2 >= d1: | |
1819 | errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut) | |
1820 | ||
1821 | logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]") | |
1822 | ||
1823 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1824 | ||
1825 | return True | |
1826 | ||
1827 | ||
ed776e38 | 1828 | @retry(retry_timeout=20) |
3c41ebf8 | 1829 | def clear_ip_mroute_verify(tgen, dut, expected=True): |
e8cd26fd | 1830 | """ |
1831 | Clear ip mroute by running "clear ip mroute" cli and verify | |
1832 | mroutes are up again after mroute clear | |
1833 | ||
1834 | Parameters | |
1835 | ---------- | |
1836 | * `tgen`: topogen object | |
1837 | * `dut`: Device Under Test | |
3c41ebf8 KK |
1838 | * `expected` : expected results from API, by-default True |
1839 | ||
e8cd26fd | 1840 | Usage |
1841 | ----- | |
1842 | ||
1843 | result = clear_ip_mroute_verify(tgen, dut) | |
1844 | ||
1845 | Returns | |
1846 | ------- | |
1847 | errormsg(str) or True | |
1848 | """ | |
1849 | ||
1850 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1851 | ||
1852 | mroute_before_clear = {} | |
1853 | mroute_after_clear = {} | |
1854 | ||
1855 | rnode = tgen.routers()[dut] | |
1856 | ||
e8cd26fd | 1857 | logger.info("[DUT: %s]: IP mroutes uptime before clear", dut) |
1858 | mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
1859 | ||
1860 | for group in mroute_json_1.keys(): | |
1861 | mroute_before_clear[group] = {} | |
1862 | for key in mroute_json_1[group].keys(): | |
1863 | for _key, _value in mroute_json_1[group][key]["oil"].items(): | |
1864 | if _key != "pimreg": | |
1865 | mroute_before_clear[group][key] = _value["upTime"] | |
1866 | ||
1867 | logger.info("[DUT: %s]: Clearing ip mroute", dut) | |
1868 | result = run_frr_cmd(rnode, "clear ip mroute") | |
1869 | ||
1870 | # RFC 3376: 8.2. Query Interval - Default: 125 seconds | |
1871 | # So waiting for maximum 130 sec to get the igmp report | |
1872 | for retry in range(1, 26): | |
1873 | logger.info("[DUT: %s]: Waiting for 2 sec for mroutes" " to come up", dut) | |
1874 | sleep(5) | |
1875 | keys_json1 = mroute_json_1.keys() | |
1876 | mroute_json_2 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
1877 | ||
1878 | if bool(mroute_json_2): | |
1879 | keys_json2 = mroute_json_2.keys() | |
1880 | ||
1881 | for group in mroute_json_2.keys(): | |
1882 | flag = False | |
1883 | for key in mroute_json_2[group].keys(): | |
1884 | if "oil" not in mroute_json_2[group]: | |
1885 | continue | |
1886 | ||
1887 | for _key, _value in mroute_json_2[group][key]["oil"].items(): | |
1888 | if _key != "pimreg" and keys_json1 == keys_json2: | |
1889 | break | |
1890 | flag = True | |
1891 | if flag: | |
1892 | break | |
1893 | else: | |
1894 | continue | |
1895 | ||
1896 | for group in mroute_json_2.keys(): | |
1897 | mroute_after_clear[group] = {} | |
1898 | for key in mroute_json_2[group].keys(): | |
1899 | for _key, _value in mroute_json_2[group][key]["oil"].items(): | |
1900 | if _key != "pimreg": | |
1901 | mroute_after_clear[group][key] = _value["upTime"] | |
1902 | ||
1903 | # Verify uptime for mroute | |
1904 | for group in mroute_before_clear.keys(): | |
1905 | for source in mroute_before_clear[group].keys(): | |
1906 | if set(mroute_before_clear[group]) != set(mroute_after_clear[group]): | |
1907 | errormsg = ( | |
1908 | "[DUT: %s]: mroute (%s, %s) has not come" | |
1909 | " up after mroute clear [FAILED!!]" % (dut, source, group) | |
1910 | ) | |
1911 | return errormsg | |
1912 | ||
1913 | d1 = datetime.datetime.strptime( | |
1914 | mroute_before_clear[group][source], "%H:%M:%S" | |
1915 | ) | |
1916 | d2 = datetime.datetime.strptime( | |
1917 | mroute_after_clear[group][source], "%H:%M:%S" | |
1918 | ) | |
1919 | if d2 >= d1: | |
1920 | errormsg = "[DUT: %s]: IP mroute is not cleared" " [FAILED!!]" % (dut) | |
1921 | ||
1922 | logger.info("[DUT: %s]: IP mroute is cleared [PASSED!!]", dut) | |
1923 | ||
1924 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1925 | ||
1926 | return True | |
1927 | ||
1928 | ||
1929 | def clear_ip_mroute(tgen, dut=None): | |
1930 | """ | |
1931 | Clear ip mroute by running "clear ip mroute" cli | |
1932 | ||
1933 | Parameters | |
1934 | ---------- | |
1935 | * `tgen`: topogen object | |
1936 | * `dut`: device under test, default None | |
1937 | ||
1938 | Usage | |
1939 | ----- | |
1940 | clear_ip_mroute(tgen, dut) | |
1941 | """ | |
1942 | ||
1943 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1944 | ||
1945 | router_list = tgen.routers() | |
1946 | for router, rnode in router_list.items(): | |
1947 | if dut is not None and router != dut: | |
1948 | continue | |
1949 | ||
1950 | logger.debug("[DUT: %s]: Clearing ip mroute", router) | |
1951 | rnode.vtysh_cmd("clear ip mroute") | |
1952 | ||
1953 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
1954 | ||
1955 | ||
1956 | def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None): | |
1957 | """ | |
1958 | Configure interface ip for sender and receiver routers | |
1959 | as per bsr packet | |
1960 | ||
1961 | Parameters | |
1962 | ---------- | |
1963 | * `tgen` : Topogen object | |
1964 | * `topo` : json file data | |
1965 | * `senderRouter` : Sender router | |
1966 | * `receiverRouter` : Receiver router | |
1967 | * `packet` : BSR packet in raw format | |
1968 | ||
1969 | Returns | |
1970 | ------- | |
1971 | True or False | |
1972 | """ | |
1973 | result = False | |
1974 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
1975 | ||
1976 | try: | |
1977 | config_data = [] | |
1978 | ||
1979 | src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"] | |
1980 | dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"] | |
1981 | ||
1982 | for destLink, data in topo["routers"][senderRouter]["links"].items(): | |
1983 | if "type" in data and data["type"] == "loopback": | |
1984 | continue | |
1985 | ||
1986 | if "pim" in data and data["pim"] == "enable": | |
1987 | sender_interface = data["interface"] | |
1988 | sender_interface_ip = data["ipv4"] | |
1989 | ||
1990 | config_data.append("interface {}".format(sender_interface)) | |
1991 | config_data.append("no ip address {}".format(sender_interface_ip)) | |
1992 | config_data.append("ip address {}".format(src_ip)) | |
1993 | ||
1994 | result = create_common_configuration( | |
1995 | tgen, senderRouter, config_data, "interface_config" | |
1996 | ) | |
1997 | if result is not True: | |
1998 | return False | |
1999 | ||
2000 | config_data = [] | |
2001 | links = topo["routers"][destLink]["links"] | |
2002 | pim_neighbor = {key: links[key] for key in [senderRouter]} | |
2003 | ||
2004 | data = pim_neighbor[senderRouter] | |
2005 | if "type" in data and data["type"] == "loopback": | |
2006 | continue | |
2007 | ||
2008 | if "pim" in data and data["pim"] == "enable": | |
2009 | receiver_interface = data["interface"] | |
2010 | receiver_interface_ip = data["ipv4"] | |
2011 | ||
2012 | config_data.append("interface {}".format(receiver_interface)) | |
2013 | config_data.append("no ip address {}".format(receiver_interface_ip)) | |
2014 | config_data.append("ip address {}".format(dest_ip)) | |
2015 | ||
2016 | result = create_common_configuration( | |
2017 | tgen, receiverRouter, config_data, "interface_config" | |
2018 | ) | |
2019 | if result is not True: | |
2020 | return False | |
2021 | ||
2022 | except InvalidCLIError: | |
2023 | # Traceback | |
2024 | errormsg = traceback.format_exc() | |
2025 | logger.error(errormsg) | |
2026 | return errormsg | |
2027 | ||
2028 | logger.debug("Exiting lib API: reconfig_interfaces()") | |
2029 | return result | |
2030 | ||
2031 | ||
2032 | def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping): | |
2033 | """ | |
2034 | Add physical interfaces tp RP for all the RPs | |
2035 | ||
2036 | Parameters | |
2037 | ---------- | |
2038 | * `tgen` : Topogen object | |
2039 | * `topo` : json file data | |
2040 | * `interface` : RP interface | |
2041 | * `rp` : rp for given topology | |
2042 | * `rp_mapping` : dictionary of all groups and RPs | |
2043 | ||
2044 | Returns | |
2045 | ------- | |
2046 | True or False | |
2047 | """ | |
2048 | result = False | |
2049 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2050 | ||
2051 | try: | |
2052 | config_data = [] | |
2053 | ||
2054 | for group, rp_list in rp_mapping.items(): | |
2055 | for _rp in rp_list: | |
2056 | config_data.append("interface {}".format(interface)) | |
2057 | config_data.append("ip address {}".format(_rp)) | |
2058 | config_data.append("ip pim") | |
2059 | ||
4f99894d | 2060 | # Why not config just once, why per group? |
e8cd26fd | 2061 | result = create_common_configuration( |
2062 | tgen, rp, config_data, "interface_config" | |
2063 | ) | |
2064 | if result is not True: | |
2065 | return False | |
2066 | ||
2067 | except InvalidCLIError: | |
2068 | # Traceback | |
2069 | errormsg = traceback.format_exc() | |
2070 | logger.error(errormsg) | |
2071 | return errormsg | |
2072 | ||
8ab46256 | 2073 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) |
e8cd26fd | 2074 | return result |
2075 | ||
2076 | ||
49581587 | 2077 | def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=None): |
e8cd26fd | 2078 | """ |
2079 | Using scapy Raw() method to send BSR raw packet from one FRR | |
2080 | to other | |
2081 | ||
2082 | Parameters: | |
2083 | ----------- | |
2084 | * `tgen` : Topogen object | |
2085 | * `topo` : json file data | |
2086 | * `senderRouter` : Sender router | |
2087 | * `receiverRouter` : Receiver router | |
2088 | * `packet` : BSR packet in raw format | |
e8cd26fd | 2089 | |
2090 | returns: | |
2091 | -------- | |
2092 | errormsg or True | |
2093 | """ | |
2094 | ||
2095 | global CWD | |
2096 | result = "" | |
2097 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2098 | ||
49581587 CH |
2099 | python3_path = tgen.net.get_exec_path(["python3", "python"]) |
2100 | script_path = os.path.join(CWD, "send_bsr_packet.py") | |
2101 | node = tgen.net[senderRouter] | |
e8cd26fd | 2102 | |
2103 | for destLink, data in topo["routers"][senderRouter]["links"].items(): | |
2104 | if "type" in data and data["type"] == "loopback": | |
2105 | continue | |
2106 | ||
2107 | if "pim" in data and data["pim"] == "enable": | |
2108 | sender_interface = data["interface"] | |
2109 | ||
2110 | packet = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["data"] | |
2111 | ||
49581587 CH |
2112 | cmd = [ |
2113 | python3_path, | |
2114 | script_path, | |
2115 | packet, | |
2116 | sender_interface, | |
2117 | "--interval=1", | |
2118 | "--count=1", | |
2119 | ] | |
e8cd26fd | 2120 | logger.info("Scapy cmd: \n %s", cmd) |
49581587 | 2121 | node.cmd_raises(cmd) |
e8cd26fd | 2122 | |
2123 | logger.debug("Exiting lib API: scapy_send_bsr_raw_packet") | |
2124 | return True | |
2125 | ||
2126 | ||
2127 | def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None): | |
2128 | """ | |
2129 | Find which RP is having lowest prioriy and returns rp IP | |
2130 | ||
2131 | Parameters | |
2132 | ---------- | |
2133 | * `tgen`: topogen object | |
2134 | * `dut`: device under test | |
2135 | * `bsr`: BSR address | |
2136 | * 'grp': Group Address | |
2137 | ||
2138 | Usage | |
2139 | ----- | |
2140 | dut = "r1" | |
2141 | result = verify_pim_rp_info(tgen, dut, bsr) | |
2142 | ||
2143 | Returns: | |
2144 | dictionary: group and RP, which has to be installed as per | |
2145 | lowest priority or highest priority | |
2146 | """ | |
2147 | ||
2148 | rp_details = {} | |
2149 | rnode = tgen.routers()[dut] | |
2150 | ||
2151 | logger.info("[DUT: %s]: Fetching rp details from bsrp-info", dut) | |
2152 | bsrp_json = run_frr_cmd(rnode, "show ip pim bsrp-info json", isjson=True) | |
2153 | ||
2154 | if grp not in bsrp_json: | |
2155 | return {} | |
2156 | ||
2157 | for group, rp_data in bsrp_json.items(): | |
2158 | if group == "BSR Address" and bsrp_json["BSR Address"] == bsr: | |
2159 | continue | |
2160 | ||
2161 | if group != grp: | |
2162 | continue | |
2163 | ||
2164 | rp_priority = {} | |
2165 | rp_hash = {} | |
2166 | ||
2167 | for rp, value in rp_data.items(): | |
2168 | if rp == "Pending RP count": | |
2169 | continue | |
2170 | rp_priority[value["Rp Address"]] = value["Rp Priority"] | |
2171 | rp_hash[value["Rp Address"]] = value["Hash Val"] | |
2172 | ||
2173 | priority_dict = dict(zip(rp_priority.values(), rp_priority.keys())) | |
2174 | hash_dict = dict(zip(rp_hash.values(), rp_hash.keys())) | |
2175 | ||
2176 | # RP with lowest priority | |
2177 | if len(priority_dict) != 1: | |
2178 | rp_p, lowest_priority = sorted(rp_priority.items(), key=lambda x: x[1])[0] | |
2179 | rp_details[group] = rp_p | |
2180 | ||
2181 | # RP with highest hash value | |
2182 | if len(priority_dict) == 1: | |
2183 | rp_h, highest_hash = sorted(rp_hash.items(), key=lambda x: x[1])[-1] | |
2184 | rp_details[group] = rp_h | |
2185 | ||
2186 | # RP with highest IP address | |
2187 | if len(priority_dict) == 1 and len(hash_dict) == 1: | |
2188 | rp_details[group] = sorted(rp_priority.keys())[-1] | |
2189 | ||
2190 | return rp_details | |
2191 | ||
2192 | ||
ed776e38 | 2193 | @retry(retry_timeout=12) |
a53c08bc CH |
2194 | def verify_pim_grp_rp_source( |
2195 | tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True | |
2196 | ): | |
e8cd26fd | 2197 | """ |
2198 | Verify pim rp info by running "show ip pim rp-info" cli | |
2199 | ||
2200 | Parameters | |
2201 | ---------- | |
2202 | * `tgen`: topogen object | |
2203 | * `topo`: JSON file handler | |
2204 | * `dut`: device under test | |
2205 | * `grp_addr`: IGMP group address | |
2206 | * 'rp_source': source from which rp installed | |
2207 | * 'rpadd': rp address | |
3c41ebf8 | 2208 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2209 | |
2210 | Usage | |
2211 | ----- | |
2212 | dut = "r1" | |
2213 | group_address = "225.1.1.1" | |
2214 | rp_source = "BSR" | |
2215 | result = verify_pim_rp_and_source(tgen, topo, dut, group_address, rp_source) | |
2216 | ||
2217 | Returns | |
2218 | ------- | |
2219 | errormsg(str) or True | |
2220 | """ | |
2221 | ||
2222 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2223 | ||
2224 | if dut not in tgen.routers(): | |
2225 | return False | |
2226 | ||
2227 | rnode = tgen.routers()[dut] | |
2228 | ||
2229 | logger.info("[DUT: %s]: Verifying ip rp info", dut) | |
2230 | show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True) | |
2231 | ||
2232 | if rpadd != None: | |
2233 | rp_json = show_ip_rp_info_json[rpadd] | |
2234 | if rp_json[0]["group"] == grp_addr: | |
2235 | if rp_json[0]["source"] == rp_source: | |
2236 | logger.info( | |
2237 | "[DUT %s]: Verifying Group and rp_source [PASSED]" | |
2238 | "Found Expected: %s, %s" | |
2239 | % (dut, rp_json[0]["group"], rp_json[0]["source"]) | |
2240 | ) | |
2241 | return True | |
2242 | else: | |
2243 | errormsg = ( | |
2244 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2245 | "Expected (%s, %s) " | |
2246 | "Found (%s, %s)" | |
2247 | % ( | |
2248 | dut, | |
2249 | grp_addr, | |
2250 | rp_source, | |
2251 | rp_json[0]["group"], | |
2252 | rp_json[0]["source"], | |
2253 | ) | |
2254 | ) | |
2255 | return errormsg | |
2256 | errormsg = ( | |
2257 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2258 | "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) | |
2259 | ) | |
2260 | return errormsg | |
2261 | ||
2262 | for rp in show_ip_rp_info_json: | |
2263 | rp_json = show_ip_rp_info_json[rp] | |
2264 | logger.info("%s", rp_json) | |
2265 | if rp_json[0]["group"] == grp_addr: | |
2266 | if rp_json[0]["source"] == rp_source: | |
2267 | logger.info( | |
2268 | "[DUT %s]: Verifying Group and rp_source [PASSED]" | |
2269 | "Found Expected: %s, %s" | |
2270 | % (dut, rp_json[0]["group"], rp_json[0]["source"]) | |
2271 | ) | |
2272 | return True | |
2273 | else: | |
2274 | errormsg = ( | |
2275 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2276 | "Expected (%s, %s) " | |
2277 | "Found (%s, %s)" | |
2278 | % ( | |
2279 | dut, | |
2280 | grp_addr, | |
2281 | rp_source, | |
2282 | rp_json[0]["group"], | |
2283 | rp_json[0]["source"], | |
2284 | ) | |
2285 | ) | |
2286 | return errormsg | |
2287 | ||
2288 | errormsg = ( | |
2289 | "[DUT %s]: Verifying Group and rp_source [FAILED]" | |
2290 | "Expected: %s, %s but not found" % (dut, grp_addr, rp_source) | |
2291 | ) | |
2292 | ||
2293 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2294 | ||
2295 | return errormsg | |
2296 | ||
2297 | ||
ed776e38 | 2298 | @retry(retry_timeout=60) |
3c41ebf8 | 2299 | def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True): |
e8cd26fd | 2300 | """ |
2301 | Verify all PIM interface are up and running, config is verified | |
2302 | using "show ip pim interface" cli | |
2303 | ||
2304 | Parameters | |
2305 | ---------- | |
2306 | * `tgen`: topogen object | |
2307 | * `topo` : json file data | |
2308 | * `dut` : device under test | |
2309 | * 'bsr' : bsr ip to be verified | |
3c41ebf8 | 2310 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2311 | |
2312 | Usage | |
2313 | ----- | |
2314 | result = verify_pim_bsr(tgen, topo, dut, bsr_ip) | |
2315 | ||
2316 | Returns | |
2317 | ------- | |
2318 | errormsg(str) or True | |
2319 | """ | |
2320 | ||
2321 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2322 | ||
2323 | for router in tgen.routers(): | |
2324 | if router != dut: | |
2325 | continue | |
2326 | ||
2327 | logger.info("[DUT: %s]: Verifying PIM bsr status:", dut) | |
2328 | ||
2329 | rnode = tgen.routers()[dut] | |
2330 | pim_bsr_json = rnode.vtysh_cmd("show ip pim bsr json", isjson=True) | |
2331 | ||
2332 | logger.info("show_ip_pim_bsr_json: \n %s", pim_bsr_json) | |
2333 | ||
2334 | # Verifying PIM bsr | |
2335 | if pim_bsr_json["bsr"] != bsr_ip: | |
2336 | errormsg = ( | |
2337 | "[DUT %s]:" | |
2338 | "bsr status: not found" | |
2339 | "[FAILED]!! Expected : %s, Found : %s" | |
2340 | % (dut, bsr_ip, pim_bsr_json["bsr"]) | |
2341 | ) | |
2342 | return errormsg | |
2343 | ||
2344 | logger.info( | |
2345 | "[DUT %s]:" " bsr status: found, Address :%s" " [PASSED]!!", | |
2346 | dut, | |
2347 | pim_bsr_json["bsr"], | |
2348 | ) | |
2349 | ||
2350 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2351 | return True | |
2352 | ||
2353 | ||
ed776e38 | 2354 | @retry(retry_timeout=60) |
a53c08bc CH |
2355 | def verify_ip_pim_upstream_rpf( |
2356 | tgen, topo, dut, interface, group_addresses, rp=None, expected=True | |
2357 | ): | |
e8cd26fd | 2358 | """ |
2359 | Verify IP PIM upstream rpf, config is verified | |
2360 | using "show ip pim neighbor" cli | |
2361 | ||
2362 | Parameters | |
2363 | ---------- | |
2364 | * `tgen`: topogen object | |
2365 | * `topo` : json file data | |
2366 | * `dut` : devuce under test | |
2367 | * `interface` : upstream interface | |
2368 | * `group_addresses` : list of group address for which upstream info | |
2369 | needs to be checked | |
2370 | * `rp` : RP address | |
3c41ebf8 | 2371 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2372 | |
2373 | Usage | |
2374 | ----- | |
2375 | result = verify_ip_pim_upstream_rpf(gen, topo, dut, interface, | |
2376 | group_addresses, rp=None) | |
2377 | ||
2378 | Returns | |
2379 | ------- | |
2380 | errormsg(str) or True | |
2381 | """ | |
2382 | ||
2383 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2384 | ||
2385 | if "pim" in topo["routers"][dut]: | |
2386 | ||
2387 | logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut) | |
2388 | ||
2389 | rnode = tgen.routers()[dut] | |
2390 | show_ip_pim_upstream_rpf_json = rnode.vtysh_cmd( | |
2391 | "show ip pim upstream-rpf json", isjson=True | |
2392 | ) | |
2393 | ||
2394 | logger.info( | |
2395 | "show_ip_pim_upstream_rpf_json: \n %s", show_ip_pim_upstream_rpf_json | |
2396 | ) | |
2397 | ||
2398 | if type(group_addresses) is not list: | |
2399 | group_addresses = [group_addresses] | |
2400 | ||
2401 | for grp_addr in group_addresses: | |
2402 | for destLink, data in topo["routers"][dut]["links"].items(): | |
2403 | if "type" in data and data["type"] == "loopback": | |
2404 | continue | |
2405 | ||
2406 | if "pim" not in topo["routers"][destLink]: | |
2407 | continue | |
2408 | ||
2409 | # Verify RP info | |
2410 | if rp is None: | |
2411 | rp_details = find_rp_details(tgen, topo) | |
2412 | else: | |
660c59ed | 2413 | rp_details = {dut: rp} |
e8cd26fd | 2414 | |
2415 | if dut in rp_details: | |
2416 | pim_nh_intf_ip = topo["routers"][dut]["links"]["lo"]["ipv4"].split( | |
2417 | "/" | |
2418 | )[0] | |
2419 | else: | |
2420 | if destLink not in interface: | |
2421 | continue | |
2422 | ||
2423 | links = topo["routers"][destLink]["links"] | |
2424 | pim_neighbor = {key: links[key] for key in [dut]} | |
2425 | ||
2426 | data = pim_neighbor[dut] | |
2427 | if "pim" in data and data["pim"] == "enable": | |
2428 | pim_nh_intf_ip = data["ipv4"].split("/")[0] | |
2429 | ||
2430 | upstream_rpf_json = show_ip_pim_upstream_rpf_json[grp_addr]["*"] | |
2431 | ||
2432 | # Verifying ip pim upstream rpf | |
2433 | if ( | |
2434 | upstream_rpf_json["rpfInterface"] == interface | |
2435 | and upstream_rpf_json["ribNexthop"] != pim_nh_intf_ip | |
2436 | ): | |
2437 | errormsg = ( | |
2438 | "[DUT %s]: Verifying group: %s, " | |
2439 | "rpf interface: %s, " | |
2440 | " rib Nexthop check [FAILED]!!" | |
2441 | "Expected: %s, Found: %s" | |
2442 | % ( | |
2443 | dut, | |
2444 | grp_addr, | |
2445 | interface, | |
2446 | pim_nh_intf_ip, | |
2447 | upstream_rpf_json["ribNexthop"], | |
2448 | ) | |
2449 | ) | |
2450 | return errormsg | |
2451 | ||
2452 | logger.info( | |
2453 | "[DUT %s]: Verifying group: %s," | |
2454 | " rpf interface: %s, " | |
2455 | " rib Nexthop: %s [PASSED]!!", | |
2456 | dut, | |
2457 | grp_addr, | |
2458 | interface, | |
2459 | pim_nh_intf_ip, | |
2460 | ) | |
2461 | ||
2462 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2463 | return True | |
2464 | ||
2465 | ||
2466 | def enable_disable_pim_unicast_bsm(tgen, router, intf, enable=True): | |
2467 | """ | |
2468 | Helper API to enable or disable pim bsm on interfaces | |
2469 | ||
2470 | Parameters | |
2471 | ---------- | |
2472 | * `tgen` : Topogen object | |
2473 | * `router` : router id to be configured. | |
2474 | * `intf` : Interface to be configured | |
2475 | * `enable` : this flag denotes if config should be enabled or disabled | |
2476 | ||
2477 | Returns | |
2478 | ------- | |
2479 | True or False | |
2480 | """ | |
2481 | result = False | |
2482 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2483 | ||
2484 | try: | |
2485 | config_data = [] | |
2486 | cmd = "interface {}".format(intf) | |
2487 | config_data.append(cmd) | |
2488 | ||
2489 | if enable == True: | |
2490 | config_data.append("ip pim unicast-bsm") | |
2491 | else: | |
2492 | config_data.append("no ip pim unicast-bsm") | |
2493 | ||
2494 | result = create_common_configuration( | |
2495 | tgen, router, config_data, "interface_config", build=False | |
2496 | ) | |
2497 | if result is not True: | |
2498 | return False | |
2499 | ||
2500 | except InvalidCLIError: | |
2501 | # Traceback | |
2502 | errormsg = traceback.format_exc() | |
2503 | logger.error(errormsg) | |
2504 | return errormsg | |
2505 | ||
2506 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2507 | return result | |
2508 | ||
2509 | ||
2510 | def enable_disable_pim_bsm(tgen, router, intf, enable=True): | |
2511 | """ | |
2512 | Helper API to enable or disable pim bsm on interfaces | |
2513 | ||
2514 | Parameters | |
2515 | ---------- | |
2516 | * `tgen` : Topogen object | |
2517 | * `router` : router id to be configured. | |
2518 | * `intf` : Interface to be configured | |
2519 | * `enable` : this flag denotes if config should be enabled or disabled | |
2520 | ||
2521 | Returns | |
2522 | ------- | |
2523 | True or False | |
2524 | """ | |
2525 | result = False | |
2526 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2527 | ||
2528 | try: | |
2529 | config_data = [] | |
2530 | cmd = "interface {}".format(intf) | |
2531 | config_data.append(cmd) | |
2532 | ||
2533 | if enable is True: | |
2534 | config_data.append("ip pim bsm") | |
2535 | else: | |
2536 | config_data.append("no ip pim bsm") | |
2537 | ||
2538 | result = create_common_configuration( | |
2539 | tgen, router, config_data, "interface_config", build=False | |
2540 | ) | |
2541 | if result is not True: | |
2542 | return False | |
2543 | ||
2544 | except InvalidCLIError: | |
2545 | # Traceback | |
2546 | errormsg = traceback.format_exc() | |
2547 | logger.error(errormsg) | |
2548 | return errormsg | |
2549 | ||
2550 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2551 | return result | |
2552 | ||
2553 | ||
ed776e38 | 2554 | @retry(retry_timeout=60) |
a53c08bc CH |
2555 | def verify_ip_pim_join( |
2556 | tgen, topo, dut, interface, group_addresses, src_address=None, expected=True | |
2557 | ): | |
e8cd26fd | 2558 | """ |
2559 | Verify ip pim join by running "show ip pim join" cli | |
2560 | ||
2561 | Parameters | |
2562 | ---------- | |
2563 | * `tgen`: topogen object | |
2564 | * `topo`: JSON file handler | |
2565 | * `dut`: device under test | |
2566 | * `interface`: interface name, from which PIM join would come | |
2567 | * `group_addresses`: IGMP group address | |
2568 | * `src_address`: Source address | |
3c41ebf8 | 2569 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2570 | |
2571 | Usage | |
2572 | ----- | |
2573 | dut = "r1" | |
2574 | interface = "r1-r0-eth0" | |
2575 | group_address = "225.1.1.1" | |
2576 | result = verify_ip_pim_join(tgen, dut, star, group_address, interface) | |
2577 | ||
2578 | Returns | |
2579 | ------- | |
2580 | errormsg(str) or True | |
2581 | """ | |
2582 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2583 | ||
2584 | if dut not in tgen.routers(): | |
2585 | return False | |
2586 | ||
2587 | rnode = tgen.routers()[dut] | |
2588 | ||
2589 | logger.info("[DUT: %s]: Verifying pim join", dut) | |
2590 | show_pim_join_json = run_frr_cmd(rnode, "show ip pim join json", isjson=True) | |
2591 | ||
2592 | if type(group_addresses) is not list: | |
2593 | group_addresses = [group_addresses] | |
2594 | ||
2595 | for grp_addr in group_addresses: | |
2596 | # Verify if IGMP is enabled in DUT | |
2597 | if "igmp" not in topo["routers"][dut]: | |
2598 | pim_join = True | |
2599 | else: | |
2600 | pim_join = False | |
2601 | ||
2602 | interface_json = show_pim_join_json[interface] | |
2603 | ||
2604 | grp_addr = grp_addr.split("/")[0] | |
2605 | for source, data in interface_json[grp_addr].items(): | |
2606 | ||
2607 | # Verify pim join | |
2608 | if pim_join: | |
2609 | if data["group"] == grp_addr and data["channelJoinName"] == "JOIN": | |
2610 | logger.info( | |
2611 | "[DUT %s]: Verifying pim join for group: %s" | |
2612 | "[PASSED]!! Found Expected: (%s)", | |
2613 | dut, | |
2614 | grp_addr, | |
2615 | data["channelJoinName"], | |
2616 | ) | |
2617 | else: | |
2618 | errormsg = ( | |
2619 | "[DUT %s]: Verifying pim join for group: %s" | |
2620 | "[FAILED]!! Expected: (%s) " | |
2621 | "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"]) | |
2622 | ) | |
2623 | return errormsg | |
2624 | ||
2625 | if not pim_join: | |
2626 | if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO": | |
2627 | logger.info( | |
2628 | "[DUT %s]: Verifying pim join for group: %s" | |
2629 | "[PASSED]!! Found Expected: (%s)", | |
2630 | dut, | |
2631 | grp_addr, | |
2632 | data["channelJoinName"], | |
2633 | ) | |
2634 | else: | |
2635 | errormsg = ( | |
2636 | "[DUT %s]: Verifying pim join for group: %s" | |
2637 | "[FAILED]!! Expected: (%s) " | |
2638 | "Found: (%s)" | |
2639 | % (dut, grp_addr, "NOINFO", data["channelJoinName"]) | |
2640 | ) | |
2641 | return errormsg | |
2642 | ||
2643 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2644 | return True | |
2645 | ||
2646 | ||
ed776e38 | 2647 | @retry(retry_timeout=60) |
3c41ebf8 | 2648 | def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True): |
e8cd26fd | 2649 | """ |
2650 | Verify igmp interface details, verifying following configs: | |
2651 | timerQueryInterval | |
2652 | timerQueryResponseIntervalMsec | |
2653 | lastMemberQueryCount | |
2654 | timerLastMemberQueryMsec | |
2655 | ||
2656 | Parameters | |
2657 | ---------- | |
2658 | * `tgen`: topogen object | |
2659 | * `input_dict` : Input dict data, required to verify | |
2660 | timer | |
2661 | * `stats_return`: If user wants API to return statistics | |
3c41ebf8 | 2662 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2663 | |
2664 | Usage | |
2665 | ----- | |
2666 | input_dict ={ | |
2667 | "l1": { | |
2668 | "igmp": { | |
2669 | "interfaces": { | |
2670 | "l1-i1-eth1": { | |
2671 | "igmp": { | |
2672 | "query": { | |
2673 | "query-interval" : 200, | |
2674 | "query-max-response-time" : 100 | |
2675 | }, | |
2676 | "statistics": { | |
2677 | "queryV2" : 2, | |
2678 | "reportV2" : 1 | |
2679 | } | |
2680 | } | |
2681 | } | |
2682 | } | |
2683 | } | |
2684 | } | |
2685 | } | |
2686 | result = verify_igmp_config(tgen, input_dict, stats_return) | |
2687 | ||
2688 | Returns | |
2689 | ------- | |
2690 | errormsg(str) or True | |
2691 | """ | |
2692 | ||
2693 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2694 | ||
2695 | for dut in input_dict.keys(): | |
2696 | rnode = tgen.routers()[dut] | |
2697 | ||
2698 | for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): | |
2699 | ||
2700 | statistics = False | |
2701 | report = False | |
2702 | if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: | |
2703 | statistics = True | |
2704 | cmd = "show ip igmp statistics" | |
2705 | else: | |
2706 | cmd = "show ip igmp" | |
2707 | ||
2708 | logger.info( | |
2709 | "[DUT: %s]: Verifying IGMP interface %s detail:", dut, interface | |
2710 | ) | |
2711 | ||
2712 | if statistics: | |
2713 | if ( | |
2714 | "report" | |
2715 | in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"][ | |
2716 | "statistics" | |
2717 | ] | |
2718 | ): | |
2719 | report = True | |
2720 | ||
2721 | if statistics and report: | |
2722 | show_ip_igmp_intf_json = run_frr_cmd( | |
5aab262b | 2723 | rnode, "{} json".format(cmd), isjson=True |
e8cd26fd | 2724 | ) |
2725 | intf_detail_json = show_ip_igmp_intf_json["global"] | |
2726 | else: | |
2727 | show_ip_igmp_intf_json = run_frr_cmd( | |
2728 | rnode, "{} interface {} json".format(cmd, interface), isjson=True | |
2729 | ) | |
2730 | ||
2731 | if not report: | |
2732 | if interface not in show_ip_igmp_intf_json: | |
2733 | errormsg = ( | |
2734 | "[DUT %s]: IGMP interface: %s " | |
2735 | " is not present in CLI output " | |
2736 | "[FAILED]!! " % (dut, interface) | |
2737 | ) | |
2738 | return errormsg | |
2739 | ||
2740 | else: | |
2741 | intf_detail_json = show_ip_igmp_intf_json[interface] | |
2742 | ||
2743 | if stats_return: | |
2744 | igmp_stats = {} | |
2745 | ||
2746 | if "statistics" in data["igmp"]: | |
2747 | if stats_return: | |
2748 | igmp_stats["statistics"] = {} | |
2749 | for query, value in data["igmp"]["statistics"].items(): | |
2750 | if query == "queryV2": | |
2751 | # Verifying IGMP interface queryV2 statistics | |
2752 | if stats_return: | |
2753 | igmp_stats["statistics"][query] = intf_detail_json[ | |
2754 | "queryV2" | |
2755 | ] | |
2756 | ||
2757 | else: | |
2758 | if intf_detail_json["queryV2"] != value: | |
2759 | errormsg = ( | |
2760 | "[DUT %s]: IGMP interface: %s " | |
2761 | " queryV2 statistics verification " | |
2762 | "[FAILED]!! Expected : %s," | |
2763 | " Found : %s" | |
2764 | % ( | |
2765 | dut, | |
2766 | interface, | |
2767 | value, | |
2768 | intf_detail_json["queryV2"], | |
2769 | ) | |
2770 | ) | |
2771 | return errormsg | |
2772 | ||
2773 | logger.info( | |
2774 | "[DUT %s]: IGMP interface: %s " | |
2775 | "queryV2 statistics is %s", | |
2776 | dut, | |
2777 | interface, | |
2778 | value, | |
2779 | ) | |
2780 | ||
2781 | if query == "reportV2": | |
2782 | # Verifying IGMP interface timerV2 statistics | |
2783 | if stats_return: | |
2784 | igmp_stats["statistics"][query] = intf_detail_json[ | |
2785 | "reportV2" | |
2786 | ] | |
2787 | ||
2788 | else: | |
2789 | if intf_detail_json["reportV2"] <= value: | |
2790 | errormsg = ( | |
2791 | "[DUT %s]: IGMP reportV2 " | |
2792 | "statistics verification " | |
2793 | "[FAILED]!! Expected : %s " | |
2794 | "or more, Found : %s" | |
2795 | % ( | |
2796 | dut, | |
2797 | interface, | |
2798 | value, | |
e8cd26fd | 2799 | ) |
2800 | ) | |
2801 | return errormsg | |
2802 | ||
2803 | logger.info( | |
2804 | "[DUT %s]: IGMP reportV2 " "statistics is %s", | |
2805 | dut, | |
2806 | intf_detail_json["reportV2"], | |
2807 | ) | |
2808 | ||
2809 | if "query" in data["igmp"]: | |
2810 | for query, value in data["igmp"]["query"].items(): | |
2811 | if query == "query-interval": | |
2812 | # Verifying IGMP interface query interval timer | |
2813 | if intf_detail_json["timerQueryInterval"] != value: | |
2814 | errormsg = ( | |
2815 | "[DUT %s]: IGMP interface: %s " | |
2816 | " query-interval verification " | |
2817 | "[FAILED]!! Expected : %s," | |
2818 | " Found : %s" | |
2819 | % ( | |
2820 | dut, | |
2821 | interface, | |
2822 | value, | |
2823 | intf_detail_json["timerQueryInterval"], | |
2824 | ) | |
2825 | ) | |
2826 | return errormsg | |
2827 | ||
2828 | logger.info( | |
2829 | "[DUT %s]: IGMP interface: %s " "query-interval is %s", | |
2830 | dut, | |
2831 | interface, | |
2832 | value, | |
2833 | ) | |
2834 | ||
2835 | if query == "query-max-response-time": | |
2836 | # Verifying IGMP interface query max response timer | |
2837 | if ( | |
2838 | intf_detail_json["timerQueryResponseIntervalMsec"] | |
2839 | != value * 100 | |
2840 | ): | |
2841 | errormsg = ( | |
2842 | "[DUT %s]: IGMP interface: %s " | |
2843 | "query-max-response-time " | |
2844 | "verification [FAILED]!!" | |
2845 | " Expected : %s, Found : %s" | |
2846 | % ( | |
2847 | dut, | |
2848 | interface, | |
2849 | value * 1000, | |
2850 | intf_detail_json["timerQueryResponseIntervalMsec"], | |
2851 | ) | |
2852 | ) | |
2853 | return errormsg | |
2854 | ||
2855 | logger.info( | |
2856 | "[DUT %s]: IGMP interface: %s " | |
2857 | "query-max-response-time is %s ms", | |
2858 | dut, | |
2859 | interface, | |
2860 | value * 100, | |
2861 | ) | |
2862 | ||
2863 | if query == "last-member-query-count": | |
2864 | # Verifying IGMP interface last member query count | |
2865 | if intf_detail_json["lastMemberQueryCount"] != value: | |
2866 | errormsg = ( | |
2867 | "[DUT %s]: IGMP interface: %s " | |
2868 | "last-member-query-count " | |
2869 | "verification [FAILED]!!" | |
2870 | " Expected : %s, Found : %s" | |
2871 | % ( | |
2872 | dut, | |
2873 | interface, | |
2874 | value, | |
2875 | intf_detail_json["lastMemberQueryCount"], | |
2876 | ) | |
2877 | ) | |
2878 | return errormsg | |
2879 | ||
2880 | logger.info( | |
2881 | "[DUT %s]: IGMP interface: %s " | |
2882 | "last-member-query-count is %s ms", | |
2883 | dut, | |
2884 | interface, | |
2885 | value * 1000, | |
2886 | ) | |
2887 | ||
2888 | if query == "last-member-query-interval": | |
2889 | # Verifying IGMP interface last member query interval | |
2890 | if ( | |
2891 | intf_detail_json["timerLastMemberQueryMsec"] | |
2892 | != value * 100 * intf_detail_json["lastMemberQueryCount"] | |
2893 | ): | |
2894 | errormsg = ( | |
2895 | "[DUT %s]: IGMP interface: %s " | |
2896 | "last-member-query-interval " | |
2897 | "verification [FAILED]!!" | |
2898 | " Expected : %s, Found : %s" | |
2899 | % ( | |
2900 | dut, | |
2901 | interface, | |
2902 | value * 1000, | |
2903 | intf_detail_json["timerLastMemberQueryMsec"], | |
2904 | ) | |
2905 | ) | |
2906 | return errormsg | |
2907 | ||
2908 | logger.info( | |
2909 | "[DUT %s]: IGMP interface: %s " | |
2910 | "last-member-query-interval is %s ms", | |
2911 | dut, | |
2912 | interface, | |
2913 | value * intf_detail_json["lastMemberQueryCount"] * 100, | |
2914 | ) | |
2915 | ||
2916 | if "version" in data["igmp"]: | |
2917 | # Verifying IGMP interface state is up | |
2918 | if intf_detail_json["state"] != "up": | |
2919 | errormsg = ( | |
2920 | "[DUT %s]: IGMP interface: %s " | |
2921 | " state: %s verification " | |
2922 | "[FAILED]!!" % (dut, interface, intf_detail_json["state"]) | |
2923 | ) | |
2924 | return errormsg | |
2925 | ||
2926 | logger.info( | |
2927 | "[DUT %s]: IGMP interface: %s " "state: %s", | |
2928 | dut, | |
2929 | interface, | |
2930 | intf_detail_json["state"], | |
2931 | ) | |
2932 | ||
2933 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
2934 | return True if stats_return == False else igmp_stats | |
2935 | ||
2936 | ||
ed776e38 | 2937 | @retry(retry_timeout=60) |
3c41ebf8 | 2938 | def verify_pim_config(tgen, input_dict, expected=True): |
e8cd26fd | 2939 | """ |
2940 | Verify pim interface details, verifying following configs: | |
2941 | drPriority | |
2942 | helloPeriod | |
2943 | helloReceived | |
2944 | helloSend | |
2945 | drAddress | |
2946 | ||
2947 | Parameters | |
2948 | ---------- | |
2949 | * `tgen`: topogen object | |
2950 | * `input_dict` : Input dict data, required to verify | |
2951 | timer | |
3c41ebf8 | 2952 | * `expected` : expected results from API, by-default True |
e8cd26fd | 2953 | |
2954 | Usage | |
2955 | ----- | |
2956 | input_dict ={ | |
2957 | "l1": { | |
2958 | "igmp": { | |
2959 | "interfaces": { | |
2960 | "l1-i1-eth1": { | |
2961 | "pim": { | |
2962 | "drPriority" : 10, | |
2963 | "helloPeriod" : 5 | |
2964 | } | |
2965 | } | |
2966 | } | |
2967 | } | |
2968 | } | |
2969 | } | |
2970 | } | |
2971 | result = verify_pim_config(tgen, input_dict) | |
2972 | ||
2973 | Returns | |
2974 | ------- | |
2975 | errormsg(str) or True | |
2976 | """ | |
2977 | ||
2978 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
2979 | ||
2980 | for dut in input_dict.keys(): | |
2981 | rnode = tgen.routers()[dut] | |
2982 | ||
2983 | for interface, data in input_dict[dut]["pim"]["interfaces"].items(): | |
2984 | ||
2985 | logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface) | |
2986 | ||
2987 | show_ip_igmp_intf_json = run_frr_cmd( | |
2988 | rnode, "show ip pim interface {} json".format(interface), isjson=True | |
2989 | ) | |
2990 | ||
2991 | if interface not in show_ip_igmp_intf_json: | |
2992 | errormsg = ( | |
2993 | "[DUT %s]: PIM interface: %s " | |
2994 | " is not present in CLI output " | |
2995 | "[FAILED]!! " % (dut, interface) | |
2996 | ) | |
2997 | return errormsg | |
2998 | ||
2999 | intf_detail_json = show_ip_igmp_intf_json[interface] | |
3000 | ||
3001 | for config, value in data.items(): | |
3002 | if config == "helloPeriod": | |
3003 | # Verifying PIM interface helloPeriod | |
3004 | if intf_detail_json["helloPeriod"] != value: | |
3005 | errormsg = ( | |
3006 | "[DUT %s]: PIM interface: %s " | |
3007 | " helloPeriod verification " | |
3008 | "[FAILED]!! Expected : %s," | |
3009 | " Found : %s" | |
3010 | % (dut, interface, value, intf_detail_json["helloPeriod"]) | |
3011 | ) | |
3012 | return errormsg | |
3013 | ||
3014 | logger.info( | |
3015 | "[DUT %s]: PIM interface: %s " "helloPeriod is %s", | |
3016 | dut, | |
3017 | interface, | |
3018 | value, | |
3019 | ) | |
3020 | ||
3021 | if config == "drPriority": | |
3022 | # Verifying PIM interface drPriority | |
3023 | if intf_detail_json["drPriority"] != value: | |
3024 | errormsg = ( | |
3025 | "[DUT %s]: PIM interface: %s " | |
3026 | " drPriority verification " | |
3027 | "[FAILED]!! Expected : %s," | |
3028 | " Found : %s" | |
3029 | % (dut, interface, value, intf_detail_json["drPriority"]) | |
3030 | ) | |
3031 | return errormsg | |
3032 | ||
3033 | logger.info( | |
3034 | "[DUT %s]: PIM interface: %s " "drPriority is %s", | |
3035 | dut, | |
3036 | interface, | |
3037 | value, | |
3038 | ) | |
3039 | ||
3040 | if config == "drAddress": | |
3041 | # Verifying PIM interface drAddress | |
3042 | if intf_detail_json["drAddress"] != value: | |
3043 | errormsg = ( | |
3044 | "[DUT %s]: PIM interface: %s " | |
3045 | " drAddress verification " | |
3046 | "[FAILED]!! Expected : %s," | |
3047 | " Found : %s" | |
3048 | % (dut, interface, value, intf_detail_json["drAddress"]) | |
3049 | ) | |
3050 | return errormsg | |
3051 | ||
3052 | logger.info( | |
3053 | "[DUT %s]: PIM interface: %s " "drAddress is %s", | |
3054 | dut, | |
3055 | interface, | |
3056 | value, | |
3057 | ) | |
3058 | ||
3059 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3060 | return True | |
3061 | ||
3062 | ||
ed776e38 | 3063 | @retry(retry_timeout=40) |
3c41ebf8 | 3064 | def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True): |
e8cd26fd | 3065 | """ |
3066 | Verify multicast traffic by running | |
3067 | "show multicast traffic count json" cli | |
3068 | ||
3069 | Parameters | |
3070 | ---------- | |
3071 | * `tgen`: topogen object | |
3072 | * `input_dict(dict)`: defines DUT, what and for which interfaces | |
3073 | traffic needs to be verified | |
3074 | * `return_traffic`: returns traffic stats | |
3c41ebf8 KK |
3075 | * `expected` : expected results from API, by-default True |
3076 | ||
e8cd26fd | 3077 | Usage |
3078 | ----- | |
3079 | input_dict = { | |
3080 | "r1": { | |
3081 | "traffic_received": ["r1-r0-eth0"], | |
3082 | "traffic_sent": ["r1-r0-eth0"] | |
3083 | } | |
3084 | } | |
3085 | ||
3086 | result = verify_multicast_traffic(tgen, input_dict) | |
3087 | ||
3088 | Returns | |
3089 | ------- | |
3090 | errormsg(str) or True | |
3091 | """ | |
3092 | ||
3093 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3094 | ||
3095 | traffic_dict = {} | |
3096 | for dut in input_dict.keys(): | |
3097 | if dut not in tgen.routers(): | |
3098 | continue | |
3099 | ||
3100 | rnode = tgen.routers()[dut] | |
3101 | ||
3102 | logger.info("[DUT: %s]: Verifying multicast " "traffic", dut) | |
3103 | ||
3104 | show_multicast_traffic_json = run_frr_cmd( | |
3105 | rnode, "show ip multicast count json", isjson=True | |
3106 | ) | |
3107 | ||
3108 | for traffic_type, interfaces in input_dict[dut].items(): | |
3109 | traffic_dict[traffic_type] = {} | |
3110 | if traffic_type == "traffic_received": | |
3111 | for interface in interfaces: | |
3112 | traffic_dict[traffic_type][interface] = {} | |
3113 | interface_json = show_multicast_traffic_json[interface] | |
3114 | ||
3115 | if interface_json["pktsIn"] == 0 and interface_json["bytesIn"] == 0: | |
3116 | errormsg = ( | |
3117 | "[DUT %s]: Multicast traffic is " | |
3118 | "not received on interface %s " | |
3119 | "PktsIn: %s, BytesIn: %s " | |
3120 | "[FAILED]!!" | |
3121 | % ( | |
3122 | dut, | |
3123 | interface, | |
3124 | interface_json["pktsIn"], | |
3125 | interface_json["bytesIn"], | |
3126 | ) | |
3127 | ) | |
3128 | return errormsg | |
3129 | ||
3130 | elif ( | |
3131 | interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0 | |
3132 | ): | |
3133 | ||
3134 | traffic_dict[traffic_type][interface][ | |
3135 | "pktsIn" | |
3136 | ] = interface_json["pktsIn"] | |
3137 | traffic_dict[traffic_type][interface][ | |
3138 | "bytesIn" | |
3139 | ] = interface_json["bytesIn"] | |
3140 | ||
3141 | logger.info( | |
3142 | "[DUT %s]: Multicast traffic is " | |
3143 | "received on interface %s " | |
3144 | "PktsIn: %s, BytesIn: %s " | |
3145 | "[PASSED]!!" | |
3146 | % ( | |
3147 | dut, | |
3148 | interface, | |
3149 | interface_json["pktsIn"], | |
3150 | interface_json["bytesIn"], | |
3151 | ) | |
3152 | ) | |
3153 | ||
3154 | else: | |
3155 | errormsg = ( | |
3156 | "[DUT %s]: Multicast traffic interface %s:" | |
3157 | " Miss-match in " | |
3158 | "PktsIn: %s, BytesIn: %s" | |
3159 | "[FAILED]!!" | |
3160 | % ( | |
3161 | dut, | |
3162 | interface, | |
3163 | interface_json["pktsIn"], | |
3164 | interface_json["bytesIn"], | |
3165 | ) | |
3166 | ) | |
3167 | return errormsg | |
3168 | ||
3169 | if traffic_type == "traffic_sent": | |
3170 | traffic_dict[traffic_type] = {} | |
3171 | for interface in interfaces: | |
3172 | traffic_dict[traffic_type][interface] = {} | |
3173 | interface_json = show_multicast_traffic_json[interface] | |
3174 | ||
3175 | if ( | |
3176 | interface_json["pktsOut"] == 0 | |
3177 | and interface_json["bytesOut"] == 0 | |
3178 | ): | |
3179 | errormsg = ( | |
3180 | "[DUT %s]: Multicast traffic is " | |
3181 | "not received on interface %s " | |
3182 | "PktsIn: %s, BytesIn: %s" | |
3183 | "[FAILED]!!" | |
3184 | % ( | |
3185 | dut, | |
3186 | interface, | |
3187 | interface_json["pktsOut"], | |
3188 | interface_json["bytesOut"], | |
3189 | ) | |
3190 | ) | |
3191 | return errormsg | |
3192 | ||
3193 | elif ( | |
3194 | interface_json["pktsOut"] != 0 | |
3195 | and interface_json["bytesOut"] != 0 | |
3196 | ): | |
3197 | ||
3198 | traffic_dict[traffic_type][interface][ | |
3199 | "pktsOut" | |
3200 | ] = interface_json["pktsOut"] | |
3201 | traffic_dict[traffic_type][interface][ | |
3202 | "bytesOut" | |
3203 | ] = interface_json["bytesOut"] | |
3204 | ||
3205 | logger.info( | |
3206 | "[DUT %s]: Multicast traffic is " | |
3207 | "received on interface %s " | |
3208 | "PktsOut: %s, BytesOut: %s " | |
3209 | "[PASSED]!!" | |
3210 | % ( | |
3211 | dut, | |
3212 | interface, | |
3213 | interface_json["pktsOut"], | |
3214 | interface_json["bytesOut"], | |
3215 | ) | |
3216 | ) | |
3217 | else: | |
3218 | errormsg = ( | |
3219 | "[DUT %s]: Multicast traffic interface %s:" | |
3220 | " Miss-match in " | |
3221 | "PktsOut: %s, BytesOut: %s " | |
3222 | "[FAILED]!!" | |
3223 | % ( | |
3224 | dut, | |
3225 | interface, | |
3226 | interface_json["pktsOut"], | |
3227 | interface_json["bytesOut"], | |
3228 | ) | |
3229 | ) | |
3230 | return errormsg | |
3231 | ||
3232 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3233 | return True if return_traffic == False else traffic_dict | |
3234 | ||
3235 | ||
3236 | def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses): | |
3237 | """ | |
3238 | Verify upstream inbound interface is updated correctly | |
3239 | by running "show ip pim upstream" cli | |
3240 | ||
3241 | Parameters | |
3242 | ---------- | |
3243 | * `tgen`: topogen object | |
3244 | * `dut`: device under test | |
3245 | * `iif`: inbound interface | |
3246 | * `src_address`: source address | |
3247 | * `group_addresses`: IGMP group address | |
3248 | ||
3249 | Usage | |
3250 | ----- | |
3251 | dut = "r1" | |
3252 | iif = "r1-r0-eth0" | |
3253 | src_address = "*" | |
3254 | group_address = "225.1.1.1" | |
3255 | result = get_refCount_for_mroute(tgen, dut, iif, src_address, | |
3256 | group_address) | |
3257 | ||
3258 | Returns | |
3259 | ------- | |
3260 | refCount(int) | |
3261 | """ | |
3262 | ||
3263 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3264 | ||
3265 | refCount = 0 | |
3266 | if dut not in tgen.routers(): | |
3267 | return False | |
3268 | ||
3269 | rnode = tgen.routers()[dut] | |
3270 | ||
3271 | logger.info("[DUT: %s]: Verifying refCount for mroutes: ", dut) | |
3272 | show_ip_pim_upstream_json = run_frr_cmd( | |
3273 | rnode, "show ip pim upstream json", isjson=True | |
3274 | ) | |
3275 | ||
3276 | if type(group_addresses) is not list: | |
3277 | group_addresses = [group_addresses] | |
3278 | ||
3279 | for grp_addr in group_addresses: | |
3280 | # Verify group address | |
3281 | if grp_addr not in show_ip_pim_upstream_json: | |
3282 | errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % ( | |
3283 | dut, | |
3284 | grp_addr, | |
3285 | ) | |
3286 | return errormsg | |
3287 | group_addr_json = show_ip_pim_upstream_json[grp_addr] | |
3288 | ||
3289 | # Verify source address | |
3290 | if src_address not in group_addr_json: | |
3291 | errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % ( | |
3292 | dut, | |
3293 | src_address, | |
3294 | grp_addr, | |
3295 | ) | |
3296 | return errormsg | |
3297 | ||
3298 | # Verify Inbound Interface | |
3299 | if group_addr_json[src_address]["inboundInterface"] == iif: | |
3300 | refCount = group_addr_json[src_address]["refCount"] | |
3301 | ||
3302 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3303 | return refCount | |
3304 | ||
3305 | ||
ed776e38 | 3306 | @retry(retry_timeout=40) |
a53c08bc CH |
3307 | def verify_multicast_flag_state( |
3308 | tgen, dut, src_address, group_addresses, flag, expected=True | |
3309 | ): | |
e8cd26fd | 3310 | """ |
3311 | Verify flag state for mroutes and make sure (*, G)/(S, G) are having | |
3312 | coorect flags by running "show ip mroute" cli | |
3313 | ||
3314 | Parameters | |
3315 | ---------- | |
3316 | * `tgen`: topogen object | |
3317 | * `dut`: device under test | |
3318 | * `src_address`: source address | |
3319 | * `group_addresses`: IGMP group address | |
3320 | * `flag`: flag state, needs to be verified | |
3c41ebf8 | 3321 | * `expected` : expected results from API, by-default True |
e8cd26fd | 3322 | |
3323 | Usage | |
3324 | ----- | |
3325 | dut = "r1" | |
3326 | flag = "SC" | |
3327 | group_address = "225.1.1.1" | |
3328 | result = verify_multicast_flag_state(tgen, dut, src_address, | |
3329 | group_address, flag) | |
3330 | ||
3331 | Returns | |
3332 | ------- | |
3333 | errormsg(str) or True | |
3334 | """ | |
3335 | ||
3336 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3337 | ||
3338 | if dut not in tgen.routers(): | |
3339 | return False | |
3340 | ||
3341 | rnode = tgen.routers()[dut] | |
3342 | ||
3343 | logger.info("[DUT: %s]: Verifying flag state for mroutes", dut) | |
3344 | show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True) | |
3345 | ||
3346 | if bool(show_ip_mroute_json) == False: | |
3347 | error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut) | |
3348 | return error_msg | |
3349 | ||
3350 | if type(group_addresses) is not list: | |
3351 | group_addresses = [group_addresses] | |
3352 | ||
3353 | for grp_addr in group_addresses: | |
3354 | if grp_addr not in show_ip_mroute_json: | |
3355 | errormsg = ( | |
3356 | "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! ", | |
3357 | dut, | |
3358 | src_address, | |
3359 | grp_addr, | |
3360 | ) | |
3361 | return errormsg | |
3362 | else: | |
3363 | group_addr_json = show_ip_mroute_json[grp_addr] | |
3364 | ||
3365 | if src_address not in group_addr_json: | |
3366 | errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % ( | |
3367 | dut, | |
3368 | src_address, | |
3369 | grp_addr, | |
3370 | ) | |
3371 | return errormsg | |
3372 | else: | |
3373 | mroutes = group_addr_json[src_address] | |
3374 | ||
3375 | if mroutes["installed"] != 0: | |
3376 | logger.info( | |
3377 | "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr | |
3378 | ) | |
3379 | ||
3380 | if mroutes["flags"] != flag: | |
3381 | errormsg = ( | |
3382 | "[DUT %s]: Verifying flag for (%s, %s) " | |
3383 | "mroute [FAILED]!! " | |
3384 | "Expected: %s Found: %s" | |
3385 | % (dut, src_address, grp_addr, flag, mroutes["flags"]) | |
3386 | ) | |
3387 | return errormsg | |
3388 | ||
3389 | logger.info( | |
3390 | "[DUT %s]: Verifying flag for (%s, %s)" | |
3391 | " mroute, [PASSED]!! " | |
3392 | "Found Expected: %s", | |
3393 | dut, | |
3394 | src_address, | |
3395 | grp_addr, | |
3396 | mroutes["flags"], | |
3397 | ) | |
3398 | ||
3399 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3400 | return True | |
eab72dc8 | 3401 | |
3402 | ||
ed776e38 | 3403 | @retry(retry_timeout=40) |
3c41ebf8 | 3404 | def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip, expected=True): |
eab72dc8 | 3405 | """ |
3406 | Verify all IGMP interface are up and running, config is verified | |
3407 | using "show ip igmp interface" cli | |
3408 | ||
3409 | Parameters | |
3410 | ---------- | |
3411 | * `tgen`: topogen object | |
3412 | * `topo` : json file data | |
3413 | * `dut` : device under test | |
3414 | * `igmp_iface` : interface name | |
3415 | * `interface_ip` : interface ip address | |
3c41ebf8 | 3416 | * `expected` : expected results from API, by-default True |
eab72dc8 | 3417 | |
3418 | Usage | |
3419 | ----- | |
3420 | result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip) | |
3421 | ||
3422 | Returns | |
3423 | ------- | |
3424 | errormsg(str) or True | |
3425 | """ | |
3426 | ||
3427 | logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) | |
3428 | ||
3429 | for router in tgen.routers(): | |
3430 | if router != dut: | |
3431 | continue | |
3432 | ||
5980ad0a | 3433 | logger.info("[DUT: %s]: Verifying PIM interface status:", dut) |
eab72dc8 | 3434 | |
3435 | rnode = tgen.routers()[dut] | |
5980ad0a DS |
3436 | show_ip_igmp_interface_json = run_frr_cmd( |
3437 | rnode, "show ip igmp interface json", isjson=True | |
3438 | ) | |
eab72dc8 | 3439 | |
5980ad0a | 3440 | if igmp_iface in show_ip_igmp_interface_json: |
eab72dc8 | 3441 | igmp_intf_json = show_ip_igmp_interface_json[igmp_iface] |
3442 | # Verifying igmp interface | |
5980ad0a DS |
3443 | if igmp_intf_json["address"] != interface_ip: |
3444 | errormsg = ( | |
3445 | "[DUT %s]: igmp interface ip is not correct " | |
3446 | "[FAILED]!! Expected : %s, Found : %s" | |
3447 | % (dut, igmp_intf_json["address"], interface_ip) | |
3448 | ) | |
eab72dc8 | 3449 | return errormsg |
3450 | ||
5980ad0a DS |
3451 | logger.info( |
3452 | "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!", | |
3453 | dut, | |
3454 | igmp_iface, | |
3455 | interface_ip, | |
3456 | ) | |
eab72dc8 | 3457 | else: |
5980ad0a DS |
3458 | errormsg = ( |
3459 | "[DUT %s]: igmp interface: %s " | |
3460 | "igmp interface ip: %s, is not present " | |
3461 | % (dut, igmp_iface, interface_ip) | |
3462 | ) | |
eab72dc8 | 3463 | return errormsg |
3464 | ||
3465 | logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) | |
3466 | return True | |
a5124c49 CH |
3467 | |
3468 | ||
a53c08bc | 3469 | class McastTesterHelper(HostApplicationHelper): |
a5124c49 CH |
3470 | def __init__(self, tgen=None): |
3471 | self.script_path = os.path.join(CWD, "mcast-tester.py") | |
3472 | self.host_conn = {} | |
3473 | self.listen_sock = None | |
3474 | ||
3475 | # # Get a temporary file for socket path | |
3476 | # (fd, sock_path) = tempfile.mkstemp("-mct.sock", "tmp" + str(os.getpid())) | |
3477 | # os.close(fd) | |
3478 | # os.remove(sock_path) | |
3479 | # self.app_sock_path = sock_path | |
3480 | ||
3481 | # # Listen on unix socket | |
3482 | # logger.debug("%s: listening on socket %s", self, self.app_sock_path) | |
3483 | # self.listen_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) | |
3484 | # self.listen_sock.settimeout(10) | |
3485 | # self.listen_sock.bind(self.app_sock_path) | |
3486 | # self.listen_sock.listen(10) | |
3487 | ||
3488 | python3_path = get_exec_path(["python3", "python"]) | |
3489 | super(McastTesterHelper, self).__init__( | |
3490 | tgen, | |
3491 | # [python3_path, self.script_path, self.app_sock_path] | |
a53c08bc | 3492 | [python3_path, self.script_path], |
a5124c49 CH |
3493 | ) |
3494 | ||
a53c08bc | 3495 | def __str__(self): |
a5124c49 CH |
3496 | return "McastTesterHelper({})".format(self.script_path) |
3497 | ||
3498 | def run_join(self, host, join_addrs, join_towards=None, join_intf=None): | |
3499 | """ | |
3500 | Join a UDP multicast group. | |
3501 | ||
3502 | One of join_towards or join_intf MUST be set. | |
3503 | ||
3504 | Parameters: | |
3505 | ----------- | |
3506 | * `host`: host from where IGMP join would be sent | |
3507 | * `join_addrs`: multicast address (or addresses) to join to | |
3508 | * `join_intf`: the interface to bind the join[s] to | |
3509 | * `join_towards`: router whos interface to bind the join[s] to | |
3510 | """ | |
3511 | if not isinstance(join_addrs, list) and not isinstance(join_addrs, tuple): | |
3512 | join_addrs = [join_addrs] | |
3513 | ||
3514 | if join_towards: | |
a53c08bc CH |
3515 | join_intf = frr_unicode( |
3516 | self.tgen.json_topo["routers"][host]["links"][join_towards]["interface"] | |
3517 | ) | |
a5124c49 CH |
3518 | else: |
3519 | assert join_intf | |
3520 | ||
3521 | for join in join_addrs: | |
3522 | self.run(host, [join, join_intf]) | |
3523 | ||
3524 | return True | |
3525 | ||
3526 | def run_traffic(self, host, send_to_addrs, bind_towards=None, bind_intf=None): | |
3527 | """ | |
3528 | Send UDP multicast traffic. | |
3529 | ||
3530 | One of bind_towards or bind_intf MUST be set. | |
3531 | ||
3532 | Parameters: | |
3533 | ----------- | |
3534 | * `host`: host to send traffic from | |
3535 | * `send_to_addrs`: multicast address (or addresses) to send traffic to | |
3536 | * `bind_towards`: Router who's interface the source ip address is got from | |
3537 | """ | |
3538 | if bind_towards: | |
a53c08bc CH |
3539 | bind_intf = frr_unicode( |
3540 | self.tgen.json_topo["routers"][host]["links"][bind_towards]["interface"] | |
3541 | ) | |
a5124c49 CH |
3542 | else: |
3543 | assert bind_intf | |
3544 | ||
3545 | if not isinstance(send_to_addrs, list) and not isinstance(send_to_addrs, tuple): | |
3546 | send_to_addrs = [send_to_addrs] | |
3547 | ||
3548 | for send_to in send_to_addrs: | |
3549 | self.run(host, ["--send=0.7", send_to, bind_intf]) | |
3550 | ||
3551 | return True | |
3552 | ||
3553 | # def cleanup(self): | |
3554 | # super(McastTesterHelper, self).cleanup() | |
3555 | ||
3556 | # if not self.listen_sock: | |
3557 | # return | |
3558 | ||
3559 | # logger.debug("%s: closing listen socket %s", self, self.app_sock_path) | |
3560 | # self.listen_sock.close() | |
3561 | # self.listen_sock = None | |
3562 | ||
3563 | # if os.path.exists(self.app_sock_path): | |
3564 | # os.remove(self.app_sock_path) | |
3565 | ||
3566 | # def started_proc(self, host, p): | |
3567 | # logger.debug("%s: %s: accepting on socket %s", self, host, self.app_sock_path) | |
3568 | # try: | |
3569 | # conn = self.listen_sock.accept() | |
3570 | # return conn | |
3571 | # except Exception as error: | |
3572 | # logger.error("%s: %s: accept on socket failed: %s", self, host, error) | |
3573 | # if p.poll() is not None: | |
3574 | # logger.error("%s: %s: helper app quit: %s", self, host, comm_error(p)) | |
3575 | # raise | |
3576 | ||
3577 | # def stopping_proc(self, host, p, conn): | |
3578 | # logger.debug("%s: %s: closing socket %s", self, host, conn) | |
3579 | # conn[0].close() |