]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/lib/bgp.py
Merge pull request #5517 from mjstapp/fix_evpn_state
[mirror_frr.git] / tests / topotests / lib / bgp.py
1 #
2 # Copyright (c) 2019 by VMware, Inc. ("VMware")
3 # Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
4 # ("NetDEF") in this file.
5 #
6 # Permission to use, copy, modify, and/or distribute this software
7 # for any purpose with or without fee is hereby granted, provided
8 # that the above copyright notice and this permission notice appear
9 # in all copies.
10 #
11 # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
12 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
14 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
15 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
18 # OF THIS SOFTWARE.
19 #
20
21 from copy import deepcopy
22 from time import sleep
23 import traceback
24 import ipaddr
25 from lib import topotest
26
27 from lib.topolog import logger
28
29 # Import common_config to use commomnly used APIs
30 from lib.common_config import (create_common_configuration,
31 InvalidCLIError,
32 load_config_to_router,
33 check_address_types,
34 generate_ips,
35 find_interface_with_greater_ip,
36 run_frr_cmd, retry)
37
38 BGP_CONVERGENCE_TIMEOUT = 10
39
40
41 def create_router_bgp(tgen, topo, input_dict=None, build=False):
42 """
43 API to configure bgp on router
44
45 Parameters
46 ----------
47 * `tgen` : Topogen object
48 * `topo` : json file data
49 * `input_dict` : Input dict data, required when configuring from testcase
50 * `build` : Only for initial setup phase this is set as True.
51
52 Usage
53 -----
54 input_dict = {
55 "r1": {
56 "bgp": {
57 "local_as": "200",
58 "router_id": "22.22.22.22",
59 "address_family": {
60 "ipv4": {
61 "unicast": {
62 "redistribute": [
63 {"redist_type": "static"},
64 {"redist_type": "connected"}
65 ],
66 "advertise_networks": [
67 {
68 "network": "20.0.0.0/32",
69 "no_of_network": 10
70 },
71 {
72 "network": "30.0.0.0/32",
73 "no_of_network": 10
74 }
75 ],
76 "neighbor": {
77 "r3": {
78 "keepalivetimer": 60,
79 "holddowntimer": 180,
80 "dest_link": {
81 "r4": {
82 "prefix_lists": [
83 {
84 "name": "pf_list_1",
85 "direction": "in"
86 }
87 ],
88 "route_maps": [
89 {"name": "RMAP_MED_R3",
90 "direction": "in"}
91 ],
92 "next_hop_self": True
93 }
94 }
95 }
96 }
97 }
98 }
99 }
100 }
101 }
102 }
103
104
105 Returns
106 -------
107 True or False
108 """
109 logger.debug("Entering lib API: create_router_bgp()")
110 result = False
111 if not input_dict:
112 input_dict = deepcopy(topo)
113 else:
114 topo = topo["routers"]
115 input_dict = deepcopy(input_dict)
116
117 for router in input_dict.keys():
118 if "bgp" not in input_dict[router]:
119 logger.debug("Router %s: 'bgp' not present in input_dict", router)
120 continue
121
122 data_all_bgp = __create_bgp_global(tgen, input_dict, router, build)
123 if data_all_bgp:
124 bgp_data = input_dict[router]["bgp"]
125
126 bgp_addr_data = bgp_data.setdefault("address_family", {})
127
128 if not bgp_addr_data:
129 logger.debug("Router %s: 'address_family' not present in "
130 "input_dict for BGP", router)
131 else:
132
133 ipv4_data = bgp_addr_data.setdefault("ipv4", {})
134 ipv6_data = bgp_addr_data.setdefault("ipv6", {})
135
136 neigh_unicast = True if ipv4_data.setdefault("unicast", {}) \
137 or ipv6_data.setdefault("unicast", {}) else False
138
139 if neigh_unicast:
140 data_all_bgp = __create_bgp_unicast_neighbor(
141 tgen, topo, input_dict, router,
142 config_data=data_all_bgp)
143
144 try:
145 result = create_common_configuration(tgen, router, data_all_bgp,
146 "bgp", build)
147 except InvalidCLIError:
148 # Traceback
149 errormsg = traceback.format_exc()
150 logger.error(errormsg)
151 return errormsg
152
153 logger.debug("Exiting lib API: create_router_bgp()")
154 return result
155
156
157 def __create_bgp_global(tgen, input_dict, router, build=False):
158 """
159 Helper API to create bgp global configuration.
160
161 Parameters
162 ----------
163 * `tgen` : Topogen object
164 * `input_dict` : Input dict data, required when configuring from testcase
165 * `router` : router id to be configured.
166 * `build` : Only for initial setup phase this is set as True.
167
168 Returns
169 -------
170 True or False
171 """
172
173 logger.debug("Entering lib API: __create_bgp_global()")
174
175 bgp_data = input_dict[router]["bgp"]
176 del_bgp_action = bgp_data.setdefault("delete", False)
177 if del_bgp_action:
178 config_data = ["no router bgp"]
179
180 return config_data
181
182 config_data = []
183
184 if "local_as" not in bgp_data and build:
185 logger.error("Router %s: 'local_as' not present in input_dict"
186 "for BGP", router)
187 return False
188
189 local_as = bgp_data.setdefault("local_as", "")
190 cmd = "router bgp {}".format(local_as)
191 vrf_id = bgp_data.setdefault("vrf", None)
192 if vrf_id:
193 cmd = "{} vrf {}".format(cmd, vrf_id)
194
195 config_data.append(cmd)
196
197 router_id = bgp_data.setdefault("router_id", None)
198 del_router_id = bgp_data.setdefault("del_router_id", False)
199 if del_router_id:
200 config_data.append("no bgp router-id")
201 if router_id:
202 config_data.append("bgp router-id {}".format(
203 router_id))
204
205 return config_data
206
207
208 def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router,
209 config_data=None):
210 """
211 Helper API to create configuration for address-family unicast
212
213 Parameters
214 ----------
215 * `tgen` : Topogen object
216 * `topo` : json file data
217 * `input_dict` : Input dict data, required when configuring from testcase
218 * `router` : router id to be configured.
219 * `build` : Only for initial setup phase this is set as True.
220 """
221
222 logger.debug("Entering lib API: __create_bgp_unicast_neighbor()")
223
224 add_neigh = True
225 if "router bgp" in config_data:
226 add_neigh = False
227 bgp_data = input_dict[router]["bgp"]["address_family"]
228
229 for addr_type, addr_dict in bgp_data.iteritems():
230 if not addr_dict:
231 continue
232
233 if not check_address_types(addr_type):
234 continue
235
236 addr_data = addr_dict["unicast"]
237 if addr_data:
238 config_data.append("address-family {} unicast".format(
239 addr_type
240 ))
241 advertise_network = addr_data.setdefault("advertise_networks",
242 [])
243 for advertise_network_dict in advertise_network:
244 network = advertise_network_dict["network"]
245 if type(network) is not list:
246 network = [network]
247
248 if "no_of_network" in advertise_network_dict:
249 no_of_network = advertise_network_dict["no_of_network"]
250 else:
251 no_of_network = 1
252
253 del_action = advertise_network_dict.setdefault("delete",
254 False)
255
256 # Generating IPs for verification
257 prefix = str(
258 ipaddr.IPNetwork(unicode(network[0])).prefixlen)
259 network_list = generate_ips(network, no_of_network)
260 for ip in network_list:
261 ip = str(ipaddr.IPNetwork(unicode(ip)).network)
262
263 cmd = "network {}/{}".format(ip, prefix)
264 if del_action:
265 cmd = "no {}".format(cmd)
266
267 config_data.append(cmd)
268
269 max_paths = addr_data.setdefault("maximum_paths", {})
270 if max_paths:
271 ibgp = max_paths.setdefault("ibgp", None)
272 ebgp = max_paths.setdefault("ebgp", None)
273 if ibgp:
274 config_data.append("maximum-paths ibgp {}".format(
275 ibgp
276 ))
277 if ebgp:
278 config_data.append("maximum-paths {}".format(
279 ebgp
280 ))
281
282 aggregate_addresses = addr_data.setdefault("aggregate_address", [])
283 for aggregate_address in aggregate_addresses:
284 network = aggregate_address.setdefault("network", None)
285 if not network:
286 logger.debug("Router %s: 'network' not present in "
287 "input_dict for BGP", router)
288 else:
289 cmd = "aggregate-address {}".format(network)
290
291 as_set = aggregate_address.setdefault("as_set", False)
292 summary = aggregate_address.setdefault("summary", False)
293 del_action = aggregate_address.setdefault("delete", False)
294 if as_set:
295 cmd = "{} as-set".format(cmd)
296 if summary:
297 cmd = "{} summary".format(cmd)
298
299 if del_action:
300 cmd = "no {}".format(cmd)
301
302 config_data.append(cmd)
303
304 redistribute_data = addr_data.setdefault("redistribute", {})
305 if redistribute_data:
306 for redistribute in redistribute_data:
307 if "redist_type" not in redistribute:
308 logger.error("Router %s: 'redist_type' not present in "
309 "input_dict", router)
310 else:
311 cmd = "redistribute {}".format(
312 redistribute["redist_type"])
313 redist_attr = redistribute.setdefault("attribute",
314 None)
315 if redist_attr:
316 cmd = "{} {}".format(cmd, redist_attr)
317 del_action = redistribute.setdefault("delete", False)
318 if del_action:
319 cmd = "no {}".format(cmd)
320 config_data.append(cmd)
321
322 if "neighbor" in addr_data:
323 neigh_data = __create_bgp_neighbor(topo, input_dict,
324 router, addr_type, add_neigh)
325 config_data.extend(neigh_data)
326
327 for addr_type, addr_dict in bgp_data.iteritems():
328 if not addr_dict or not check_address_types(addr_type):
329 continue
330
331 addr_data = addr_dict["unicast"]
332 if "neighbor" in addr_data:
333 neigh_addr_data = __create_bgp_unicast_address_family(
334 topo, input_dict, router, addr_type, add_neigh)
335
336 config_data.extend(neigh_addr_data)
337
338
339 logger.debug("Exiting lib API: __create_bgp_unicast_neighbor()")
340 return config_data
341
342
343 def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
344 """
345 Helper API to create neighbor specific configuration
346
347 Parameters
348 ----------
349 * `tgen` : Topogen object
350 * `topo` : json file data
351 * `input_dict` : Input dict data, required when configuring from testcase
352 * `router` : router id to be configured
353 """
354
355 config_data = []
356 logger.debug("Entering lib API: __create_bgp_neighbor()")
357
358 bgp_data = input_dict[router]["bgp"]["address_family"]
359 neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
360
361 for name, peer_dict in neigh_data.iteritems():
362 for dest_link, peer in peer_dict["dest_link"].iteritems():
363 nh_details = topo[name]
364 remote_as = nh_details["bgp"]["local_as"]
365 update_source = None
366
367 if dest_link in nh_details["links"].keys():
368 ip_addr = \
369 nh_details["links"][dest_link][addr_type].split("/")[0]
370 # Loopback interface
371 if "source_link" in peer and peer["source_link"] == "lo":
372 update_source = topo[router]["links"]["lo"][
373 addr_type].split("/")[0]
374
375 neigh_cxt = "neighbor {}".format(ip_addr)
376
377 if add_neigh:
378 config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
379 if addr_type == "ipv6":
380 config_data.append("address-family ipv6 unicast")
381 config_data.append("{} activate".format(neigh_cxt))
382
383 disable_connected = peer.setdefault("disable_connected_check",
384 False)
385 keep_alive = peer.setdefault("keep_alive", 60)
386 hold_down = peer.setdefault("hold_down", 180)
387 password = peer.setdefault("password", None)
388 max_hop_limit = peer.setdefault("ebgp_multihop", 1)
389
390 if update_source:
391 config_data.append("{} update-source {}".format(
392 neigh_cxt, update_source))
393 if disable_connected:
394 config_data.append("{} disable-connected-check".format(
395 disable_connected))
396 if update_source:
397 config_data.append("{} update-source {}".format(neigh_cxt,
398 update_source))
399 if int(keep_alive) != 60 and int(hold_down) != 180:
400 config_data.append(
401 "{} timers {} {}".format(neigh_cxt, keep_alive,
402 hold_down))
403 if password:
404 config_data.append(
405 "{} password {}".format(neigh_cxt, password))
406
407 if max_hop_limit > 1:
408 config_data.append("{} ebgp-multihop {}".format(neigh_cxt,
409 max_hop_limit))
410 config_data.append("{} enforce-multihop".format(neigh_cxt))
411
412 logger.debug("Exiting lib API: __create_bgp_unicast_neighbor()")
413 return config_data
414
415
416 def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type,
417 add_neigh=True):
418 """
419 API prints bgp global config to bgp_json file.
420
421 Parameters
422 ----------
423 * `bgp_cfg` : BGP class variables have BGP config saved in it for
424 particular router,
425 * `local_as_no` : Local as number
426 * `router_id` : Router-id
427 * `ecmp_path` : ECMP max path
428 * `gr_enable` : BGP global gracefull restart config
429 """
430
431 config_data = []
432 logger.debug("Entering lib API: __create_bgp_unicast_neighbor()")
433
434 bgp_data = input_dict[router]["bgp"]["address_family"]
435 neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
436
437 for peer_name, peer_dict in deepcopy(neigh_data).iteritems():
438 for dest_link, peer in peer_dict["dest_link"].iteritems():
439 deactivate = None
440 nh_details = topo[peer_name]
441 # Loopback interface
442 if "source_link" in peer and peer["source_link"] == "lo":
443 for destRouterLink, data in sorted(nh_details["links"].
444 iteritems()):
445 if "type" in data and data["type"] == "loopback":
446 if dest_link == destRouterLink:
447 ip_addr = \
448 nh_details["links"][destRouterLink][
449 addr_type].split("/")[0]
450
451 # Physical interface
452 else:
453 if dest_link in nh_details["links"].keys():
454
455 ip_addr = nh_details["links"][dest_link][
456 addr_type].split("/")[0]
457 if addr_type == "ipv4" and bgp_data["ipv6"]:
458 deactivate = nh_details["links"][
459 dest_link]["ipv6"].split("/")[0]
460
461 neigh_cxt = "neighbor {}".format(ip_addr)
462 config_data.append("address-family {} unicast".format(
463 addr_type
464 ))
465 if deactivate:
466 config_data.append(
467 "no neighbor {} activate".format(deactivate))
468
469 next_hop_self = peer.setdefault("next_hop_self", None)
470 send_community = peer.setdefault("send_community", None)
471 prefix_lists = peer.setdefault("prefix_lists", {})
472 route_maps = peer.setdefault("route_maps", {})
473 no_send_community = peer.setdefault("no_send_community", None)
474
475 # next-hop-self
476 if next_hop_self:
477 config_data.append("{} next-hop-self".format(neigh_cxt))
478 # send_community
479 if send_community:
480 config_data.append("{} send-community".format(neigh_cxt))
481
482 # no_send_community
483 if no_send_community:
484 config_data.append("no {} send-community {}".format(
485 neigh_cxt, no_send_community))
486
487 if prefix_lists:
488 for prefix_list in prefix_lists:
489 name = prefix_list.setdefault("name", {})
490 direction = prefix_list.setdefault("direction", "in")
491 del_action = prefix_list.setdefault("delete", False)
492 if not name:
493 logger.info("Router %s: 'name' not present in "
494 "input_dict for BGP neighbor prefix lists",
495 router)
496 else:
497 cmd = "{} prefix-list {} {}".format(neigh_cxt, name,
498 direction)
499 if del_action:
500 cmd = "no {}".format(cmd)
501 config_data.append(cmd)
502
503 if route_maps:
504 for route_map in route_maps:
505 name = route_map.setdefault("name", {})
506 direction = route_map.setdefault("direction", "in")
507 del_action = route_map.setdefault("delete", False)
508 if not name:
509 logger.info("Router %s: 'name' not present in "
510 "input_dict for BGP neighbor route name",
511 router)
512 else:
513 cmd = "{} route-map {} {}".format(neigh_cxt, name,
514 direction)
515 if del_action:
516 cmd = "no {}".format(cmd)
517 config_data.append(cmd)
518
519 return config_data
520
521
522 #############################################
523 # Verification APIs
524 #############################################
525 @retry(attempts=3, wait=2, return_is_str=True)
526 def verify_router_id(tgen, topo, input_dict):
527 """
528 Running command "show ip bgp json" for DUT and reading router-id
529 from input_dict and verifying with command output.
530 1. Statically modfified router-id should take place
531 2. When static router-id is deleted highest loopback should
532 become router-id
533 3. When loopback intf is down then highest physcial intf
534 should become router-id
535
536 Parameters
537 ----------
538 * `tgen`: topogen object
539 * `topo`: input json file data
540 * `input_dict`: input dictionary, have details of Device Under Test, for
541 which user wants to test the data
542 Usage
543 -----
544 # Verify if router-id for r1 is 12.12.12.12
545 input_dict = {
546 "r1":{
547 "router_id": "12.12.12.12"
548 }
549 # Verify that router-id for r1 is highest interface ip
550 input_dict = {
551 "routers": ["r1"]
552 }
553 result = verify_router_id(tgen, topo, input_dict)
554
555 Returns
556 -------
557 errormsg(str) or True
558 """
559
560 logger.debug("Entering lib API: verify_router_id()")
561 for router in input_dict.keys():
562 if router not in tgen.routers():
563 continue
564
565 rnode = tgen.routers()[router]
566
567 del_router_id = input_dict[router]["bgp"].setdefault(
568 "del_router_id", False)
569
570 logger.info("Checking router %s router-id", router)
571 show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
572 isjson=True)
573 router_id_out = show_bgp_json["ipv4Unicast"]["routerId"]
574 router_id_out = ipaddr.IPv4Address(unicode(router_id_out))
575
576 # Once router-id is deleted, highest interface ip should become
577 # router-id
578 if del_router_id:
579 router_id = find_interface_with_greater_ip(topo, router)
580 else:
581 router_id = input_dict[router]["bgp"]["router_id"]
582 router_id = ipaddr.IPv4Address(unicode(router_id))
583
584 if router_id == router_id_out:
585 logger.info("Found expected router-id %s for router %s",
586 router_id, router)
587 else:
588 errormsg = "Router-id for router:{} mismatch, expected:" \
589 " {} but found:{}".format(router, router_id,
590 router_id_out)
591 return errormsg
592
593 logger.debug("Exiting lib API: verify_router_id()")
594 return True
595
596
597 @retry(attempts=20, wait=2, return_is_str=True)
598 def verify_bgp_convergence(tgen, topo):
599 """
600 API will verify if BGP is converged with in the given time frame.
601 Running "show bgp summary json" command and verify bgp neighbor
602 state is established,
603 Parameters
604 ----------
605 * `tgen`: topogen object
606 * `topo`: input json file data
607 * `addr_type`: ip_type, ipv4/ipv6
608 Usage
609 -----
610 # To veriry is BGP is converged for all the routers used in
611 topology
612 results = verify_bgp_convergence(tgen, topo, "ipv4")
613 Returns
614 -------
615 errormsg(str) or True
616 """
617
618 logger.debug("Entering lib API: verify_bgp_convergence()")
619 for router, rnode in tgen.routers().iteritems():
620 logger.info("Verifying BGP Convergence on router %s", router)
621 show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
622 isjson=True)
623 # Verifying output dictionary show_bgp_json is empty or not
624 if not bool(show_bgp_json):
625 errormsg = "BGP is not running"
626 return errormsg
627
628 # To find neighbor ip type
629 bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
630 for addr_type in bgp_addr_type.keys():
631 if not check_address_types(addr_type):
632 continue
633 total_peer = 0
634
635 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
636
637 for bgp_neighbor in bgp_neighbors:
638 total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
639
640 for addr_type in bgp_addr_type.keys():
641 if not check_address_types(addr_type):
642 continue
643 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
644
645 no_of_peer = 0
646 for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
647 for dest_link in peer_data["dest_link"].keys():
648 data = topo["routers"][bgp_neighbor]["links"]
649 if dest_link in data:
650 neighbor_ip = \
651 data[dest_link][addr_type].split("/")[0]
652 if addr_type == "ipv4":
653 ipv4_data = show_bgp_json["ipv4Unicast"][
654 "peers"]
655 nh_state = ipv4_data[neighbor_ip]["state"]
656 else:
657 ipv6_data = show_bgp_json["ipv6Unicast"][
658 "peers"]
659 nh_state = ipv6_data[neighbor_ip]["state"]
660
661 if nh_state == "Established":
662 no_of_peer += 1
663 if no_of_peer == total_peer:
664 logger.info("BGP is Converged for router %s", router)
665 else:
666 errormsg = "BGP is not converged for router {}".format(
667 router)
668 return errormsg
669
670 logger.debug("Exiting API: verify_bgp_convergence()")
671 return True
672
673
674 def modify_as_number(tgen, topo, input_dict):
675 """
676 API reads local_as and remote_as from user defined input_dict and
677 modify router"s ASNs accordingly. Router"s config is modified and
678 recent/changed config is loadeded to router.
679
680 Parameters
681 ----------
682 * `tgen` : Topogen object
683 * `topo` : json file data
684 * `input_dict` : defines for which router ASNs needs to be modified
685
686 Usage
687 -----
688 To modify ASNs for router r1
689 input_dict = {
690 "r1": {
691 "bgp": {
692 "local_as": 131079
693 }
694 }
695 result = modify_as_number(tgen, topo, input_dict)
696
697 Returns
698 -------
699 errormsg(str) or True
700 """
701
702 logger.debug("Entering lib API: modify_as_number()")
703 try:
704
705 new_topo = deepcopy(topo["routers"])
706 router_dict = {}
707 for router in input_dict.keys():
708 # Remove bgp configuration
709
710 router_dict.update({
711 router: {
712 "bgp": {
713 "delete": True
714 }
715 }
716 })
717
718 new_topo[router]["bgp"]["local_as"] = \
719 input_dict[router]["bgp"]["local_as"]
720
721 logger.info("Removing bgp configuration")
722 create_router_bgp(tgen, topo, router_dict)
723
724 logger.info("Applying modified bgp configuration")
725 create_router_bgp(tgen, new_topo)
726
727 except Exception as e:
728 # handle any exception
729 logger.error("Error %s occured. Arguments %s.", e.message, e.args)
730
731 # Traceback
732 errormsg = traceback.format_exc()
733 logger.error(errormsg)
734 return errormsg
735
736 logger.debug("Exiting lib API: modify_as_number()")
737
738 return True
739
740
741 @retry(attempts=3, wait=2, return_is_str=True)
742 def verify_as_numbers(tgen, topo, input_dict):
743 """
744 This API is to verify AS numbers for given DUT by running
745 "show ip bgp neighbor json" command. Local AS and Remote AS
746 will ve verified with input_dict data and command output.
747
748 Parameters
749 ----------
750 * `tgen`: topogen object
751 * `topo`: input json file data
752 * `addr_type` : ip type, ipv4/ipv6
753 * `input_dict`: defines - for which router, AS numbers needs to be verified
754
755 Usage
756 -----
757 input_dict = {
758 "r1": {
759 "bgp": {
760 "local_as": 131079
761 }
762 }
763 }
764 result = verify_as_numbers(tgen, topo, addr_type, input_dict)
765
766 Returns
767 -------
768 errormsg(str) or True
769 """
770
771 logger.debug("Entering lib API: verify_as_numbers()")
772 for router in input_dict.keys():
773 if router not in tgen.routers():
774 continue
775
776 rnode = tgen.routers()[router]
777
778 logger.info("Verifying AS numbers for dut %s:", router)
779
780 show_ip_bgp_neighbor_json = run_frr_cmd(rnode,
781 "show ip bgp neighbor json", isjson=True)
782 local_as = input_dict[router]["bgp"]["local_as"]
783 bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
784
785 for addr_type in bgp_addr_type:
786 if not check_address_types(addr_type):
787 continue
788
789 bgp_neighbors = bgp_addr_type[addr_type]["unicast"][
790 "neighbor"]
791
792 for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
793 remote_as = input_dict[bgp_neighbor]["bgp"]["local_as"]
794 for dest_link, peer_dict in peer_data["dest_link"].iteritems():
795 neighbor_ip = None
796 data = topo["routers"][bgp_neighbor]["links"]
797
798 if dest_link in data:
799 neighbor_ip = data[dest_link][addr_type]. \
800 split("/")[0]
801 neigh_data = show_ip_bgp_neighbor_json[neighbor_ip]
802 # Verify Local AS for router
803 if neigh_data["localAs"] != local_as:
804 errormsg = "Failed: Verify local_as for dut {}," \
805 " found: {} but expected: {}".format(
806 router, neigh_data["localAs"],
807 local_as)
808 return errormsg
809 else:
810 logger.info("Verified local_as for dut %s, found"
811 " expected: %s", router, local_as)
812
813 # Verify Remote AS for neighbor
814 if neigh_data["remoteAs"] != remote_as:
815 errormsg = "Failed: Verify remote_as for dut " \
816 "{}'s neighbor {}, found: {} but " \
817 "expected: {}".format(
818 router, bgp_neighbor,
819 neigh_data["remoteAs"], remote_as)
820 return errormsg
821 else:
822 logger.info("Verified remote_as for dut %s's "
823 "neighbor %s, found expected: %s",
824 router, bgp_neighbor, remote_as)
825
826 logger.debug("Exiting lib API: verify_AS_numbers()")
827 return True
828
829
830 def clear_bgp_and_verify(tgen, topo, router):
831 """
832 This API is to clear bgp neighborship and verify bgp neighborship
833 is coming up(BGP is converged) usinf "show bgp summary json" command
834 and also verifying for all bgp neighbors uptime before and after
835 clear bgp sessions is different as the uptime must be changed once
836 bgp sessions are cleared using "clear ip bgp */clear bgp ipv6 *" cmd.
837
838 Parameters
839 ----------
840 * `tgen`: topogen object
841 * `topo`: input json file data
842 * `router`: device under test
843
844 Usage
845 -----
846 result = clear_bgp_and_verify(tgen, topo, addr_type, dut)
847
848 Returns
849 -------
850 errormsg(str) or True
851 """
852
853 logger.debug("Entering lib API: clear_bgp_and_verify()")
854
855 if router not in tgen.routers():
856 return False
857
858 rnode = tgen.routers()[router]
859
860 peer_uptime_before_clear_bgp = {}
861 # Verifying BGP convergence before bgp clear command
862 for retry in range(31):
863 sleeptime = 3
864 # Waiting for BGP to converge
865 logger.info("Waiting for %s sec for BGP to converge on router"
866 " %s...", sleeptime, router)
867 sleep(sleeptime)
868
869 show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
870 isjson=True)
871 # Verifying output dictionary show_bgp_json is empty or not
872 if not bool(show_bgp_json):
873 errormsg = "BGP is not running"
874 return errormsg
875
876 # To find neighbor ip type
877 bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
878 total_peer = 0
879 for addr_type in bgp_addr_type.keys():
880
881 if not check_address_types(addr_type):
882 continue
883
884 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
885
886 for bgp_neighbor in bgp_neighbors:
887 total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
888
889 no_of_peer = 0
890 for addr_type in bgp_addr_type:
891 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
892
893 for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
894 for dest_link, peer_dict in peer_data["dest_link"].iteritems():
895 data = topo["routers"][bgp_neighbor]["links"]
896
897 if dest_link in data:
898 neighbor_ip = data[dest_link][addr_type].split("/")[0]
899 if addr_type == "ipv4":
900 ipv4_data = show_bgp_json["ipv4Unicast"][
901 "peers"]
902 nh_state = ipv4_data[neighbor_ip]["state"]
903
904 # Peer up time dictionary
905 peer_uptime_before_clear_bgp[bgp_neighbor] = \
906 ipv4_data[neighbor_ip]["peerUptimeEstablishedEpoch"]
907 else:
908 ipv6_data = show_bgp_json["ipv6Unicast"][
909 "peers"]
910 nh_state = ipv6_data[neighbor_ip]["state"]
911
912 # Peer up time dictionary
913 peer_uptime_before_clear_bgp[bgp_neighbor] = \
914 ipv6_data[neighbor_ip]["peerUptimeEstablishedEpoch"]
915
916 if nh_state == "Established":
917 no_of_peer += 1
918
919 if no_of_peer == total_peer:
920 logger.info("BGP is Converged for router %s before bgp"
921 " clear", router)
922 break
923 else:
924 logger.info("BGP is not yet Converged for router %s "
925 "before bgp clear", router)
926 else:
927 errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \
928 " router {}".format(router)
929 return errormsg
930
931 logger.info(peer_uptime_before_clear_bgp)
932 # Clearing BGP
933 logger.info("Clearing BGP neighborship for router %s..", router)
934 for addr_type in bgp_addr_type.keys():
935 if addr_type == "ipv4":
936 run_frr_cmd(rnode, "clear ip bgp *")
937 elif addr_type == "ipv6":
938 run_frr_cmd(rnode, "clear bgp ipv6 *")
939
940 peer_uptime_after_clear_bgp = {}
941 # Verifying BGP convergence after bgp clear command
942 for retry in range(31):
943 sleeptime = 3
944 # Waiting for BGP to converge
945 logger.info("Waiting for %s sec for BGP to converge on router"
946 " %s...", sleeptime, router)
947 sleep(sleeptime)
948
949
950 show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
951 isjson=True)
952 # Verifying output dictionary show_bgp_json is empty or not
953 if not bool(show_bgp_json):
954 errormsg = "BGP is not running"
955 return errormsg
956
957 # To find neighbor ip type
958 bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
959 total_peer = 0
960 for addr_type in bgp_addr_type.keys():
961 if not check_address_types(addr_type):
962 continue
963
964 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
965
966 for bgp_neighbor in bgp_neighbors:
967 total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
968
969 no_of_peer = 0
970 for addr_type in bgp_addr_type:
971 bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
972
973 for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
974 for dest_link, peer_dict in peer_data["dest_link"].iteritems():
975 data = topo["routers"][bgp_neighbor]["links"]
976
977 if dest_link in data:
978 neighbor_ip = data[dest_link][addr_type].\
979 split("/")[0]
980 if addr_type == "ipv4":
981 ipv4_data = show_bgp_json["ipv4Unicast"][
982 "peers"]
983 nh_state = ipv4_data[neighbor_ip]["state"]
984 peer_uptime_after_clear_bgp[bgp_neighbor] = \
985 ipv4_data[neighbor_ip]["peerUptimeEstablishedEpoch"]
986 else:
987 ipv6_data = show_bgp_json["ipv6Unicast"][
988 "peers"]
989 nh_state = ipv6_data[neighbor_ip]["state"]
990 # Peer up time dictionary
991 peer_uptime_after_clear_bgp[bgp_neighbor] = \
992 ipv6_data[neighbor_ip]["peerUptimeEstablishedEpoch"]
993
994 if nh_state == "Established":
995 no_of_peer += 1
996
997 if no_of_peer == total_peer:
998 logger.info("BGP is Converged for router %s after bgp clear",
999 router)
1000 break
1001 else:
1002 logger.info("BGP is not yet Converged for router %s after"
1003 " bgp clear", router)
1004 else:
1005 errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \
1006 " router {}".format(router)
1007 return errormsg
1008 logger.info(peer_uptime_after_clear_bgp)
1009 # Comparing peerUptimeEstablishedEpoch dictionaries
1010 if peer_uptime_before_clear_bgp != peer_uptime_after_clear_bgp:
1011 logger.info("BGP neighborship is reset after clear BGP on router %s",
1012 router)
1013 else:
1014 errormsg = "BGP neighborship is not reset after clear bgp on router" \
1015 " {}".format(router)
1016 return errormsg
1017
1018 logger.debug("Exiting lib API: clear_bgp_and_verify()")
1019 return True
1020
1021
1022 def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
1023 """
1024 To verify BGP timer config, execute "show ip bgp neighbor json" command
1025 and verify bgp timers with input_dict data.
1026 To veirfy bgp timers functonality, shutting down peer interface
1027 and verify BGP neighborship status.
1028
1029 Parameters
1030 ----------
1031 * `tgen`: topogen object
1032 * `topo`: input json file data
1033 * `addr_type`: ip type, ipv4/ipv6
1034 * `input_dict`: defines for which router, bgp timers needs to be verified
1035
1036 Usage:
1037 # To verify BGP timers for neighbor r2 of router r1
1038 input_dict = {
1039 "r1": {
1040 "bgp": {
1041 "bgp_neighbors":{
1042 "r2":{
1043 "keepalivetimer": 5,
1044 "holddowntimer": 15,
1045 }}}}}
1046 result = verify_bgp_timers_and_functionality(tgen, topo, "ipv4",
1047 input_dict)
1048
1049 Returns
1050 -------
1051 errormsg(str) or True
1052 """
1053
1054 logger.debug("Entering lib API: verify_bgp_timers_and_functionality()")
1055 sleep(5)
1056 router_list = tgen.routers()
1057 for router in input_dict.keys():
1058 if router not in router_list:
1059 continue
1060
1061 rnode = router_list[router]
1062
1063 logger.info("Verifying bgp timers functionality, DUT is %s:",
1064 router)
1065
1066 show_ip_bgp_neighbor_json = \
1067 run_frr_cmd(rnode, "show ip bgp neighbor json", isjson=True)
1068
1069 bgp_addr_type = input_dict[router]["bgp"]["address_family"]
1070
1071 for addr_type in bgp_addr_type:
1072 if not check_address_types(addr_type):
1073 continue
1074
1075 bgp_neighbors = bgp_addr_type[addr_type]["unicast"][
1076 "neighbor"]
1077 for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
1078 for dest_link, peer_dict in peer_data["dest_link"].iteritems():
1079 data = topo["routers"][bgp_neighbor]["links"]
1080
1081 keepalivetimer = peer_dict["keepalivetimer"]
1082 holddowntimer = peer_dict["holddowntimer"]
1083
1084 if dest_link in data:
1085 neighbor_ip = data[dest_link][addr_type]. \
1086 split("/")[0]
1087 neighbor_intf = data[dest_link]["interface"]
1088
1089 # Verify HoldDownTimer for neighbor
1090 bgpHoldTimeMsecs = show_ip_bgp_neighbor_json[
1091 neighbor_ip]["bgpTimerHoldTimeMsecs"]
1092 if bgpHoldTimeMsecs != holddowntimer * 1000:
1093 errormsg = "Verifying holddowntimer for bgp " \
1094 "neighbor {} under dut {}, found: {} " \
1095 "but expected: {}".format(
1096 neighbor_ip, router,
1097 bgpHoldTimeMsecs,
1098 holddowntimer * 1000)
1099 return errormsg
1100
1101 # Verify KeepAliveTimer for neighbor
1102 bgpKeepAliveTimeMsecs = show_ip_bgp_neighbor_json[
1103 neighbor_ip]["bgpTimerKeepAliveIntervalMsecs"]
1104 if bgpKeepAliveTimeMsecs != keepalivetimer * 1000:
1105 errormsg = "Verifying keepalivetimer for bgp " \
1106 "neighbor {} under dut {}, found: {} " \
1107 "but expected: {}".format(
1108 neighbor_ip, router,
1109 bgpKeepAliveTimeMsecs,
1110 keepalivetimer * 1000)
1111 return errormsg
1112
1113 ####################
1114 # Shutting down peer interface after keepalive time and
1115 # after some time bringing up peer interface.
1116 # verifying BGP neighborship in (hold down-keep alive)
1117 # time, it should not go down
1118 ####################
1119
1120 # Wait till keep alive time
1121 logger.info("=" * 20)
1122 logger.info("Scenario 1:")
1123 logger.info("Shutdown and bring up peer interface: %s "
1124 "in keep alive time : %s sec and verify "
1125 " BGP neighborship is intact in %s sec ",
1126 neighbor_intf, keepalivetimer,
1127 (holddowntimer - keepalivetimer))
1128 logger.info("=" * 20)
1129 logger.info("Waiting for %s sec..", keepalivetimer)
1130 sleep(keepalivetimer)
1131
1132 # Shutting down peer ineterface
1133 logger.info("Shutting down interface %s on router %s",
1134 neighbor_intf, bgp_neighbor)
1135 topotest.interface_set_status(
1136 router_list[bgp_neighbor], neighbor_intf,
1137 ifaceaction=False)
1138
1139 # Bringing up peer interface
1140 sleep(5)
1141 logger.info("Bringing up interface %s on router %s..",
1142 neighbor_intf, bgp_neighbor)
1143 topotest.interface_set_status(
1144 router_list[bgp_neighbor], neighbor_intf,
1145 ifaceaction=True)
1146
1147 # Verifying BGP neighborship is intact in
1148 # (holddown - keepalive) time
1149 for timer in range(keepalivetimer, holddowntimer,
1150 int(holddowntimer / 3)):
1151 logger.info("Waiting for %s sec..", keepalivetimer)
1152 sleep(keepalivetimer)
1153 sleep(2)
1154 show_bgp_json = \
1155 run_frr_cmd(rnode, "show bgp summary json",
1156 isjson=True)
1157
1158 if addr_type == "ipv4":
1159 ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
1160 nh_state = ipv4_data[neighbor_ip]["state"]
1161 else:
1162 ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
1163 nh_state = ipv6_data[neighbor_ip]["state"]
1164
1165 if timer == \
1166 (holddowntimer - keepalivetimer):
1167 if nh_state != "Established":
1168 errormsg = "BGP neighborship has not gone " \
1169 "down in {} sec for neighbor {}" \
1170 .format(timer, bgp_neighbor)
1171 return errormsg
1172 else:
1173 logger.info("BGP neighborship is intact in %s"
1174 " sec for neighbor %s",
1175 timer, bgp_neighbor)
1176
1177 ####################
1178 # Shutting down peer interface and verifying that BGP
1179 # neighborship is going down in holddown time
1180 ####################
1181 logger.info("=" * 20)
1182 logger.info("Scenario 2:")
1183 logger.info("Shutdown peer interface: %s and verify BGP"
1184 " neighborship has gone down in hold down "
1185 "time %s sec", neighbor_intf, holddowntimer)
1186 logger.info("=" * 20)
1187
1188 logger.info("Shutting down interface %s on router %s..",
1189 neighbor_intf, bgp_neighbor)
1190 topotest.interface_set_status(router_list[bgp_neighbor],
1191 neighbor_intf,
1192 ifaceaction=False)
1193
1194 # Verifying BGP neighborship is going down in holddown time
1195 for timer in range(keepalivetimer,
1196 (holddowntimer + keepalivetimer),
1197 int(holddowntimer / 3)):
1198 logger.info("Waiting for %s sec..", keepalivetimer)
1199 sleep(keepalivetimer)
1200 sleep(2)
1201 show_bgp_json = \
1202 run_frr_cmd(rnode, "show bgp summary json",
1203 isjson=True)
1204
1205 if addr_type == "ipv4":
1206 ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
1207 nh_state = ipv4_data[neighbor_ip]["state"]
1208 else:
1209 ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
1210 nh_state = ipv6_data[neighbor_ip]["state"]
1211
1212 if timer == holddowntimer:
1213 if nh_state == "Established":
1214 errormsg = "BGP neighborship has not gone " \
1215 "down in {} sec for neighbor {}" \
1216 .format(timer, bgp_neighbor)
1217 return errormsg
1218 else:
1219 logger.info("BGP neighborship has gone down in"
1220 " %s sec for neighbor %s",
1221 timer, bgp_neighbor)
1222
1223 logger.debug("Exiting lib API: verify_bgp_timers_and_functionality()")
1224 return True
1225
1226
1227 @retry(attempts=3, wait=4, return_is_str=True)
1228 def verify_bgp_attributes(tgen, addr_type, dut, static_routes, rmap_name,
1229 input_dict, seq_id=None):
1230 """
1231 API will verify BGP attributes set by Route-map for given prefix and
1232 DUT. it will run "show bgp ipv4/ipv6 {prefix_address} json" command
1233 in DUT to verify BGP attributes set by route-map, Set attributes
1234 values will be read from input_dict and verified with command output.
1235
1236 * `tgen`: topogen object
1237 * `addr_type` : ip type, ipv4/ipv6
1238 * `dut`: Device Under Test
1239 * `static_routes`: Static Routes for which BGP set attributes needs to be
1240 verified
1241 * `rmap_name`: route map name for which set criteria needs to be verified
1242 * `input_dict`: defines for which router, AS numbers needs
1243 * `seq_id`: sequence number of rmap, default is None
1244
1245 Usage
1246 -----
1247 input_dict = {
1248 "r3": {
1249 "route_maps": {
1250 "rmap_match_pf_1_ipv4": [{
1251 "action": "permit",
1252 'seq_id': '5',
1253 "match": {
1254 addr_type: {
1255 "prefix_lists": "pf_list_1_" + addr_type
1256 }
1257 },
1258 "set": {
1259 "localpref": 150,
1260 "weight": 100
1261 }
1262 }],
1263 "rmap_match_pf_2_ipv6": [{
1264 "action": "permit",
1265 'seq_id': '5',
1266 "match": {
1267 addr_type: {
1268 "prefix_lists": "pf_list_1_" + addr_type
1269 }
1270 },
1271 "set": {
1272 "med": 50
1273 }
1274 }]
1275 }
1276 }
1277 }
1278 result = verify_bgp_attributes(tgen, 'ipv4', "r1", "10.0.20.1/32",
1279 rmap_match_pf_1_ipv4, input_dict)
1280
1281 Returns
1282 -------
1283 errormsg(str) or True
1284 """
1285
1286 logger.debug("Entering lib API: verify_bgp_attributes()")
1287 for router, rnode in tgen.routers().iteritems():
1288 if router != dut:
1289 continue
1290
1291 logger.info('Verifying BGP set attributes for dut {}:'.format(router))
1292
1293 for static_route in static_routes:
1294 cmd = "show bgp {} {} json".format(addr_type, static_route)
1295 show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True)
1296 print("show_bgp_json $$$$$", show_bgp_json)
1297
1298 dict_to_test = []
1299 tmp_list = []
1300 for rmap_router in input_dict.keys():
1301 for rmap, values in input_dict[rmap_router][
1302 "route_maps"].items():
1303 print("rmap == rmap_name $$$$1", rmap, rmap_name)
1304 if rmap == rmap_name:
1305 print("rmap == rmap_name $$$$", rmap, rmap_name)
1306 dict_to_test = values
1307 for rmap_dict in values:
1308 if seq_id is not None:
1309 if type(seq_id) is not list:
1310 seq_id = [seq_id]
1311
1312 if "seq_id" in rmap_dict:
1313 rmap_seq_id = \
1314 rmap_dict["seq_id"]
1315 for _seq_id in seq_id:
1316 if _seq_id == rmap_seq_id:
1317 tmp_list.append(rmap_dict)
1318 if tmp_list:
1319 dict_to_test = tmp_list
1320
1321 print("dict_to_test $$$$", dict_to_test)
1322 for rmap_dict in dict_to_test:
1323 if "set" in rmap_dict:
1324 for criteria in rmap_dict["set"].keys():
1325 if criteria not in show_bgp_json[
1326 "paths"][0]:
1327 errormsg = ("BGP attribute: {}"
1328 " is not found in"
1329 " cli: {} output "
1330 "in router {}".
1331 format(criteria,
1332 cmd,
1333 router))
1334 return errormsg
1335
1336 if rmap_dict["set"][criteria] == \
1337 show_bgp_json["paths"][0][
1338 criteria]:
1339 logger.info("Verifying BGP "
1340 "attribute {} for"
1341 " route: {} in "
1342 "router: {}, found"
1343 " expected value:"
1344 " {}".
1345 format(criteria,
1346 static_route,
1347 dut,
1348 rmap_dict[
1349 "set"][
1350 criteria]))
1351 else:
1352 errormsg = \
1353 ("Failed: Verifying BGP "
1354 "attribute {} for route:"
1355 " {} in router: {}, "
1356 " expected value: {} but"
1357 " found: {}".
1358 format(criteria,
1359 static_route,
1360 dut,
1361 rmap_dict["set"]
1362 [criteria],
1363 show_bgp_json[
1364 'paths'][
1365 0][criteria]))
1366 return errormsg
1367
1368 logger.debug("Exiting lib API: verify_bgp_attributes()")
1369 return True
1370
1371 @retry(attempts=4, wait=2, return_is_str=True, initial_wait=2)
1372 def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,
1373 attribute):
1374 """
1375 API is to verify best path according to BGP attributes for given routes.
1376 "show bgp ipv4/6 json" command will be run and verify best path according
1377 to shortest as-path, highest local-preference and med, lowest weight and
1378 route origin IGP>EGP>INCOMPLETE.
1379
1380 Parameters
1381 ----------
1382 * `tgen` : topogen object
1383 * `addr_type` : ip type, ipv4/ipv6
1384 * `tgen` : topogen object
1385 * `attribute` : calculate best path using this attribute
1386 * `input_dict`: defines different routes to calculate for which route
1387 best path is selected
1388
1389 Usage
1390 -----
1391 # To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from
1392 router r7 to router r1(DUT) as per shortest as-path attribute
1393 input_dict = {
1394 "r7": {
1395 "bgp": {
1396 "address_family": {
1397 "ipv4": {
1398 "unicast": {
1399 "advertise_networks": [
1400 {
1401 "network": "200.50.2.0/32"
1402 },
1403 {
1404 "network": "200.60.2.0/32"
1405 }
1406 ]
1407 }
1408 }
1409 }
1410 }
1411 }
1412 }
1413 attribute = "localpref"
1414 result = verify_best_path_as_per_bgp_attribute(tgen, "ipv4", dut, \
1415 input_dict, attribute)
1416 Returns
1417 -------
1418 errormsg(str) or True
1419 """
1420
1421 logger.debug("Entering lib API: verify_best_path_as_per_bgp_attribute()")
1422 if router not in tgen.routers():
1423 return False
1424
1425 rnode = tgen.routers()[router]
1426
1427 command = "show bgp {} json".format(addr_type)
1428
1429 sleep(5)
1430 logger.info("Verifying router %s RIB for best path:", router)
1431 sh_ip_bgp_json = run_frr_cmd(rnode, command, isjson=True)
1432
1433 for route_val in input_dict.values():
1434 net_data = route_val["bgp"]["address_family"][addr_type]["unicast"]
1435 networks = net_data["advertise_networks"]
1436 for network in networks:
1437 route = network["network"]
1438
1439 route_attributes = sh_ip_bgp_json["routes"][route]
1440 _next_hop = None
1441 compare = None
1442 attribute_dict = {}
1443 for route_attribute in route_attributes:
1444 next_hops = route_attribute["nexthops"]
1445 for next_hop in next_hops:
1446 next_hop_ip = next_hop["ip"]
1447 attribute_dict[next_hop_ip] = route_attribute[attribute]
1448
1449 # AS_PATH attribute
1450 if attribute == "aspath":
1451 # Find next_hop for the route have minimum as_path
1452 _next_hop = min(attribute_dict, key=lambda x: len(set(
1453 attribute_dict[x])))
1454 compare = "SHORTEST"
1455
1456 # LOCAL_PREF attribute
1457 elif attribute == "localpref":
1458 # Find next_hop for the route have highest local preference
1459 _next_hop = max(attribute_dict, key=(lambda k:
1460 attribute_dict[k]))
1461 compare = "HIGHEST"
1462
1463 # WEIGHT attribute
1464 elif attribute == "weight":
1465 # Find next_hop for the route have highest weight
1466 _next_hop = max(attribute_dict, key=(lambda k:
1467 attribute_dict[k]))
1468 compare = "HIGHEST"
1469
1470 # ORIGIN attribute
1471 elif attribute == "origin":
1472 # Find next_hop for the route have IGP as origin, -
1473 # - rule is IGP>EGP>INCOMPLETE
1474 _next_hop = [key for (key, value) in
1475 attribute_dict.iteritems()
1476 if value == "IGP"][0]
1477 compare = ""
1478
1479 # MED attribute
1480 elif attribute == "med":
1481 # Find next_hop for the route have LOWEST MED
1482 _next_hop = min(attribute_dict, key=(lambda k:
1483 attribute_dict[k]))
1484 compare = "LOWEST"
1485
1486 # Show ip route
1487 if addr_type == "ipv4":
1488 command = "show ip route json"
1489 else:
1490 command = "show ipv6 route json"
1491
1492 rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
1493
1494 # Verifying output dictionary rib_routes_json is not empty
1495 if not bool(rib_routes_json):
1496 errormsg = "No route found in RIB of router {}..". \
1497 format(router)
1498 return errormsg
1499
1500 st_found = False
1501 nh_found = False
1502 # Find best is installed in RIB
1503 if route in rib_routes_json:
1504 st_found = True
1505 # Verify next_hop in rib_routes_json
1506 if rib_routes_json[route][0]["nexthops"][0]["ip"] in \
1507 attribute_dict:
1508 nh_found = True
1509 else:
1510 errormsg = "Incorrect Nexthop for BGP route {} in " \
1511 "RIB of router {}, Expected: {}, Found:" \
1512 " {}\n".format(route, router,
1513 rib_routes_json[route][0][
1514 "nexthops"][0]["ip"],
1515 _next_hop)
1516 return errormsg
1517
1518 if st_found and nh_found:
1519 logger.info(
1520 "Best path for prefix: %s with next_hop: %s is "
1521 "installed according to %s %s: (%s) in RIB of "
1522 "router %s", route, _next_hop, compare,
1523 attribute, attribute_dict[_next_hop], router)
1524
1525 logger.debug("Exiting lib API: verify_best_path_as_per_bgp_attribute()")
1526 return True
1527
1528
1529 def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,
1530 attribute):
1531 """
1532 API is to verify best path according to admin distance for given
1533 route. "show ip/ipv6 route json" command will be run and verify
1534 best path accoring to shortest admin distanc.
1535
1536 Parameters
1537 ----------
1538 * `addr_type` : ip type, ipv4/ipv6
1539 * `dut`: Device Under Test
1540 * `tgen` : topogen object
1541 * `attribute` : calculate best path using admin distance
1542 * `input_dict`: defines different routes with different admin distance
1543 to calculate for which route best path is selected
1544 Usage
1545 -----
1546 # To verify best path for route 200.50.2.0/32 from router r2 to
1547 router r1(DUT) as per shortest admin distance which is 60.
1548 input_dict = {
1549 "r2": {
1550 "static_routes": [{"network": "200.50.2.0/32", \
1551 "admin_distance": 80, "next_hop": "10.0.0.14"},
1552 {"network": "200.50.2.0/32", \
1553 "admin_distance": 60, "next_hop": "10.0.0.18"}]
1554 }}
1555 attribute = "localpref"
1556 result = verify_best_path_as_per_admin_distance(tgen, "ipv4", dut, \
1557 input_dict, attribute):
1558 Returns
1559 -------
1560 errormsg(str) or True
1561 """
1562
1563 logger.debug("Entering lib API: verify_best_path_as_per_admin_distance()")
1564 router_list = tgen.routers()
1565 if router not in router_list:
1566 return False
1567
1568 rnode = tgen.routers()[router]
1569
1570 sleep(5)
1571 logger.info("Verifying router %s RIB for best path:", router)
1572
1573 # Show ip route cmd
1574 if addr_type == "ipv4":
1575 command = "show ip route json"
1576 else:
1577 command = "show ipv6 route json"
1578
1579 for routes_from_router in input_dict.keys():
1580 sh_ip_route_json = router_list[routes_from_router].vtysh_cmd(
1581 command, isjson=True)
1582 networks = input_dict[routes_from_router]["static_routes"]
1583 for network in networks:
1584 route = network["network"]
1585
1586 route_attributes = sh_ip_route_json[route]
1587 _next_hop = None
1588 compare = None
1589 attribute_dict = {}
1590 for route_attribute in route_attributes:
1591 next_hops = route_attribute["nexthops"]
1592 for next_hop in next_hops:
1593 next_hop_ip = next_hop["ip"]
1594 attribute_dict[next_hop_ip] = route_attribute["distance"]
1595
1596 # Find next_hop for the route have LOWEST Admin Distance
1597 _next_hop = min(attribute_dict, key=(lambda k:
1598 attribute_dict[k]))
1599 compare = "LOWEST"
1600
1601 # Show ip route
1602 rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
1603
1604 # Verifying output dictionary rib_routes_json is not empty
1605 if not bool(rib_routes_json):
1606 errormsg = "No route found in RIB of router {}..".format(router)
1607 return errormsg
1608
1609 st_found = False
1610 nh_found = False
1611 # Find best is installed in RIB
1612 if route in rib_routes_json:
1613 st_found = True
1614 # Verify next_hop in rib_routes_json
1615 if rib_routes_json[route][0]["nexthops"][0]["ip"] == \
1616 _next_hop:
1617 nh_found = True
1618 else:
1619 errormsg = ("Nexthop {} is Missing for BGP route {}"
1620 " in RIB of router {}\n".format(_next_hop,
1621 route, router))
1622 return errormsg
1623
1624 if st_found and nh_found:
1625 logger.info("Best path for prefix: %s is installed according"
1626 " to %s %s: (%s) in RIB of router %s", route,
1627 compare, attribute,
1628 attribute_dict[_next_hop], router)
1629
1630 logger.info(
1631 "Exiting lib API: verify_best_path_as_per_admin_distance()")
1632 return True