2 # Copyright (c) 2019 by VMware, Inc. ("VMware")
3 # Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
4 # ("NetDEF") in this file.
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
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
21 from copy
import deepcopy
22 from time
import sleep
25 from lib
import topotest
27 from lib
.topolog
import logger
29 # Import common_config to use commomnly used APIs
30 from lib
.common_config
import (create_common_configuration
,
32 load_config_to_router
,
35 find_interface_with_greater_ip
,
38 BGP_CONVERGENCE_TIMEOUT
= 10
41 def create_router_bgp(tgen
, topo
, input_dict
=None, build
=False):
43 API to configure bgp on router
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.
58 "router_id": "22.22.22.22",
63 {"redist_type": "static"},
64 {"redist_type": "connected"}
66 "advertise_networks": [
68 "network": "20.0.0.0/32",
72 "network": "30.0.0.0/32",
89 {"name": "RMAP_MED_R3",
109 logger
.debug("Entering lib API: create_router_bgp()")
112 input_dict
= deepcopy(topo
)
114 topo
= topo
["routers"]
115 input_dict
= deepcopy(input_dict
)
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
)
122 data_all_bgp
= __create_bgp_global(tgen
, input_dict
, router
, build
)
124 bgp_data
= input_dict
[router
]["bgp"]
126 bgp_addr_data
= bgp_data
.setdefault("address_family", {})
128 if not bgp_addr_data
:
129 logger
.debug("Router %s: 'address_family' not present in "
130 "input_dict for BGP", router
)
133 ipv4_data
= bgp_addr_data
.setdefault("ipv4", {})
134 ipv6_data
= bgp_addr_data
.setdefault("ipv6", {})
136 neigh_unicast
= True if ipv4_data
.setdefault("unicast", {}) \
137 or ipv6_data
.setdefault("unicast", {}) else False
140 data_all_bgp
= __create_bgp_unicast_neighbor(
141 tgen
, topo
, input_dict
, router
,
142 config_data
=data_all_bgp
)
145 result
= create_common_configuration(tgen
, router
, data_all_bgp
,
147 except InvalidCLIError
:
149 errormsg
= traceback
.format_exc()
150 logger
.error(errormsg
)
153 logger
.debug("Exiting lib API: create_router_bgp()")
157 def __create_bgp_global(tgen
, input_dict
, router
, build
=False):
159 Helper API to create bgp global configuration.
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.
173 logger
.debug("Entering lib API: __create_bgp_global()")
175 bgp_data
= input_dict
[router
]["bgp"]
176 del_bgp_action
= bgp_data
.setdefault("delete", False)
178 config_data
= ["no router bgp"]
184 if "local_as" not in bgp_data
and build
:
185 logger
.error("Router %s: 'local_as' not present in input_dict"
189 local_as
= bgp_data
.setdefault("local_as", "")
190 cmd
= "router bgp {}".format(local_as
)
191 vrf_id
= bgp_data
.setdefault("vrf", None)
193 cmd
= "{} vrf {}".format(cmd
, vrf_id
)
195 config_data
.append(cmd
)
197 router_id
= bgp_data
.setdefault("router_id", None)
198 del_router_id
= bgp_data
.setdefault("del_router_id", False)
200 config_data
.append("no bgp router-id")
202 config_data
.append("bgp router-id {}".format(
208 def __create_bgp_unicast_neighbor(tgen
, topo
, input_dict
, router
,
211 Helper API to create configuration for address-family unicast
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.
222 logger
.debug("Entering lib API: __create_bgp_unicast_neighbor()")
225 if "router bgp" in config_data
:
227 bgp_data
= input_dict
[router
]["bgp"]["address_family"]
229 for addr_type
, addr_dict
in bgp_data
.iteritems():
233 if not check_address_types(addr_type
):
236 addr_data
= addr_dict
["unicast"]
238 config_data
.append("address-family {} unicast".format(
241 advertise_network
= addr_data
.setdefault("advertise_networks",
243 for advertise_network_dict
in advertise_network
:
244 network
= advertise_network_dict
["network"]
245 if type(network
) is not list:
248 if "no_of_network" in advertise_network_dict
:
249 no_of_network
= advertise_network_dict
["no_of_network"]
253 del_action
= advertise_network_dict
.setdefault("delete",
256 # Generating IPs for verification
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
)
263 cmd
= "network {}/{}".format(ip
, prefix
)
265 cmd
= "no {}".format(cmd
)
267 config_data
.append(cmd
)
269 max_paths
= addr_data
.setdefault("maximum_paths", {})
271 ibgp
= max_paths
.setdefault("ibgp", None)
272 ebgp
= max_paths
.setdefault("ebgp", None)
274 config_data
.append("maximum-paths ibgp {}".format(
278 config_data
.append("maximum-paths {}".format(
282 aggregate_addresses
= addr_data
.setdefault("aggregate_address", [])
283 for aggregate_address
in aggregate_addresses
:
284 network
= aggregate_address
.setdefault("network", None)
286 logger
.debug("Router %s: 'network' not present in "
287 "input_dict for BGP", router
)
289 cmd
= "aggregate-address {}".format(network
)
291 as_set
= aggregate_address
.setdefault("as_set", False)
292 summary
= aggregate_address
.setdefault("summary", False)
293 del_action
= aggregate_address
.setdefault("delete", False)
295 cmd
= "{} as-set".format(cmd
)
297 cmd
= "{} summary".format(cmd
)
300 cmd
= "no {}".format(cmd
)
302 config_data
.append(cmd
)
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
)
311 cmd
= "redistribute {}".format(
312 redistribute
["redist_type"])
313 redist_attr
= redistribute
.setdefault("attribute",
316 cmd
= "{} {}".format(cmd
, redist_attr
)
317 del_action
= redistribute
.setdefault("delete", False)
319 cmd
= "no {}".format(cmd
)
320 config_data
.append(cmd
)
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
)
327 for addr_type
, addr_dict
in bgp_data
.iteritems():
328 if not addr_dict
or not check_address_types(addr_type
):
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
)
336 config_data
.extend(neigh_addr_data
)
339 logger
.debug("Exiting lib API: __create_bgp_unicast_neighbor()")
343 def __create_bgp_neighbor(topo
, input_dict
, router
, addr_type
, add_neigh
=True):
345 Helper API to create neighbor specific configuration
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
356 logger
.debug("Entering lib API: __create_bgp_neighbor()")
358 bgp_data
= input_dict
[router
]["bgp"]["address_family"]
359 neigh_data
= bgp_data
[addr_type
]["unicast"]["neighbor"]
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"]
367 if dest_link
in nh_details
["links"].keys():
369 nh_details
["links"][dest_link
][addr_type
].split("/")[0]
371 if "source_link" in peer
and peer
["source_link"] == "lo":
372 update_source
= topo
[router
]["links"]["lo"][
373 addr_type
].split("/")[0]
375 neigh_cxt
= "neighbor {}".format(ip_addr
)
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
))
383 disable_connected
= peer
.setdefault("disable_connected_check",
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)
391 config_data
.append("{} update-source {}".format(
392 neigh_cxt
, update_source
))
393 if disable_connected
:
394 config_data
.append("{} disable-connected-check".format(
397 config_data
.append("{} update-source {}".format(neigh_cxt
,
399 if int(keep_alive
) != 60 and int(hold_down
) != 180:
401 "{} timers {} {}".format(neigh_cxt
, keep_alive
,
405 "{} password {}".format(neigh_cxt
, password
))
407 if max_hop_limit
> 1:
408 config_data
.append("{} ebgp-multihop {}".format(neigh_cxt
,
410 config_data
.append("{} enforce-multihop".format(neigh_cxt
))
412 logger
.debug("Exiting lib API: __create_bgp_unicast_neighbor()")
416 def __create_bgp_unicast_address_family(topo
, input_dict
, router
, addr_type
,
419 API prints bgp global config to bgp_json file.
423 * `bgp_cfg` : BGP class variables have BGP config saved in it for
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
432 logger
.debug("Entering lib API: __create_bgp_unicast_neighbor()")
434 bgp_data
= input_dict
[router
]["bgp"]["address_family"]
435 neigh_data
= bgp_data
[addr_type
]["unicast"]["neighbor"]
437 for peer_name
, peer_dict
in deepcopy(neigh_data
).iteritems():
438 for dest_link
, peer
in peer_dict
["dest_link"].iteritems():
440 nh_details
= topo
[peer_name
]
442 if "source_link" in peer
and peer
["source_link"] == "lo":
443 for destRouterLink
, data
in sorted(nh_details
["links"].
445 if "type" in data
and data
["type"] == "loopback":
446 if dest_link
== destRouterLink
:
448 nh_details
["links"][destRouterLink
][
449 addr_type
].split("/")[0]
453 if dest_link
in nh_details
["links"].keys():
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]
461 neigh_cxt
= "neighbor {}".format(ip_addr
)
462 config_data
.append("address-family {} unicast".format(
467 "no neighbor {} activate".format(deactivate
))
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)
477 config_data
.append("{} next-hop-self".format(neigh_cxt
))
480 config_data
.append("{} send-community".format(neigh_cxt
))
483 if no_send_community
:
484 config_data
.append("no {} send-community {}".format(
485 neigh_cxt
, no_send_community
))
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)
493 logger
.info("Router %s: 'name' not present in "
494 "input_dict for BGP neighbor prefix lists",
497 cmd
= "{} prefix-list {} {}".format(neigh_cxt
, name
,
500 cmd
= "no {}".format(cmd
)
501 config_data
.append(cmd
)
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)
509 logger
.info("Router %s: 'name' not present in "
510 "input_dict for BGP neighbor route name",
513 cmd
= "{} route-map {} {}".format(neigh_cxt
, name
,
516 cmd
= "no {}".format(cmd
)
517 config_data
.append(cmd
)
522 #############################################
524 #############################################
525 @retry(attempts
=3, wait
=2, return_is_str
=True)
526 def verify_router_id(tgen
, topo
, input_dict
):
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
533 3. When loopback intf is down then highest physcial intf
534 should become router-id
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
544 # Verify if router-id for r1 is 12.12.12.12
547 "router_id": "12.12.12.12"
549 # Verify that router-id for r1 is highest interface ip
553 result = verify_router_id(tgen, topo, input_dict)
557 errormsg(str) or True
560 logger
.debug("Entering lib API: verify_router_id()")
561 for router
in input_dict
.keys():
562 if router
not in tgen
.routers():
565 rnode
= tgen
.routers()[router
]
567 del_router_id
= input_dict
[router
]["bgp"].setdefault(
568 "del_router_id", False)
570 logger
.info("Checking router %s router-id", router
)
571 show_bgp_json
= run_frr_cmd(rnode
, "show bgp summary json",
573 router_id_out
= show_bgp_json
["ipv4Unicast"]["routerId"]
574 router_id_out
= ipaddr
.IPv4Address(unicode(router_id_out
))
576 # Once router-id is deleted, highest interface ip should become
579 router_id
= find_interface_with_greater_ip(topo
, router
)
581 router_id
= input_dict
[router
]["bgp"]["router_id"]
582 router_id
= ipaddr
.IPv4Address(unicode(router_id
))
584 if router_id
== router_id_out
:
585 logger
.info("Found expected router-id %s for router %s",
588 errormsg
= "Router-id for router:{} mismatch, expected:" \
589 " {} but found:{}".format(router
, router_id
,
593 logger
.debug("Exiting lib API: verify_router_id()")
597 @retry(attempts
=20, wait
=2, return_is_str
=True)
598 def verify_bgp_convergence(tgen
, topo
):
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,
605 * `tgen`: topogen object
606 * `topo`: input json file data
607 * `addr_type`: ip_type, ipv4/ipv6
610 # To veriry is BGP is converged for all the routers used in
612 results = verify_bgp_convergence(tgen, topo, "ipv4")
615 errormsg(str) or True
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",
623 # Verifying output dictionary show_bgp_json is empty or not
624 if not bool(show_bgp_json
):
625 errormsg
= "BGP is not running"
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
):
635 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
637 for bgp_neighbor
in bgp_neighbors
:
638 total_peer
+= len(bgp_neighbors
[bgp_neighbor
]["dest_link"])
640 for addr_type
in bgp_addr_type
.keys():
641 if not check_address_types(addr_type
):
643 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
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
:
651 data
[dest_link
][addr_type
].split("/")[0]
652 if addr_type
== "ipv4":
653 ipv4_data
= show_bgp_json
["ipv4Unicast"][
655 nh_state
= ipv4_data
[neighbor_ip
]["state"]
657 ipv6_data
= show_bgp_json
["ipv6Unicast"][
659 nh_state
= ipv6_data
[neighbor_ip
]["state"]
661 if nh_state
== "Established":
663 if no_of_peer
== total_peer
:
664 logger
.info("BGP is Converged for router %s", router
)
666 errormsg
= "BGP is not converged for router {}".format(
670 logger
.debug("Exiting API: verify_bgp_convergence()")
674 def modify_as_number(tgen
, topo
, input_dict
):
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.
682 * `tgen` : Topogen object
683 * `topo` : json file data
684 * `input_dict` : defines for which router ASNs needs to be modified
688 To modify ASNs for router r1
695 result = modify_as_number(tgen, topo, input_dict)
699 errormsg(str) or True
702 logger
.debug("Entering lib API: modify_as_number()")
705 new_topo
= deepcopy(topo
["routers"])
707 for router
in input_dict
.keys():
708 # Remove bgp configuration
718 new_topo
[router
]["bgp"]["local_as"] = \
719 input_dict
[router
]["bgp"]["local_as"]
721 logger
.info("Removing bgp configuration")
722 create_router_bgp(tgen
, topo
, router_dict
)
724 logger
.info("Applying modified bgp configuration")
725 create_router_bgp(tgen
, new_topo
)
727 except Exception as e
:
728 # handle any exception
729 logger
.error("Error %s occured. Arguments %s.", e
.message
, e
.args
)
732 errormsg
= traceback
.format_exc()
733 logger
.error(errormsg
)
736 logger
.debug("Exiting lib API: modify_as_number()")
741 @retry(attempts
=3, wait
=2, return_is_str
=True)
742 def verify_as_numbers(tgen
, topo
, input_dict
):
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.
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
764 result = verify_as_numbers(tgen, topo, addr_type, input_dict)
768 errormsg(str) or True
771 logger
.debug("Entering lib API: verify_as_numbers()")
772 for router
in input_dict
.keys():
773 if router
not in tgen
.routers():
776 rnode
= tgen
.routers()[router
]
778 logger
.info("Verifying AS numbers for dut %s:", router
)
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"]
785 for addr_type
in bgp_addr_type
:
786 if not check_address_types(addr_type
):
789 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"][
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():
796 data
= topo
["routers"][bgp_neighbor
]["links"]
798 if dest_link
in data
:
799 neighbor_ip
= data
[dest_link
][addr_type
]. \
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"],
810 logger
.info("Verified local_as for dut %s, found"
811 " expected: %s", router
, local_as
)
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
)
822 logger
.info("Verified remote_as for dut %s's "
823 "neighbor %s, found expected: %s",
824 router
, bgp_neighbor
, remote_as
)
826 logger
.debug("Exiting lib API: verify_AS_numbers()")
830 def clear_bgp_and_verify(tgen
, topo
, router
):
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.
840 * `tgen`: topogen object
841 * `topo`: input json file data
842 * `router`: device under test
846 result = clear_bgp_and_verify(tgen, topo, addr_type, dut)
850 errormsg(str) or True
853 logger
.debug("Entering lib API: clear_bgp_and_verify()")
855 if router
not in tgen
.routers():
858 rnode
= tgen
.routers()[router
]
860 peer_uptime_before_clear_bgp
= {}
861 # Verifying BGP convergence before bgp clear command
862 for retry
in range(31):
864 # Waiting for BGP to converge
865 logger
.info("Waiting for %s sec for BGP to converge on router"
866 " %s...", sleeptime
, router
)
869 show_bgp_json
= run_frr_cmd(rnode
, "show bgp summary json",
871 # Verifying output dictionary show_bgp_json is empty or not
872 if not bool(show_bgp_json
):
873 errormsg
= "BGP is not running"
876 # To find neighbor ip type
877 bgp_addr_type
= topo
["routers"][router
]["bgp"]["address_family"]
879 for addr_type
in bgp_addr_type
.keys():
881 if not check_address_types(addr_type
):
884 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
886 for bgp_neighbor
in bgp_neighbors
:
887 total_peer
+= len(bgp_neighbors
[bgp_neighbor
]["dest_link"])
890 for addr_type
in bgp_addr_type
:
891 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
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"]
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"][
902 nh_state
= ipv4_data
[neighbor_ip
]["state"]
904 # Peer up time dictionary
905 peer_uptime_before_clear_bgp
[bgp_neighbor
] = \
906 ipv4_data
[neighbor_ip
]["peerUptimeEstablishedEpoch"]
908 ipv6_data
= show_bgp_json
["ipv6Unicast"][
910 nh_state
= ipv6_data
[neighbor_ip
]["state"]
912 # Peer up time dictionary
913 peer_uptime_before_clear_bgp
[bgp_neighbor
] = \
914 ipv6_data
[neighbor_ip
]["peerUptimeEstablishedEpoch"]
916 if nh_state
== "Established":
919 if no_of_peer
== total_peer
:
920 logger
.info("BGP is Converged for router %s before bgp"
924 logger
.info("BGP is not yet Converged for router %s "
925 "before bgp clear", router
)
927 errormsg
= "TIMEOUT!! BGP is not converged in 30 seconds for" \
928 " router {}".format(router
)
931 logger
.info(peer_uptime_before_clear_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 *")
940 peer_uptime_after_clear_bgp
= {}
941 # Verifying BGP convergence after bgp clear command
942 for retry
in range(31):
944 # Waiting for BGP to converge
945 logger
.info("Waiting for %s sec for BGP to converge on router"
946 " %s...", sleeptime
, router
)
950 show_bgp_json
= run_frr_cmd(rnode
, "show bgp summary json",
952 # Verifying output dictionary show_bgp_json is empty or not
953 if not bool(show_bgp_json
):
954 errormsg
= "BGP is not running"
957 # To find neighbor ip type
958 bgp_addr_type
= topo
["routers"][router
]["bgp"]["address_family"]
960 for addr_type
in bgp_addr_type
.keys():
961 if not check_address_types(addr_type
):
964 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
966 for bgp_neighbor
in bgp_neighbors
:
967 total_peer
+= len(bgp_neighbors
[bgp_neighbor
]["dest_link"])
970 for addr_type
in bgp_addr_type
:
971 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"]["neighbor"]
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"]
977 if dest_link
in data
:
978 neighbor_ip
= data
[dest_link
][addr_type
].\
980 if addr_type
== "ipv4":
981 ipv4_data
= show_bgp_json
["ipv4Unicast"][
983 nh_state
= ipv4_data
[neighbor_ip
]["state"]
984 peer_uptime_after_clear_bgp
[bgp_neighbor
] = \
985 ipv4_data
[neighbor_ip
]["peerUptimeEstablishedEpoch"]
987 ipv6_data
= show_bgp_json
["ipv6Unicast"][
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"]
994 if nh_state
== "Established":
997 if no_of_peer
== total_peer
:
998 logger
.info("BGP is Converged for router %s after bgp clear",
1002 logger
.info("BGP is not yet Converged for router %s after"
1003 " bgp clear", router
)
1005 errormsg
= "TIMEOUT!! BGP is not converged in 30 seconds for" \
1006 " router {}".format(router
)
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",
1014 errormsg
= "BGP neighborship is not reset after clear bgp on router" \
1015 " {}".format(router
)
1018 logger
.debug("Exiting lib API: clear_bgp_and_verify()")
1022 def verify_bgp_timers_and_functionality(tgen
, topo
, input_dict
):
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.
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
1037 # To verify BGP timers for neighbor r2 of router r1
1043 "keepalivetimer": 5,
1044 "holddowntimer": 15,
1046 result = verify_bgp_timers_and_functionality(tgen, topo, "ipv4",
1051 errormsg(str) or True
1054 logger
.debug("Entering lib API: verify_bgp_timers_and_functionality()")
1056 router_list
= tgen
.routers()
1057 for router
in input_dict
.keys():
1058 if router
not in router_list
:
1061 rnode
= router_list
[router
]
1063 logger
.info("Verifying bgp timers functionality, DUT is %s:",
1066 show_ip_bgp_neighbor_json
= \
1067 run_frr_cmd(rnode
, "show ip bgp neighbor json", isjson
=True)
1069 bgp_addr_type
= input_dict
[router
]["bgp"]["address_family"]
1071 for addr_type
in bgp_addr_type
:
1072 if not check_address_types(addr_type
):
1075 bgp_neighbors
= bgp_addr_type
[addr_type
]["unicast"][
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"]
1081 keepalivetimer
= peer_dict
["keepalivetimer"]
1082 holddowntimer
= peer_dict
["holddowntimer"]
1084 if dest_link
in data
:
1085 neighbor_ip
= data
[dest_link
][addr_type
]. \
1087 neighbor_intf
= data
[dest_link
]["interface"]
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
,
1098 holddowntimer
* 1000)
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)
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 ####################
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
)
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
,
1139 # Bringing up peer interface
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
,
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
)
1155 run_frr_cmd(rnode
, "show bgp summary json",
1158 if addr_type
== "ipv4":
1159 ipv4_data
= show_bgp_json
["ipv4Unicast"]["peers"]
1160 nh_state
= ipv4_data
[neighbor_ip
]["state"]
1162 ipv6_data
= show_bgp_json
["ipv6Unicast"]["peers"]
1163 nh_state
= ipv6_data
[neighbor_ip
]["state"]
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
)
1173 logger
.info("BGP neighborship is intact in %s"
1174 " sec for neighbor %s",
1175 timer
, bgp_neighbor
)
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)
1188 logger
.info("Shutting down interface %s on router %s..",
1189 neighbor_intf
, bgp_neighbor
)
1190 topotest
.interface_set_status(router_list
[bgp_neighbor
],
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
)
1202 run_frr_cmd(rnode
, "show bgp summary json",
1205 if addr_type
== "ipv4":
1206 ipv4_data
= show_bgp_json
["ipv4Unicast"]["peers"]
1207 nh_state
= ipv4_data
[neighbor_ip
]["state"]
1209 ipv6_data
= show_bgp_json
["ipv6Unicast"]["peers"]
1210 nh_state
= ipv6_data
[neighbor_ip
]["state"]
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
)
1219 logger
.info("BGP neighborship has gone down in"
1220 " %s sec for neighbor %s",
1221 timer
, bgp_neighbor
)
1223 logger
.debug("Exiting lib API: verify_bgp_timers_and_functionality()")
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):
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.
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
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
1250 "rmap_match_pf_1_ipv4": [{
1255 "prefix_lists": "pf_list_1_" + addr_type
1263 "rmap_match_pf_2_ipv6": [{
1268 "prefix_lists": "pf_list_1_" + addr_type
1278 result = verify_bgp_attributes(tgen, 'ipv4', "r1", "10.0.20.1/32",
1279 rmap_match_pf_1_ipv4, input_dict)
1283 errormsg(str) or True
1286 logger
.debug("Entering lib API: verify_bgp_attributes()")
1287 for router
, rnode
in tgen
.routers().iteritems():
1291 logger
.info('Verifying BGP set attributes for dut {}:'.format(router
))
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
)
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:
1312 if "seq_id" in rmap_dict
:
1315 for _seq_id
in seq_id
:
1316 if _seq_id
== rmap_seq_id
:
1317 tmp_list
.append(rmap_dict
)
1319 dict_to_test
= tmp_list
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
[
1327 errormsg
= ("BGP attribute: {}"
1336 if rmap_dict
["set"][criteria
] == \
1337 show_bgp_json
["paths"][0][
1339 logger
.info("Verifying BGP "
1353 ("Failed: Verifying BGP "
1354 "attribute {} for route:"
1355 " {} in router: {}, "
1356 " expected value: {} but"
1368 logger
.debug("Exiting lib API: verify_bgp_attributes()")
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
,
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.
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
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
1399 "advertise_networks": [
1401 "network": "200.50.2.0/32"
1404 "network": "200.60.2.0/32"
1413 attribute = "localpref"
1414 result = verify_best_path_as_per_bgp_attribute(tgen, "ipv4", dut, \
1415 input_dict, attribute)
1418 errormsg(str) or True
1421 logger
.debug("Entering lib API: verify_best_path_as_per_bgp_attribute()")
1422 if router
not in tgen
.routers():
1425 rnode
= tgen
.routers()[router
]
1427 command
= "show bgp {} json".format(addr_type
)
1430 logger
.info("Verifying router %s RIB for best path:", router
)
1431 sh_ip_bgp_json
= run_frr_cmd(rnode
, command
, isjson
=True)
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"]
1439 route_attributes
= sh_ip_bgp_json
["routes"][route
]
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
]
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"
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
:
1464 elif attribute
== "weight":
1465 # Find next_hop for the route have highest weight
1466 _next_hop
= max(attribute_dict
, key
=(lambda k
:
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]
1480 elif attribute
== "med":
1481 # Find next_hop for the route have LOWEST MED
1482 _next_hop
= min(attribute_dict
, key
=(lambda k
:
1487 if addr_type
== "ipv4":
1488 command
= "show ip route json"
1490 command
= "show ipv6 route json"
1492 rib_routes_json
= run_frr_cmd(rnode
, command
, isjson
=True)
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 {}..". \
1502 # Find best is installed in RIB
1503 if route
in rib_routes_json
:
1505 # Verify next_hop in rib_routes_json
1506 if rib_routes_json
[route
][0]["nexthops"][0]["ip"] in \
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"],
1518 if st_found
and nh_found
:
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
)
1525 logger
.debug("Exiting lib API: verify_best_path_as_per_bgp_attribute()")
1529 def verify_best_path_as_per_admin_distance(tgen
, addr_type
, router
, input_dict
,
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.
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
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.
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"}]
1555 attribute = "localpref"
1556 result = verify_best_path_as_per_admin_distance(tgen, "ipv4", dut, \
1557 input_dict, attribute):
1560 errormsg(str) or True
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
:
1568 rnode
= tgen
.routers()[router
]
1571 logger
.info("Verifying router %s RIB for best path:", router
)
1574 if addr_type
== "ipv4":
1575 command
= "show ip route json"
1577 command
= "show ipv6 route json"
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"]
1586 route_attributes
= sh_ip_route_json
[route
]
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"]
1596 # Find next_hop for the route have LOWEST Admin Distance
1597 _next_hop
= min(attribute_dict
, key
=(lambda k
:
1602 rib_routes_json
= run_frr_cmd(rnode
, command
, isjson
=True)
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
)
1611 # Find best is installed in RIB
1612 if route
in rib_routes_json
:
1614 # Verify next_hop in rib_routes_json
1615 if rib_routes_json
[route
][0]["nexthops"][0]["ip"] == \
1619 errormsg
= ("Nexthop {} is Missing for BGP route {}"
1620 " in RIB of router {}\n".format(_next_hop
,
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
,
1628 attribute_dict
[_next_hop
], router
)
1631 "Exiting lib API: verify_best_path_as_per_admin_distance()")