From: naveen Date: Tue, 28 Jul 2020 15:02:17 +0000 (+0530) Subject: tests: ospf_basic_functionality topojson testcases. X-Git-Tag: base_7.6~537^2~4 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=4256a20961308bda6528ee1313063ac4396e6e94;p=matthieu%2Ffrr.git tests: ospf_basic_functionality topojson testcases. 1. Adding 18 ospf testcases to topojson. 2. Adding ospf.py library. Test suite execution time is ~18 minutes. Signed-off-by: naveen --- diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 45a86c7a3f..09e7fa7873 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -258,6 +258,7 @@ def create_common_configuration( "route_maps": "! Route Maps Config\n", "bgp": "! BGP Config\n", "vrf": "! VRF Config\n", + "ospf": "! OSPF Config\n", } ) @@ -739,6 +740,11 @@ def start_topology(tgen): # Loading empty bgpd.conf file to router, to start the bgp deamon router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname)) + # Loading empty ospf.conf file to router, to start the bgp deamon + router.load_config( + TopoRouter.RD_OSPF, + '{}/{}/ospfd.conf'.format(TMPDIR, rname) + ) # Starting routers logger.info("Starting all routers once topology is created") tgen.start_router() @@ -1394,6 +1400,58 @@ def create_interfaces_cfg(tgen, topo, build=False): else: interface_data.append("ipv6 address {}\n".format(intf_addr)) + if 'ospf' in data: + ospf_data = data['ospf'] + if 'area' in ospf_data: + intf_ospf_area = c_data["links"][destRouterLink][ + "ospf"]["area"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf area") + else: + interface_data.append("ip ospf area {}".format( + intf_ospf_area + )) + + if "hello_interval" in ospf_data: + intf_ospf_hello = c_data["links"][destRouterLink][ + "ospf"]["hello_interval"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf "\ + " hello-interval") + else: + interface_data.append("ip ospf "\ + " hello-interval {}".format(intf_ospf_hello)) + + if "dead_interval" in ospf_data: + intf_ospf_dead = c_data["links"][destRouterLink][ + "ospf"]["dead_interval"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " dead-interval") + else: + interface_data.append("ip ospf "\ + " dead-interval {}".format(intf_ospf_dead)) + + if "network" in ospf_data: + intf_ospf_nw = c_data["links"][destRouterLink][ + "ospf"]["network"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " network {}".format(intf_ospf_nw)) + else: + interface_data.append("ip ospf"\ + " network {}".format(intf_ospf_nw)) + + if "priority" in ospf_data: + intf_ospf_nw = c_data["links"][destRouterLink][ + "ospf"]["priority"] + + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " priority") + else: + interface_data.append("ip ospf"\ + " priority {}".format(intf_ospf_nw)) result = create_common_configuration( tgen, c_router, interface_data, "interface_config", build=build ) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py new file mode 100644 index 0000000000..f6a02bd890 --- /dev/null +++ b/tests/topotests/lib/ospf.py @@ -0,0 +1,1182 @@ +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +from copy import deepcopy +import traceback +from time import sleep +from lib.topolog import logger +import ipaddr + + +# Import common_config to use commomnly used APIs +from lib.common_config import (create_common_configuration, + InvalidCLIError, retry, + generate_ips, + check_address_types, + validate_ip_address, + run_frr_cmd) + +LOGDIR = "/tmp/topotests/" +TMPDIR = None + +################################ +# Configure procs +################################ + +def create_router_ospf( + tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "ospf": { + "router_id": "22.22.22.22", + "area": [{ "id":0.0.0.0, "type": "nssa"}] + } + } + + result = create_router_ospf(tgen, topo, input_dict) + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: create_router_ospf()") + result = False + + if not input_dict: + input_dict = deepcopy(topo) + else: + topo = topo["routers"] + input_dict = deepcopy(input_dict) + + for router in input_dict.keys(): + if "ospf" not in input_dict[router]: + logger.debug("Router %s: 'ospf' not present in input_dict", router) + continue + + result = __create_ospf_global( + tgen, input_dict, router, build, load_config) + if result is True: + ospf_data = input_dict[router]["ospf"] + + + logger.debug("Exiting lib API: create_router_ospf()") + return result + + +def __create_ospf_global( + tgen, input_dict, router, build=False, + load_config=True): + """ + Helper API to create ospf global configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router to be configured. + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: __create_ospf_global()") + try: + + ospf_data = input_dict[router]["ospf"] + del_ospf_action = ospf_data.setdefault("delete", False) + if del_ospf_action: + config_data = ["no router ospf"] + result = create_common_configuration(tgen, router, config_data, + "ospf", build, + load_config) + return result + + config_data = [] + cmd = "router ospf" + + config_data.append(cmd) + + # router id + router_id = ospf_data.setdefault("router_id", None) + del_router_id = ospf_data.setdefault("del_router_id", False) + if del_router_id: + config_data.append("no ospf router-id") + if router_id: + config_data.append("ospf router-id {}".format( + router_id)) + + # redistribute command + redistribute_data = ospf_data.setdefault("redistribute", {}) + if redistribute_data: + for redistribute in redistribute_data: + if "redist_type" not in redistribute: + logger.debug("Router %s: 'redist_type' not present in " + "input_dict", router) + else: + cmd = "redistribute {}".format( + redistribute["redist_type"]) + for red_type in redistribute_data: + if "route_map" in red_type: + cmd = cmd + " route-map {}".format(red_type[ + 'route_map']) + del_action = redistribute.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + #area information + area_data = ospf_data.setdefault("area", {}) + if area_data: + for area in area_data: + if "id" not in area: + logger.debug("Router %s: 'area id' not present in " + "input_dict", router) + else: + cmd = "area {}".format(area["id"]) + + if "type" in area: + cmd = cmd + " {}".format(area["type"]) + + del_action = area.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + + # summary information + summary_data = ospf_data.setdefault("summary-address", {}) + if summary_data: + for summary in summary_data: + if "prefix" not in summary: + logger.debug("Router %s: 'summary-address' not present in " + "input_dict", router) + else: + cmd = "summary {}/{}".format(summary["prefix"], summary[ + "mask"]) + + _tag = summary.setdefault("tag", None) + if _tag: + cmd = "{} tag {}".format(cmd, _tag) + + _advertise = summary.setdefault("advertise", True) + if not _advertise: + cmd = "{} no-advertise".format(cmd) + + del_action = summary.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_ospf_global()") + return result + + +def create_router_ospf6( + tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "ospf6": { + "router_id": "22.22.22.22", + } + } + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: create_router_ospf()") + result = False + + if not input_dict: + input_dict = deepcopy(topo) + else: + topo = topo["routers"] + input_dict = deepcopy(input_dict) + for router in input_dict.keys(): + if "ospf" not in input_dict[router]: + logger.debug("Router %s: 'ospf' not present in input_dict", router) + continue + + result = __create_ospf_global( + tgen, input_dict, router, build, load_config) + + logger.debug("Exiting lib API: create_router_ospf()") + return result + + +def __create_ospf6_global( + tgen, input_dict, router, build=False, + load_config=True): + """ + Helper API to create ospf global configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured. + * `build` : Only for initial setup phase this is set as True. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: __create_ospf_global()") + try: + + ospf_data = input_dict[router]["ospf6"] + del_ospf_action = ospf_data.setdefault("delete", False) + if del_ospf_action: + config_data = ["no ipv6 router ospf"] + result = create_common_configuration(tgen, router, config_data, + "ospf", build, + load_config) + return result + + config_data = [] + cmd = "router ospf" + + config_data.append(cmd) + + router_id = ospf_data.setdefault("router_id", None) + del_router_id = ospf_data.setdefault("del_router_id", False) + if del_router_id: + config_data.append("no ospf router-id") + if router_id: + config_data.append("ospf router-id {}".format( + router_id)) + + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_ospf_global()") + return result + +def config_ospf_interface (tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Usage + ----- + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": 'message-digest', + "authentication-key": "ospf", + "message-digest-key": "10" + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + + Returns + ------- + True or False + """ + logger.debug("Enter lib config_ospf_interface") + if not input_dict: + input_dict = deepcopy(topo) + else: + input_dict = deepcopy(input_dict) + for router in input_dict.keys(): + config_data = [] + for lnk in input_dict[router]['links'].keys(): + if "ospf" not in input_dict[router]['links'][lnk]: + logger.debug("Router %s: ospf configs is not present in" + "input_dict, passed input_dict", router, + input_dict) + continue + ospf_data = input_dict[router]['links'][lnk]['ospf'] + data_ospf_area = ospf_data.setdefault("area", None) + data_ospf_auth = ospf_data.setdefault("authentication", None) + data_ospf_dr_priority = ospf_data.setdefault("priority", None) + data_ospf_cost = ospf_data.setdefault("cost", None) + + try: + intf = topo['routers'][router]['links'][lnk]['interface'] + except KeyError: + intf = topo['switches'][router]['links'][lnk]['interface'] + + # interface + cmd = "interface {}".format(intf) + + config_data.append(cmd) + # interface area config + if data_ospf_area: + cmd = "ip ospf area {}".format(data_ospf_area) + config_data.append(cmd) + # interface ospf auth + if data_ospf_auth: + if data_ospf_auth == 'null': + cmd = "ip ospf authentication null" + elif data_ospf_auth == 'message-digest': + cmd = "ip ospf authentication message-digest" + else: + cmd = "ip ospf authentication" + + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if "message-digest-key" in ospf_data: + cmd = "ip ospf message-digest-key {} md5 {}".format( + ospf_data["message-digest-key"],ospf_data[ + "authentication-key"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if "authentication-key" in ospf_data and \ + "message-digest-key" not in ospf_data: + cmd = "ip ospf authentication-key {}".format(ospf_data[ + "authentication-key"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # interface ospf dr priority + if data_ospf_dr_priority in ospf_data: + cmd = "ip ospf priority {}".format( + ospf_data["priority"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # interface ospf cost + if data_ospf_cost in ospf_data: + cmd = "ip ospf cost {}".format( + ospf_data["cost"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if build: + return config_data + else: + result = create_common_configuration(tgen, router, config_data, + "interface_config", + build=build) + logger.debug("Exiting lib API: create_igmp_config()") + return result + +def clear_ospf(tgen, router): + """ + This API is to clear ospf neighborship by running + clear ip ospf interface * command, + + Parameters + ---------- + * `tgen`: topogen object + * `router`: device under test + + Usage + ----- + clear_ospf(tgen, "r1") + """ + + logger.debug("Entering lib API: clear_ospf()") + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + # Clearing OSPF + logger.info("Clearing ospf process for router %s..", router) + + run_frr_cmd(rnode, "clear ip ospf interface ") + + logger.debug("Exiting lib API: clear_ospf()") + + +################################ +# Verification procs +################################ +@retry(attempts=40, wait=2, return_is_str=True) +def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): + """ + This API is to verify ospf neighborship by running + show ip ospf neighbour command, + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `lan` : verify neighbors in lan topology + + Usage + ----- + 1. To check FULL neighbors. + verify_ospf_neighbor(tgen, topo, dut=dut) + + 2. To check neighbors with their roles. + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": { + "state": "Full", + "role": "DR" + }, + "r2": { + "state": "Full", + "role": "DROther" + }, + "r3": { + "state": "Full", + "role": "DROther" + } + } + } + } + } + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + + Returns + ------- + True or False (Error Message) + """ + logger.debug("Entering lib API: verify_ospf_neighbor()") + result = False + if input_dict: + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF neighborship on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, + "show ip ospf neighbor all json", isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + ospf_data_list = input_dict[router]["ospf"] + ospf_nbr_list = ospf_data_list['neighbors'] + + for ospf_nbr, nbr_data in ospf_nbr_list.items(): + data_ip = topo['routers'][ospf_nbr]['links'] + data_rid = topo['routers'][ospf_nbr]['ospf']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip[switch]['ipv4'].split("/")[0] + else: + continue + else: + neighbor_ip = data_ip[router]['ipv4'].split("/")[0] + + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + try: + nh_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[0] + intf_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[1] + except KeyError: + errormsg = "[DUT: {}] OSPF peer {} missing".format(router, + nbr_rid) + return errormsg + + nbr_state = nbr_data.setdefault("state",None) + nbr_role = nbr_data.setdefault("role",None) + + if nbr_state: + if nbr_state == nh_state: + logger.info("[DUT: {}] OSPF Nbr is {}:{} State {}".format + (router, ospf_nbr, nbr_rid, nh_state)) + result = True + else: + errormsg = ("[DUT: {}] OSPF is not Converged, neighbor" + " state is {}".format(router, nh_state)) + return errormsg + if nbr_role: + if nbr_role == intf_state: + logger.info("[DUT: {}] OSPF Nbr is {}: {} Role {}".format( + router, ospf_nbr, nbr_rid, nbr_role)) + else: + errormsg = ("[DUT: {}] OSPF is not Converged with rid" + "{}, role is {}".format(router, nbr_rid, intf_state)) + return errormsg + continue + else: + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF neighborship on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, + "show ip ospf neighbor all json", isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + ospf_data_list = topo["routers"][router]["ospf"] + ospf_neighbors = ospf_data_list['neighbors'] + total_peer = 0 + total_peer = len(ospf_neighbors.keys()) + no_of_ospf_nbr = 0 + ospf_nbr_list = ospf_data_list['neighbors'] + no_of_peer = 0 + for ospf_nbr, nbr_data in ospf_nbr_list.items(): + if nbr_data: + data_ip = topo['routers'][nbr_data["nbr"]]['links'] + data_rid = topo['routers'][nbr_data["nbr"]][ + 'ospf']['router_id'] + else: + data_ip = topo['routers'][ospf_nbr]['links'] + data_rid = topo['routers'][ospf_nbr]['ospf']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip[switch]['ipv4'].split("/")[0] + else: + continue + else: + neighbor_ip = data_ip[router]['ipv4'].split("/")[0] + + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + try: + nh_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[0] + except KeyError: + errormsg = "[DUT: {}] OSPF peer {} missing,from "\ + "{} ".format(router, + nbr_rid, ospf_nbr) + return errormsg + + if nh_state == 'Full': + no_of_peer += 1 + + if no_of_peer == total_peer: + logger.info("[DUT: {}] OSPF is Converged".format(router)) + result = True + else: + errormsg = ("[DUT: {}] OSPF is not Converged".format(router)) + return errormsg + + logger.debug("Exiting API: verify_ospf_neighbor()") + return result + +@retry(attempts=20, wait=2, return_is_str=True, initial_wait=2) +def verify_ospf_rib(tgen, dut, input_dict, next_hop=None, + tag=None, metric=None, fib=None): + """ + This API is to verify ospf routes by running + show ip ospf route command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `next_hop` : next to be verified + * `tag` : tag to be verified + * `metric` : metric to be verified + * `fib` : True if the route is installed in FIB. + + Usage + ----- + input_dict = { + "r1": { + "static_routes": [ + { + "network": ip_net, + "no_of_ip": 1, + "routeType": "N" + } + ] + } + } + + result = verify_ospf_rib(tgen, dut, input_dict,next_hop=nh) + + Returns + ------- + True or False (Error Message) + """ + + logger.info("Entering lib API: verify_ospf_rib()") + result = False + router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + found_hops = [] + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s RIB:", router) + + # Verifying RIB routes + command = "show ip ospf route" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput] or \ + "prefix" in input_dict[routerInput]: + if "prefix" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["prefix"] + else: + static_routes = input_dict[routerInput]["static_routes"] + + + for static_route in static_routes: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary ospf_rib_json is not empty + if bool(ospf_rib_json) is False: + errormsg = "[DUT: {}] No routes found in OSPF route " \ + "table".format(router) + return errormsg + + network = static_route["network"] + no_of_ip = static_route.setdefault("no_of_ip", 1) + _tag = static_route.setdefault("tag", None) + _rtype = static_route.setdefault("routeType", None) + + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != 'ipv4': + continue + + if st_rt in ospf_rib_json: + st_found = True + found_routes.append(st_rt) + + if fib and next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + for mnh in range(0, len(ospf_rib_json[st_rt])): + if 'fib' in ospf_rib_json[st_rt][ + mnh]["nexthops"][0]: + found_hops.append([rib_r[ + "ip"] for rib_r in ospf_rib_json[ + st_rt][mnh]["nexthops"]]) + + if found_hops[0]: + missing_list_of_nexthops = \ + set(found_hops[0]).difference(next_hop) + additional_nexthops_in_required_nhs = \ + set(next_hop).difference(found_hops[0]) + + if additional_nexthops_in_required_nhs: + logger.info( + "Nexthop " + "%s is not active for route %s in " + "RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, dut) + errormsg = ( + "Nexthop {} is not active" + " for route {} in RIB of router" + " {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, dut)) + return errormsg + else: + nh_found = True + + elif next_hop and fib is None: + if type(next_hop) is not list: + next_hop = [next_hop] + found_hops = [rib_r["ip"] for rib_r in + ospf_rib_json[st_rt][ + "nexthops"]] + + if found_hops: + missing_list_of_nexthops = \ + set(found_hops).difference(next_hop) + additional_nexthops_in_required_nhs = \ + set(next_hop).difference(found_hops) + + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route"\ + " %s in RIB of router %s\n", \ + additional_nexthops_in_required_nhs, \ + st_rt, dut) + errormsg=("Nexthop {} is Missing for "\ + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, dut)) + return errormsg + else: + nh_found = True + if _rtype: + if "routeType" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: routeType missing" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + elif _rtype != ospf_rib_json[st_rt][ + "routeType"]: + errormsg = ("[DUT: {}]: routeType mismatch" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + else: + logger.info("DUT: {}]: Found routeType {}" + "for route {}".\ + format(dut, _rtype, st_rt)) + if tag: + if "tag" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: tag is not" + " present for" + " route {} in RIB \n".\ + format(dut, st_rt + )) + return errormsg + + if _tag != ospf_rib_json[ + st_rt]["tag"]: + errormsg = ("[DUT: {}]: tag value {}" + " is not matched for" + " route {} in RIB \n".\ + format(dut, _tag, st_rt, + )) + return errormsg + + if metric is not None: + if "type2cost" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: metric is" + " not present for" + " route {} in RIB \n".\ + format(dut, st_rt)) + return errormsg + + if metric != ospf_rib_json[ + st_rt]["type2cost"]: + errormsg = ("[DUT: {}]: metric value " + "{} is not matched for " + "route {} in RIB \n".\ + format(dut, metric, st_rt, + )) + return errormsg + + else: + missing_routes.append(st_rt) + + if nh_found: + logger.info("[DUT: {}]: Found next_hop {} for all OSPF" + " routes in RIB".format(router, next_hop)) + + if len(missing_routes) > 0: + errormsg = ("[DUT: {}]: Missing route in RIB, " + "routes: {}".\ + format(dut, missing_routes)) + return errormsg + + if found_routes: + logger.info("[DUT: %s]: Verified routes in RIB, found" + " routes are: %s\n", dut, found_routes) + result = True + + logger.info("Exiting lib API: verify_ospf_rib()") + return result + + +@retry(attempts=10, wait=2, return_is_str=True) +def verify_ospf_interface(tgen, topo, dut=None,lan=False, input_dict=None): + """ + This API is to verify ospf routes by running + show ip ospf interface command. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : topology descriptions + * `dut`: device under test + * `lan`: if set to true this interface belongs to LAN. + * `input_dict` : Input dict data, required when configuring from testcase + + Usage + ----- + input_dict= { + 'r0': { + 'links':{ + 's1': { + 'ospf':{ + 'priority':98, + 'timerDeadSecs': 4, + 'area': '0.0.0.3', + 'mcastMemberOspfDesignatedRouters': True, + 'mcastMemberOspfAllRouters': True, + 'ospfEnabled': True, + + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: verify_ospf_interface()") + result = False + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF interface on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, "show ip ospf interface json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # To find neighbor ip type + ospf_intf_data = input_dict[router]["links"] + for ospf_intf, intf_data in ospf_intf_data.items(): + intf = topo['routers'][router]['links'][ospf_intf]['interface'] + if intf in show_ospf_json['interfaces']: + for intf_attribute in intf_data['ospf']: + if intf_data['ospf'][intf_attribute] == show_ospf_json[ + 'interfaces'][intf][intf_attribute]: + logger.info("[DUT: %s] OSPF interface %s: %s is %s", + router, intf, intf_attribute, intf_data['ospf'][ + intf_attribute]) + else: + errormsg= "[DUT: {}] OSPF interface {}: {} is {}, \ + Expected is {}".format(router, intf, intf_attribute, + intf_data['ospf'][intf_attribute], show_ospf_json[ + 'interfaces'][intf][intf_attribute]) + return errormsg + result = True + logger.debug("Exiting API: verify_ospf_interface()") + return result + + +@retry(attempts=10, wait=2, return_is_str=True, initial_wait=2) +def verify_ospf_database(tgen, topo, dut, input_dict): + """ + This API is to verify ospf lsa's by running + show ip ospf database command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `topo` : next to be verified + + Usage + ----- + input_dict = { + "areas": { + "0.0.0.0": { + "Router Link States": { + "100.1.1.0-100.1.1.0": { + "LSID": "100.1.1.0", + "Advertised router": "100.1.1.0", + "LSA Age": 130, + "Sequence Number": "80000006", + "Checksum": "a703", + "Router links": 3 + } + }, + "Net Link States": { + "10.0.0.2-100.1.1.1": { + "LSID": "10.0.0.2", + "Advertised router": "100.1.1.1", + "LSA Age": 137, + "Sequence Number": "80000001", + "Checksum": "9583" + } + }, + }, + } + } + result = verify_ospf_database(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + result = False + router = dut + logger.debug("Entering lib API: verify_ospf_database()") + + if 'ospf' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format( + dut) + return errormsg + + rnode = tgen.routers()[dut] + + logger.info("Verifying OSPF interface on router %s:", dut) + show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", + isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # for inter and inter lsa's + ospf_db_data = input_dict.setdefault("areas", None) + ospf_external_lsa = input_dict.setdefault( + 'AS External Link States', None) + if ospf_db_data: + for ospf_area, area_lsa in ospf_db_data.items(): + if ospf_area in show_ospf_json['areas']: + if 'Router Link States' in area_lsa: + for lsa in area_lsa['Router Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Router Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Router LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'Net Link States' in area_lsa: + for lsa in area_lsa['Net Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Net Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Network LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'Summary Link States' in area_lsa: + for lsa in area_lsa['Summary Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Summary Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Summary LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'ASBR-Summary Link States' in area_lsa: + for lsa in area_lsa['ASBR-Summary Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'ASBR-Summary Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " ASBR Summary LSA is {}".format( + router, ospf_area, lsa) + return errormsg + if ospf_external_lsa: + for ospf_ext_lsa, ext_lsa_data in ospf_external_lsa.items(): + if ospf_ext_lsa in show_ospf_json['AS External Link States']: + logger.info( + "[DUT: %s] OSPF LSDB:External LSA %s", + router, ospf_ext_lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB : expected" \ + " External LSA is {}".format(router, ospf_ext_lsa) + return errormsg + + logger.debug("Exiting API: verify_ospf_database()") + return result + + + +@retry(attempts=10, wait=2, return_is_str=True) +def verify_ospf_summary(tgen, topo, dut, input_dict): + """ + This API is to verify ospf routes by running + show ip ospf interface command. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : topology descriptions + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + + Usage + ----- + input_dict = { + "11.0.0.0/8": { + "Summary address": "11.0.0.0/8", + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + result = verify_ospf_summary(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: verify_ospf_summary()") + result = False + router = dut + + logger.info("Verifying OSPF summary on router %s:", router) + + if 'ospf' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format( + router) + return errormsg + + rnode = tgen.routers()[dut] + show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # To find neighbor ip type + ospf_summary_data = input_dict + for ospf_summ, summ_data in ospf_summary_data.items(): + if ospf_summ not in show_ospf_json: + continue + summary = ospf_summary_data[ospf_summ]['Summary address'] + if summary in show_ospf_json: + for summ in summ_data: + if summ_data[summ] == show_ospf_json[summary][summ]: + logger.info("[DUT: %s] OSPF summary %s:%s is %s", + router, summary, summ, summ_data[summ]) + result = True + else: + errormsg = ("[DUT: {}] OSPF summary {}:{} is %s, " + "Expected is {}".format(router,summary, summ, + show_ospf_json[summary][summ])) + return errormsg + + logger.debug("Exiting API: verify_ospf_summary()") + return result diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 9c2baedde4..6d97ab2e90 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -23,6 +23,9 @@ from json import dumps as json_dumps from re import search as re_search import ipaddress import pytest +import ipaddr +from copy import deepcopy + # Import topogen and topotest helpers from lib.topolog import logger @@ -41,7 +44,7 @@ from lib.common_config import ( ) from lib.bgp import create_router_bgp - +from lib.ospf import create_router_ospf ROUTER_LIST = [] @@ -58,12 +61,27 @@ def build_topo_from_json(tgen, topo): topo["routers"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) ) + SWITCH_LIST = [] + if "switches" in topo: + SWITCH_LIST = sorted( + topo["switches"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) + ) + listRouters = ROUTER_LIST[:] + listSwitches = SWITCH_LIST[:] + listAllRouters = deepcopy(listRouters) + dictSwitches = {} + for routerN in ROUTER_LIST: logger.info("Topo: Add router {}".format(routerN)) tgen.add_router(routerN) listRouters.append(routerN) + for switchN in SWITCH_LIST: + logger.info("Topo: Add switch {}".format(switchN)) + dictSwitches[switchN] = tgen.add_switch(switchN) + listSwitches.append(switchN) + if "ipv4base" in topo: ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"]) @@ -191,6 +209,72 @@ def build_topo_from_json(tgen, topo): ), ) + switch_count = 0 + add_switch_to_topo = [] + while listSwitches != []: + curSwitch = listSwitches.pop(0) + # Physical Interfaces + if "links" in topo['switches'][curSwitch]: + for destRouterLink, data in sorted( + topo['switches'][curSwitch]['links'].iteritems()): + + # Loopback interfaces + if "dst_node" in data: + destRouter = data['dst_node'] + + elif "-" in destRouterLink: + # Spliting and storing destRouterLink data in tempList + tempList = destRouterLink.split("-") + # destRouter + destRouter = tempList.pop(0) + else: + destRouter = destRouterLink + + if destRouter in listAllRouters: + + topo['routers'][destRouter]['links'][curSwitch] = \ + deepcopy(topo['switches'][curSwitch]['links'][destRouterLink]) + + # Assigning name to interfaces + topo['routers'][destRouter]['links'][curSwitch]['interface'] = \ + '{}-{}-eth{}'.format(destRouter, curSwitch, topo['routers'] \ + [destRouter]['nextIfname']) + + topo['switches'][curSwitch]['links'][destRouter]['interface'] = \ + '{}-{}-eth{}'.format(curSwitch, destRouter, topo['routers'] \ + [destRouter]['nextIfname']) + + topo['routers'][destRouter]['nextIfname'] += 1 + + # Add links + dictSwitches[curSwitch].add_link(tgen.gears[destRouter], \ + topo['switches'][curSwitch]['links'][destRouter]['interface'], + topo['routers'][destRouter]['links'][curSwitch]['interface'], + ) + + # IPv4 + if 'ipv4' in topo['routers'][destRouter]['links'][curSwitch]: + if topo['routers'][destRouter]['links'][curSwitch]['ipv4'] == 'auto': + topo['routers'][destRouter]['links'][curSwitch]['ipv4'] = \ + '{}/{}'.format(ipv4Next, topo['link_ip_start'][ \ + 'v4mask']) + ipv4Next += 1 + # IPv6 + if 'ipv6' in topo['routers'][destRouter]['links'][curSwitch]: + if topo['routers'][destRouter]['links'][curSwitch]['ipv6'] == 'auto': + topo['routers'][destRouter]['links'][curSwitch]['ipv6'] = \ + '{}/{}'.format(ipv6Next, topo['link_ip_start'][ \ + 'v6mask']) + ipv6Next = ipaddr.IPv6Address(int(ipv6Next) + ipv6Step) + + logger.debug( + "Generated link data for router: %s\n%s", + curRouter, + json_dumps( + topo["routers"][curRouter]["links"], indent=4, sort_keys=True + ), + ) + def build_config_from_json(tgen, topo, save_bkup=True): """ @@ -210,6 +294,7 @@ def build_config_from_json(tgen, topo, save_bkup=True): ("bgp_community_list", create_bgp_community_lists), ("route_maps", create_route_maps), ("bgp", create_router_bgp), + ("ospf", create_router_ospf) ] ) diff --git a/tests/topotests/ospf_basic_functionality/ospf_authentication.json b/tests/topotests/ospf_basic_functionality/ospf_authentication.json new file mode 100644 index 0000000000..4edb9f25a8 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_authentication.json @@ -0,0 +1,166 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json new file mode 100644 index 0000000000..ea5b9a4655 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json @@ -0,0 +1,342 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r1-link1": { + "nbr": "r1" + }, + "r1-link2": { + "nbr": "r1" + }, + "r1-link3": { + "nbr": "r1" + }, + "r1-link4": { + "nbr": "r1" + }, + "r1-link5": { + "nbr": "r1" + }, + "r1-link6": { + "nbr": "r1" + }, + "r1-link7": { + "nbr": "r1" + }, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r0-link1": { + "nbr": "r0" + }, + "r0-link2": { + "nbr": "r0" + }, + "r0-link3": { + "nbr": "r0" + }, + "r0-link4": { + "nbr": "r0" + }, + "r0-link5": { + "nbr": "r0" + }, + "r0-link6": { + "nbr": "r0" + }, + "r0-link7": { + "nbr": "r0" + }, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json new file mode 100644 index 0000000000..ecfd35d639 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json @@ -0,0 +1,234 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "switches": { + "s1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 98 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 99 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + } + } + } + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r0": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.4", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.5", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.6", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r7": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.7", + "neighbors": { + "r0": {}, + "r1": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_lan.json b/tests/topotests/ospf_basic_functionality/ospf_lan.json new file mode 100644 index 0000000000..126934c344 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_lan.json @@ -0,0 +1,138 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "switches": { + "s1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 98 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 99 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + } + } + } + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r0": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_nssa.json b/tests/topotests/ospf_basic_functionality/ospf_nssa.json new file mode 100644 index 0000000000..f95a297bb3 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_nssa.json @@ -0,0 +1,188 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto" + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_routemaps.json b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json new file mode 100644 index 0000000000..60a8434d42 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json @@ -0,0 +1,159 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json new file mode 100644 index 0000000000..9062a09091 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json @@ -0,0 +1,168 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_single_area.json b/tests/topotests/ospf_basic_functionality/ospf_single_area.json new file mode 100644 index 0000000000..c595912887 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_single_area.json @@ -0,0 +1,188 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py new file mode 100644 index 0000000000..5f6412c997 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -0,0 +1,891 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from time import sleep +from copy import deepcopy +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + step, + shutdown_bringup_interface, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import verify_ospf_neighbor, config_ospf_interface, clear_ospf +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_authentication.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify ospf authentication with Simple password authentication. +2. Verify ospf authentication with MD5 authentication. +3. Verify ospf authentication with different authentication methods. + + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_authentication_simple_pass_tc28_p1(request): + """ + OSPF Authentication - Verify ospf authentication with Simple + password authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface" + "connected to R2 with simple password authentication using ip ospf " + "authentication Simple password cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("clear ip ospf after configuring the authentication.") + clear_ospf(tgen, "r1") + + step("Verify that the neighbour is not FULL between R1 and R2.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with simple password authentication " + "using ip ospf authentication Simple password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Disable simple password authentication on R2 using no ip ospf " + "authentication Simple password cmd." + ) + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": True, + "authentication-key": "ospf", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 neighbour is deleted for R2 after dead interval expiry") + + step("Waiting for dead time expiry....") + sleep(10) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with Simple password auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + sleep(5) + shutdown_bringup_interface(tgen, dut, intf, True) + + # clear ip ospf after configuring the authentication. + clear_ospf(tgen, "r1") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_md5_tc29_p1(request): + """ + OSPF Authentication - Verify ospf authentication with MD5 authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication message-digest cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Wait for 2 dead interval timer to check neighbor. + sleep(8) + + step("Verify that the neighbour is not FULL between R1 and R2.") + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Disable message-digest authentication on R2 using no ip ospf " + "authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry") + + step("Waiting for dead time expiry....") + sleep(10) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with message-digest auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + sleep(5) + shutdown_bringup_interface(tgen, dut, intf, True) + clear_ospf(tgen, "r1") + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_different_auths_tc30_p1(request): + """ + OSPF Authentication - Verify ospf authentication with different + authentication methods. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication message-digest cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Wait for 2 dead interval timer to check neighbor. + sleep(8) + # clear ip ospf after configuring the authentication. + # clear_ospf(tgen, "r1") + + step("Verify that the neighbour is not FULL between R1 and R2.") + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Delete the configured password on both the routers.") + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is FULL" + " between R1 and R2 using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the authentication type to simple password.") + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the password in simple password.") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Delete the password authentication on the interface ") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": True, + "authentication-key": "OSPFv4", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": True, + "authentication-key": "OSPFv4", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Enable Md5 authentication on the interface") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the MD5 authentication password") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "OSPFv4", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "OSPFv4", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py new file mode 100644 index 0000000000..51be52bd26 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -0,0 +1,471 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from ipaddress import IPv4Address + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + create_route_maps, + shutdown_bringup_interface, + create_interfaces_cfg, +) +from lib.topolog import logger + +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) + +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_ecmp.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +""" +TOPOLOGY : + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES : +1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level) +2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_ecmp_tc16_p0(request): + """ + Verify OSPF ECMP. + + Verify OSPF ECMP with max path configured as 8 (ECMP + configured at FRR level) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 8 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Configure a static route in R0 and redistribute in OSPF.") + + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that route in R2 in stalled with 8 next hops.") + nh = [] + for item in range(1, 7): + nh.append(topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0]) + + nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + + nh.append(nh2) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("shut no shut all the interfaces on the remote router - R2") + dut = "r1" + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("shut no shut on all the interfaces on DUT (r1)") + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that all the neighbours are up and routes are installed" + " with 8 next hop in ospf and ip route tables on R1." + ) + + step("Verify that OSPF is up with 8 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure static route on R0") + + dut = "r0" + red_static(dut, config=False) + + # Wait for R0 to flush external LSAs. + sleep(10) + + step("Verify that route is withdrawn from R2.") + dut = "r1" + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Re configure the static route in R0.") + dut = "r0" + red_static(dut) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_ecmp_tc17_p0(request): + """ + Verify OSPF ECMP. + + Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 2 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Configure a static route in R0 and redistribute in OSPF.") + + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that route in R2 in stalled with 2 next hops.") + + nh1 = topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0] + nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + nh = [nh1, nh2] + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure static route on R0") + + dut = "r0" + red_static(dut, config=False) + # sleep till the route gets withdrawn + sleep(10) + + step("Verify that route is withdrawn from R2.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Reconfigure the static route in R0.Change ECMP value to 2.") + dut = "r0" + red_static(dut) + + step("Configure cost on R0 as 100") + r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}} + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py new file mode 100644 index 0000000000..b663b224e4 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -0,0 +1,356 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy +import ipaddress + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation + +jsonFile = "{}/ospf_ecmp_lan.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + Topo : Broadcast Networks + +---+ +---+ +---+ +---+ + |R0 + +R1 + +R2 + +R3 | + +-+-+ +-+-+ +-+-+ +-+-+ + | | | | + | | | | + --+-----------+--------------+---------------+----- + Ethernet Segment + +TESTCASES = +1. Verify OSPF ECMP with max path configured as 8 + (Edge having 1 uplink port as broadcast network, + connect to 8 TORs - LAN case) + + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + try: + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + except OSError: + # OSError exception is raised when mininet tries to stop switch + # though switch is stopped once but mininet tries to stop same + # switch again, where it ended up with exception + pass + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_lan_ecmp_tc18_p0(request): + """ + OSPF ECMP. + + Verify OSPF ECMP with max path configured as 8 + (Edge having 1 uplink port as broadcast network, + connect to 8 TORs - LAN case) + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step(". Configure ospf in all the routers on LAN interface.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 8 neighborship sessions.") + + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Configure a static route in all the routes and " + "redistribute static/connected in OSPF." + ) + + for rtr in topo["routers"]: + input_dict = { + rtr: { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = rtr + red_static(dut) + + step( + "Verify that route in R0 in stalled with 8 hops. " + "Verify ospf route table and ip route table." + ) + + nh = [] + for rtr in topo["routers"]: + nh.append(topo["routers"][rtr]["links"]["s1"]["ipv4"].split("/")[0]) + nh.remove(topo["routers"]["r1"]["links"]["s1"]["ipv4"].split("/")[0]) + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" clear ip ospf interface on DUT(r0)") + clear_ospf(tgen, "r0") + + step( + "Verify that after clearing the ospf interface all the " + "neighbours are up and routes are installed with 8 next hop " + "in ospf and ip route tables on R0" + ) + + dut = "r0" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" clear ip ospf interface on R2") + clear_ospf(tgen, "r2") + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Delete static/connected cmd in ospf in all the routes one by one.") + for rtr in topo["routers"]: + input_dict = { + rtr: { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that all the routes are withdrawn from R0") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py new file mode 100644 index 0000000000..6f49e782b7 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -0,0 +1,721 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy +import ipaddress + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + shutdown_bringup_interface, + stop_router, + start_router, +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_lan.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} + +""" +Topology: + + Please view in a fixed-width font such as Courier. + Topo : Broadcast Networks + +---+ +---+ +---+ +---+ + |R0 + +R1 + +R2 + +R3 | + +-+-+ +-+-+ +-+-+ +-+-+ + | | | | + | | | | + --+-----------+--------------+---------------+----- + Ethernet Segment + +Testcases: +1. OSPF Hello protocol - Verify DR BDR Elections +2. OSPF IFSM -Verify state change events on DR / BDR / DR Other + + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + try: + # Stop toplogy and Remove tmp files + tgen.stop_topology + + except OSError: + # OSError exception is raised when mininet tries to stop switch + # though switch is stopped once but mininet tries to stop same + # switch again, where it ended up with exception + pass + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_lan_tc1_p0(request): + """ + OSPF Hello protocol - Verify DR BDR Elections + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step("Verify that DR BDR DRother are elected in the LAN.") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that all the routers are in FULL state with DR and BDR " + "in the topology" + ) + + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"priority": 100}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in ["r0", "r1", "r2", "r3"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DR") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"priority": 150}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in ["r0", "r1"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DR") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure DR priority 0 on R0 & Clear ospf nbrs on all the routers") + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"priority": 0}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in ["r1"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DRother") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "r2": {"state": "2-Way", "role": "DROther"}, + "r3": {"state": "2-Way", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure DR priority to default on R0 and Clear ospf neighbors" + " on all the routers" + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"priority": 100}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in ["r0", "r1"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DR") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["s1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + result = verify_ospf_neighbor(tgen, topo, dut, lan=True) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("No Shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["s1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + step("Verify that after no shut ospf neighbours are full on R0.") + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf on DR router in the topology.") + clear_ospf(tgen, "r0") + + step("Verify that BDR is getting promoted to DR after clear.") + step("Verify that all the nbrs are in FULL state with the elected DR.") + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on LAN intf on R0 to other ip from the same subnet.") + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + step( + "Verify that OSPF is in FULL state with other routers with " + "newly configured IP." + ) + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ospf router id on the R0 and clear ip ospf interface.") + change_rid = {"r0": {"ospf": {"router_id": "100.1.1.100"}}} + + result = create_router_ospf(tgen, topo, change_rid) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.100" + step("Reload the FRR router") + + stop_router(tgen, "r0") + start_router(tgen, "r0") + + step( + "Verify that OSPF is in FULL state with other routers with" + " newly configured router id." + ) + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Reconfigure the original router id and clear ip ospf interface.") + change_rid = {"r0": {"ospf": {"router_id": "100.1.1.0"}}} + result = create_router_ospf(tgen, topo, change_rid) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.0" + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r0") + start_router(tgen, "r0") + + step("Verify that OSPF is enabled with router id previously configured.") + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_lan_tc2_p0(request): + """ + OSPF IFSM -Verify state change events on DR / BDR / DR Other + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step( + "Verify that OSPF is subscribed to multi cast services " + "(All SPF, all DR Routers)." + ) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "priority": 98, + "timerDeadSecs": 4, + "area": "0.0.0.3", + "mcastMemberOspfDesignatedRouters": True, + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["s1"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "s1" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["s1"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Modify the mask on the R0 interface") + ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": ip_addr, + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "s1" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["s1"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the area id on the interface") + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"area": "0.0.0.3"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"area": "0.0.0.2"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": {"links": {"s1": {"ospf": {"area": "0.0.0.2", "ospfEnabled": True}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py new file mode 100644 index 0000000000..728fc2b8f1 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py @@ -0,0 +1,333 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import ipaddress +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.topolog import logger +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + create_route_maps, + shutdown_bringup_interface, + create_interfaces_cfg, +) +from ipaddress import IPv4Address +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_nssa.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + + + +TESTCASES = +1. OSPF Learning - Verify OSPF can learn different types of LSA and + processes them.[Edge learning different types of LSAs] +2. Verify that ospf non back bone area can be configured as NSSA area +3. Verify that ospf NSSA area DUT is capable receiving & processing + Type7 N2 route. +""" + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_learning_tc15_p0(request): + """Verify OSPF can learn different types of LSA and processes them. + + OSPF Learning : Edge learning different types of LSAs. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure area 1 as NSSA Area") + + reset_config_on_routers(tgen) + + step("Verify that Type 3 summary LSA is originated for the same Area 0") + ip = topo["routers"]["r1"]["links"]["r3-link0"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + + dut = "r0" + input_dict = { + "r1": { + "static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N IA"}] + } + } + + dut = "r0" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r2": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute static route in R2 ospf.") + dut = "r2" + red_static(dut) + + step("Verify that Type 5 LSA is originated by R2.") + dut = "r0" + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that R0 receives Type 4 summary LSA.") + dut = "r0" + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"} + ] + } + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + + for rtr in ["r1", "r2", "r3"]: + input_dict = { + rtr: {"ospf": {"area": [{"id": "0.0.0.2", "type": "nssa", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that OSPF neighbours are reset after changing area type.") + step("Verify that ABR R2 originates type 5 LSA in area 1.") + step("Verify that route is calculated and installed in R1.") + + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"} + ] + } + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py new file mode 100644 index 0000000000..373dcc29bc --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -0,0 +1,535 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_prefix_lists, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + verify_prefix_lists, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_database, +) + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_routemaps.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"] + +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPF Route map - Verify OSPF route map support functionality. +2. Verify OSPF route map support functionality when route map is not + configured at system level but configured in OSPF +3. Verify OSPF route map support functionality with set/match clauses + /call/continue/goto in a route-map to see if it takes immediate effect. +4. Verify OSPF route map support functionality + when route map actions are toggled. +5. Verify OSPF route map support functionality with multiple sequence + numbers in a single route-map for different match/set clauses. +6. Verify OSPF route map support functionality when we add/remove route-maps + with multiple set clauses and without any match statement.(Set only) +7. Verify OSPF route map support functionality when we + add/remove route-maps with multiple match clauses and without + any set statement.(Match only) + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_routemaps_functionality_tc20_p0(request): + """ + OSPF route map support functionality. + + Verify OSPF route map support functionality when route map is not + configured at system level but configured in OSPF + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0") + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute to ospf using route map ( non existent route map)") + ospf_red_r1 = { + "r0": { + "ospf": { + "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that routes are not allowed in OSPF even tough no " + "matching routing map is configured." + ) + + dut = "r1" + protocol = "ospf" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "configure the route map with the same name that is used " + "in the ospf with deny rule." + ) + + # Create route map + routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}} + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that now route map is activated & routes are denied in OSPF.") + dut = "r1" + protocol = "ospf" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}} + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that now route map is activated & routes are denied in OSPF.") + dut = "r1" + protocol = "ospf" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete the route map.") + # Create route map + routemaps = { + "r0": {"route_maps": {"rmap_ipv4": [{"action": "deny", "delete": True}]}} + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that routes are allowed in OSPF even tough " + "no matching routing map is configured." + ) + dut = "r1" + protocol = "ospf" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ospf_routemaps_functionality_tc24_p0(request): + """ + OSPF Route map - Multiple set clauses. + + Verify OSPF route map support functionality when we + add/remove route-maps with multiple match clauses and without + any set statement.(Match only) + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute to " + "OSPF using route map." + ) + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0",} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that prefix-list is created in R0.") + result = verify_prefix_lists(tgen, pfx_list) + assert result is not True, ( + "Testcase {} : Failed \n Prefix list not " + "present. Error: {}".format(tc_name, result) + ) + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + dut = "r1" + protocol = "ospf" + + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute to " + "OSPF using route map." + ) + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][1], + "no_of_ip": 1, + "next_hop": "Null0", + "tag": 1000, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that prefix-list is created in R0.") + result = verify_prefix_lists(tgen, pfx_list) + assert result is not True, ( + "Testcase {} : Failed \n Prefix list not " + "present. Error: {}".format(tc_name, result) + ) + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [{"action": "permit", "match": {"ipv4": {"tag": "1000"}}}] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + dut = "r1" + protocol = "ospf" + + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the match clause with tag in route map") + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"tag": "1000", "delete": True}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + dut = "r1" + protocol = "ospf" + + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the match clause with metric in route map.") + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py new file mode 100644 index 0000000000..aceae4ae20 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -0,0 +1,612 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from time import sleep +import ipaddress +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + shutdown_bringup_interface, +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + verify_ospf_rib, + create_router_ospf, +) + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_rte_calc.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ +""" + +TESTCASES = """ +1. Test OSPF intra area route calculations. +2. Test OSPF inter area route calculations. +3. Test OSPF redistribution of connected routes. +""" + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +@pytest.mark.precommit +def test_ospf_redistribution_tc5_p0(request): + """Test OSPF intra area route calculations.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + + step("Verify that OSPF neighbors are FULL.") + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("verify intra area route is calculated for r0-r3 interface ip in R1") + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address on newly configured loopback of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Add back the deleted ip address on newly configured interface of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Shut no shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Verify that intraroute calculated for R1 intf on R0 is deleted.") + dut = "r1" + + # sleep is added so that neighbor gets deleted after interface shut. + sleep(12) + + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("un shut the OSPF interface on R0") + dut = "r0" + shutdown_bringup_interface(tgen, dut, intf, True) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_redistribution_tc6_p0(request): + """Test OSPF inter area route calculations.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + + step("Verify that OSPF neighbors are FULL.") + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("verify intra area route is calculated for r0-r3 interface ip in R1") + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address on newly configured loopback of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Add back the deleted ip address on newly configured interface of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Shut no shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Verify that intraroute calculated for R1 intf on R0 is deleted.") + dut = "r1" + + sleep(10) + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("un shut the OSPF interface on R0") + dut = "r0" + shutdown_bringup_interface(tgen, dut, intf, True) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_redistribution_tc8_p1(request): + """ + Test OSPF redistribution of connected routes. + + Verify OSPF redistribution of connected routes when bgp multi hop + neighbor is configured using ospf routes + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + step( + "Configure loopback interface on all routers, and redistribut" + "e connected routes into ospf" + ) + reset_config_on_routers(tgen) + + step( + "verify that connected routes -loopback is found in all routers" + "advertised/exchaged via ospf" + ) + for rtr in topo["routers"]: + red_static(rtr) + red_connected(rtr) + for node in topo["routers"]: + input_dict = { + "r0": { + "static_routes": [ + { + "network": topo["routers"][node]["links"]["lo"]["ipv4"], + "no_of_ip": 1, + } + ] + } + } + for rtr in topo["routers"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure E BGP multi hop using the loopback addresses.") + as_num = 100 + for node in topo["routers"]: + as_num += 1 + topo["routers"][node].update( + { + "bgp": { + "local_as": as_num, + "address_family": {"ipv4": {"unicast": {"neighbor": {}}}}, + } + } + ) + for node in topo["routers"]: + for rtr in topo["routers"]: + if node is not rtr: + topo["routers"][node]["bgp"]["address_family"]["ipv4"]["unicast"][ + "neighbor" + ].update( + { + rtr: { + "dest_link": { + "lo": {"source_link": "lo", "ebgp_multihop": 2} + } + } + } + ) + + result = create_router_bgp(tgen, topo, topo["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that BGP neighbor is ESTABLISHED") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step( + "Configure couple of static routes in R0 and " + "Redistribute static routes in R1 bgp." + ) + + for rtr in topo["routers"]: + ospf_red = { + rtr: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r0 = { + "r0": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + protocol = "bgp" + for rtr in ["r1", "r2", "r3"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clear ospf neighbours in R0") + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that OSPF neighbours are reset and forms new adjacencies.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that BGP neighbours are reset and forms new adjacencies.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + protocol = "bgp" + for rtr in ["r1", "r2", "r3"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py new file mode 100644 index 0000000000..87b1b22c0f --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -0,0 +1,1021 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy +from ipaddress import IPv4Address + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +import ipaddress + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + create_route_maps, + shutdown_bringup_interface, + create_interfaces_cfg, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, + verify_ospf_database, +) + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_single_area.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPF IFSM -Verify state change events on p2p network. +2. OSPF Timers - Verify OSPF interface timer hello interval functionality +3. OSPF Timers - Verify OSPF interface timer dead interval functionality +4. Verify ospf show commands with json output. + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_p2p_tc3_p0(request): + """OSPF IFSM -Verify state change events on p2p network.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step( + "Verify that OSPF is subscribed to multi cast services " + "(All SPF, all DR Routers)." + ) + step("Verify that interface is enabled in ospf.") + step("Verify that config is successful.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}} + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Modify the mask on the R0 interface") + ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": ip_addr, + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ], + "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "interface" + ], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + build_config_from_json(tgen, topo, save_bkup=False) + + step("Change the area id on the interface") + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_hello_tc10_p0(request): + """ + OSPF timers. + + Verify OSPF interface timer hello interval functionality + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("modify hello timer from default value to some other value on r1") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 11, "dead_interval": 12}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify that new timer value is configured and applied using " + "the show ip ospf interface command." + ) + dut = "r1" + input_dict = { + "r1": { + "links": {"r0": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}} + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # sleep for 20 secs for hello timer expiry + sleep(20) + + step("verify that ospf neighbours are not full - hello timer mis match.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=10 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("modify hello timer from default value to r1 hello timer on r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 11, "dead_interval": 12}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("reconfigure the default hello timer value to default on r1 and r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("reconfigure the default hello timer value to default on r1 and r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("configure hello timer = 1 on r1 and r2") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 1, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 1, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": {"links": {"r1": {"ospf": {"timerMsecs": 1 * 1000, "timerDeadSecs": 4}}}} + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Configure hello timer = 65535") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 65535, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 65535, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 65535 * 1000, "timerDeadSecs": 4}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Try configuring timer values outside range for example 65536") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 65536, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Unconfigure the hello timer from the interface from r1 and r2.") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 65535}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that timer value is deleted from intf & " "set to default value 40 sec." + ) + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerMsecs": 10 * 1000}}}}} + dut = "r1" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_dead_tc11_p0(request): + """ + OSPF timers. + + Verify OSPF interface timer dead interval functionality + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("modify dead interval from default value to some other value on r1") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 12, "dead_interval": 48}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify that new timer value is configured and applied using " + "the show ip ospf interface command." + ) + dut = "r1" + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 48}}}}} + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + sleep(50) + step("verify that ospf neighbours are not full - dead timer mis match.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=10 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("modify dead interval from default value to r1" "dead interval timer on r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 48, "hello_interval": 12}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 48}}}}} + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("remove ospf on R1") + ospf_del = {"r0": {"ospf": {"delete": True}}} + result = create_router_ospf(tgen, topo, ospf_del) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("verify that on R2 ospf neighbour is full till dead time expiry.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + sleep(50) + + step("Verify that nbr on r2 gets deleted after dead interval expiry.") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + # reconfiguring deleted ospf process by resetting the configs. + reset_config_on_routers(tgen) + + step("reconfigure the default dead interval timer value to " "default on r1 and r2") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 40}}}}} + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Configure dead timer = 65535 on r1 and r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 65535}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 65535}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 65535}}}}} + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Try configuring timer values outside range for example 65536") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"dead_interval": 65536}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Unconfigure the dead timer from the interface from r1 and r2.") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"dead_interval": 65535}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that timer value is deleted from intf & " "set to default value 40 sec." + ) + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}} + dut = "r1" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_show_p1(request): + """Verify ospf show commands with json output.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step(" Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + dut = "r1" + input_dict = { + "r1": { + "links": { + "r0": { + "ospf": { + "ifUp": True, + "ifFlags": "", + "ospfEnabled": True, + "ipAddressPrefixlen": 24, + "ospfIfType": "Broadcast", + "area": "0.0.0.0", + "networkType": "BROADCAST", + "cost": 10, + "transmitDelaySecs": 1, + "state": "DR", + "priority": 1, + "mcastMemberOspfAllRouters": True, + "timerMsecs": 1000, + "timerDeadSecs": 4, + "timerWaitSecs": 4, + "timerRetransmitSecs": 5, + "nbrCount": 1, + "nbrAdjacentCount": 1, + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # show ip ospf route + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))