]> git.puffer.fish Git - matthieu/frr.git/commitdiff
tests: ospf_basic_functionality topojson testcases.
authornaveen <nguggarigoud@vmware.com>
Tue, 28 Jul 2020 15:02:17 +0000 (20:32 +0530)
committernguggarigoud <nguggarigoud@vmware.com>
Fri, 18 Sep 2020 09:43:46 +0000 (15:13 +0530)
1. Adding 18 ospf testcases to topojson.
2. Adding ospf.py library.

Test suite execution time is ~18 minutes.

Signed-off-by: naveen <nguggarigoud@vmware.com>
19 files changed:
tests/topotests/lib/common_config.py
tests/topotests/lib/ospf.py [new file with mode: 0644]
tests/topotests/lib/topojson.py
tests/topotests/ospf_basic_functionality/ospf_authentication.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_ecmp.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_lan.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_nssa.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_routemaps.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_rte_calc.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/ospf_single_area.json [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_authentication.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_lan.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_nssa.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py [new file with mode: 0644]
tests/topotests/ospf_basic_functionality/test_ospf_single_area.py [new file with mode: 0644]

index 45a86c7a3f85c3a40ddba8dbc7612f97cd213d70..09e7fa7873017f408401f2bf0e371ddbb16d0091 100644 (file)
@@ -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 (file)
index 0000000..f6a02bd
--- /dev/null
@@ -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
index 9c2baedde4af2c1f3dc42d85deb1f077badd9178..6d97ab2e9041693b7ac8dcea32f8638d07b98655 100644 (file)
@@ -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 (file)
index 0000000..4edb9f2
--- /dev/null
@@ -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 (file)
index 0000000..ea5b9a4
--- /dev/null
@@ -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 (file)
index 0000000..ecfd35d
--- /dev/null
@@ -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 (file)
index 0000000..126934c
--- /dev/null
@@ -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 (file)
index 0000000..f95a297
--- /dev/null
@@ -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 (file)
index 0000000..60a8434
--- /dev/null
@@ -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 (file)
index 0000000..9062a09
--- /dev/null
@@ -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 (file)
index 0000000..c595912
--- /dev/null
@@ -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 (file)
index 0000000..5f6412c
--- /dev/null
@@ -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 (file)
index 0000000..51be52b
--- /dev/null
@@ -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 (file)
index 0000000..b663b22
--- /dev/null
@@ -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 (file)
index 0000000..6f49e78
--- /dev/null
@@ -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 (file)
index 0000000..728fc2b
--- /dev/null
@@ -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 (file)
index 0000000..373dcc2
--- /dev/null
@@ -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 (file)
index 0000000..aceae4a
--- /dev/null
@@ -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 (file)
index 0000000..87b1b22
--- /dev/null
@@ -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": "<UP,BROADCAST,RUNNING,MULTICAST>",
+                        "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))