#!/usr/bin/python
# SPDX-License-Identifier: ISC

#
# Copyright (c) 2020 by VMware, Inc. ("VMware")
# Used Copyright (c) 2018 by Network Device Education Foundation,
# Inc. ("NetDEF") in this file.
#

"""
Following tests are covered to test bgp community functionality:
1. Verify that BGP well known communities work fine for
   eBGP and iBGP peers.
   Well known communities tested: no-export, local-AS

"""

import os
import sys
import time
import pytest

# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))

# pylint: disable=C0413
# Import topogen and topotest helpers
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,
    verify_rib,
    create_static_routes,
    check_address_types,
    step,
    create_route_maps,
    create_route_maps,
    required_linux_kernel_version,
)

from lib.topolog import logger
from lib.bgp import (
    verify_bgp_convergence,
    create_router_bgp,
    verify_bgp_rib,
    verify_bgp_community,
)
from lib.topojson import build_config_from_json

pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]


# Global variables
BGP_CONVERGENCE = False
ADDR_TYPES = check_address_types()
NETWORK = {
    "ipv4": ["192.0.2.1/32", "192.0.2.2/32"],
    "ipv6": ["2001:DB8::1:1/128", "2001:DB8::1:2/128"],
}


def setup_module(mod):
    """
    Sets up the pytest environment

    * `mod`: module name
    """

    # Required linux kernel version for this suite to run.
    result = required_linux_kernel_version("4.14")
    if result is not True:
        pytest.skip("Kernel requirements are not met, kernel version should be >= 4.14")

    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...
    json_file = "{}/bgp_communities_topo2.json".format(CWD)
    tgen = Topogen(json_file, mod.__name__)
    global topo
    topo = tgen.json_topo
    # ... and here it calls Mininet initialization functions.

    # Starting topology, create tmp files which are loaded to routers
    #  to start daemons and then start routers
    start_topology(tgen)

    # Creating configuration from JSON
    build_config_from_json(tgen, topo)

    # Checking BGP convergence
    global BGP_CONVERGENCE
    global ADDR_TYPES

    # Don't run this test if we have any failure.
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    # Api call verify whether BGP is converged
    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
        BGP_CONVERGENCE
    )

    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)


#####################################################
#
#   Tests starting
#
#####################################################


def test_bgp_no_export_local_as_communities_p0(request):
    """
    Verify that BGP well known communities work fine for
    eBGP and iBGP peers.
    Well known communities tested: no-export, local-AS
    """

    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)

    step("Initial config: Configure BGP neighborship between R1 and R3.")
    reset_config_on_routers(tgen)

    step("Configure static routes on R1 with next-hop as null0")
    for addr_type in ADDR_TYPES:
        input_dict_4 = {
            "r1": {
                "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}]
            }
        }
        result = create_static_routes(tgen, input_dict_4)
        assert result is True, "Testcase {} : Failed \n Error: {}".format(
            tc_name, result
        )

    for comm_type in ["no-export", "local-AS"]:

        step("Create a route-map on R1 to set community as {}".format(comm_type))

        seq_id = 10
        input_rmap = {
            "r1": {
                "route_maps": {
                    "rmap_wkc": [
                        {
                            "action": "permit",
                            "seq_id": seq_id,
                            "set": {"community": {"num": "{}".format(comm_type)}},
                        }
                    ]
                }
            }
        }
        result = create_route_maps(tgen, input_rmap)
        assert result is True, "Testcase {} : Failed \n Error: {}".format(
            tc_name, result
        )

        step("Apply route-map while redistributing static routes into BGP")
        input_dict_2 = {
            "r1": {
                "bgp": {
                    "address_family": {
                        "ipv4": {
                            "unicast": {
                                "redistribute": [
                                    {
                                        "redist_type": "static",
                                        "attribute": {"route-map": "rmap_wkc"},
                                    }
                                ]
                            }
                        },
                        "ipv6": {
                            "unicast": {
                                "redistribute": [
                                    {
                                        "redist_type": "static",
                                        "attribute": {"route-map": "rmap_wkc"},
                                    }
                                ]
                            }
                        },
                    }
                }
            }
        }
        result = create_router_bgp(tgen, topo, input_dict_2)

        step("Verify that BGP prefixes on R1 have community: {}".format(comm_type))
        input_dict_4 = {"community": "{}".format(comm_type)}
        for addr_type in ADDR_TYPES:
            result = verify_bgp_community(
                tgen, addr_type, "r1", NETWORK[addr_type], input_dict_4
            )
            assert result is True, "Test case {} : Should fail \n Error: {}".format(
                tc_name, result
            )

        for addr_type in ADDR_TYPES:
            input_dict_4 = {
                "r1": {
                    "static_routes": [
                        {
                            "network": NETWORK[addr_type],
                            "next_hop": topo["routers"]["r2"]["links"]["r1"][
                                addr_type
                            ].split("/")[0],
                        }
                    ]
                }
            }
            result = verify_bgp_rib(
                tgen,
                addr_type,
                "r2",
                input_dict_4,
                next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0],
            )
            assert result is True, "Testcase {} : Failed \n Error: {}".format(
                tc_name, result
            )

        step(
            "Verify that these prefixes, originated on R1, are not"
            "received on R3 but received on R2"
        )
        result = verify_rib(
            tgen,
            addr_type,
            "r3",
            input_dict_4,
            next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
            expected=False,
        )
        assert result is not True, (
            "Testcase {} : Failed \n "
            "Expected: Routes are still present in rib of r3 \n "
            "Found: {}".format(tc_name, result)
        )

        step("Remove route-map from redistribute static on R1")
        input_dict_2 = {
            "r1": {
                "bgp": {
                    "address_family": {
                        "ipv4": {
                            "unicast": {
                                "redistribute": [
                                    {"redist_type": "static", "delete": True}
                                ]
                            }
                        },
                        "ipv6": {
                            "unicast": {
                                "redistribute": [
                                    {"redist_type": "static", "delete": True}
                                ]
                            }
                        },
                    }
                }
            }
        }
        result = create_router_bgp(tgen, topo, input_dict_2)
        assert result is True, "Testcase {} : Failed \n Error: {}".format(
            tc_name, result
        )

        step("Configure redistribute static")
        input_dict_2 = {
            "r1": {
                "bgp": {
                    "address_family": {
                        "ipv4": {
                            "unicast": {"redistribute": [{"redist_type": "static"}]}
                        },
                        "ipv6": {
                            "unicast": {"redistribute": [{"redist_type": "static"}]}
                        },
                    }
                }
            }
        }
        result = create_router_bgp(tgen, topo, input_dict_2)
        assert result is True, "Testcase {} : Failed \n Error: {}".format(
            tc_name, result
        )

        step(
            "Verify that these prefixes, originated on R1, are now"
            "received on both routers R2 and R3"
        )
        for addr_type in ADDR_TYPES:
            input_dict_4 = {
                "r1": {
                    "static_routes": [
                        {
                            "network": NETWORK[addr_type],
                            "next_hop": topo["routers"]["r2"]["links"]["r1"][
                                addr_type
                            ].split("/")[0],
                        }
                    ]
                }
            }
            result = verify_bgp_rib(
                tgen,
                addr_type,
                "r2",
                input_dict_4,
                next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0],
            )
            assert result is True, "Testcase {} : Failed \n Error: {}".format(
                tc_name, result
            )

            result = verify_bgp_rib(
                tgen,
                addr_type,
                "r3",
                input_dict_4,
                next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
            )
            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))
