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

#
# test_bgp_vpnv4_asbr.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2023 by 6WIND
#

"""
 test_bgp_vpnv4_asbr.py: Test the FRR BGP daemon with rfc4364 option 10b
 r1, r2, and r100 are in an iBGP AS, while r2, r3 do an eBGP peering
 h1 is a host behind r1 VRF1, and {h2,h3} are hosts behind r3 VRF1
 The test demonstrates the connectivity across the network between h1 and h3.


 +----------+          +----+--------+              +--------+              +--------+-----+
 |          |172.31.0.0|vrf | r1     |192.168.0.0/24| r2     |192.168.1.0/24|r3      | vrf |
 |   h1     +----------+    |       1+------+-------+        +------+-------+3       |     +--- 172.31.3.0/24
 |   10     |          |VRF1|AS65500 |      |       | AS65500|      |       |AS65501 |VRF1 |
 +----------+          +-------------+      |       +--------+      |       +--------+--+-++
                           192.0.2.1        |       192.0.2.2       |                172| |
                                       +----------+            +----+--------+        31| |
                                       |rr100     |            |rs200/AS65502|         1| |
                                       +----------+            +-------------+         0| |
                                        192.0.2.100   +--------+                     /24| |
                                                      |        |        +----------+----+ |
                                                      |h3      |        |          |      |
                                                      |10      |        |  h2      |      |
                                                      +---+----+        |  10      |      |
                                                          |             +----------+      |
                                                          |172.31.2.0/24                  |
                                                          +--------------------------------+
"""

import os
import sys
import json
from functools import partial
import pytest
import functools

# 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 import topotest
from lib.bgpcheck import (
    check_show_bgp_vpn_prefix_found,
    check_show_bgp_vpn_prefix_not_found,
)
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from lib.checkping import check_ping


# Required to instantiate the topology builder class.


pytestmark = [pytest.mark.bgpd]


def build_topo(tgen):
    "Build function"

    # Allocate 8 devices
    tgen.add_router("r1")
    tgen.add_router("r2")
    tgen.add_router("r3")
    tgen.add_router("h1")
    tgen.add_router("h2")
    tgen.add_router("h3")
    tgen.add_router("rr100")
    tgen.add_router("rs200")

    switch = tgen.add_switch("s1")
    switch.add_link(tgen.gears["r1"])
    switch.add_link(tgen.gears["r2"])
    switch.add_link(tgen.gears["rr100"])

    switch = tgen.add_switch("s2")
    switch.add_link(tgen.gears["r1"])
    switch.add_link(tgen.gears["h1"])

    switch = tgen.add_switch("s3")
    switch.add_link(tgen.gears["r2"])
    switch.add_link(tgen.gears["r3"])
    switch.add_link(tgen.gears["rs200"])

    switch = tgen.add_switch("s4")
    switch.add_link(tgen.gears["r3"])
    switch.add_link(tgen.gears["h2"])

    switch = tgen.add_switch("s5")
    switch.add_link(tgen.gears["r3"])
    switch.add_link(tgen.gears["h3"])

    switch = tgen.add_switch("s6")
    switch.add_link(tgen.gears["r3"])


def _populate_iface():
    tgen = get_topogen()
    cmds_list = [
        "ip link add vrf1 type vrf table 10",
        "echo 100000 > /proc/sys/net/mpls/platform_labels",
        "ip link set dev vrf1 up",
        "ip link set dev {0}-eth1 master vrf1",
        "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input",
    ]

    for rname in ("r1", "r3"):
        for cmd in cmds_list:
            input = cmd.format(rname)
            logger.info("input: " + cmd)
            output = tgen.net[rname].cmd(cmd.format(rname))
            logger.info("output: " + output)

    cmds_list = [
        "ip link set dev {0}-eth2 master vrf1",
        "ip link set dev {0}-eth3 master vrf1",
    ]
    for cmd in cmds_list:
        input = cmd.format("r3")
        logger.info("input: " + input)
        output = tgen.net["r3"].cmd(input)
        logger.info("output: " + output)


def setup_module(mod):
    "Sets up the pytest environment"
    tgen = Topogen(build_topo, mod.__name__)
    tgen.start_topology()

    router_list = tgen.routers()
    _populate_iface()

    for rname, router in router_list.items():
        router.load_config(
            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
        )
        if rname in ("r1", "r2", "r3", "rr100", "rs200"):
            router.load_config(
                TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
            )

    # Initialize all routers.
    tgen.start_router()


def teardown_module(_mod):
    "Teardown the pytest environment"
    tgen = get_topogen()

    tgen.stop_topology()


def bgp_vpnv4_prefix_check(router, rd, prefix, label, nexthop):
    """
    Dump and check 'show bgp ipv4 vpn <prefix> json' output. An assert is triggered in case test fails
    * 'router': the router to check
    * 'rd': The route distinguisher expected
    * 'prefix': The prefix expected
    * 'label': The label expected associated with the ('rd','prefix') tuple
    * 'nexthop': The nexthop expected associated with the ('rd','prefix') tuple
    """

    def _check(router, prefix, rd, label, nexthop):
        dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
        if not dump:
            return "{0}, {1}, route distinguisher {2} not present".format(
                router.name, prefix, rd
            )
        for dumped_rd, pathes in dump.items():
            if dumped_rd != rd:
                continue
            for path in pathes["paths"]:
                if "remoteLabel" not in path.keys():
                    return "{0}, {1}, rd {2}, remoteLabel not present".format(
                        router.name, prefix, rd
                    )
                if str(path["remoteLabel"]) != label:
                    continue

                if "nexthops" not in path.keys():
                    return "{0}, {1}, rd {2}, no nexthops present".format(
                        router.name, prefix, rd
                    )

                for nh in path["nexthops"]:
                    if "ip" not in nh.keys():
                        return "{0}, {1}, rd {2}, no ipv4 nexthop available".format(
                            router.name, prefix, rd
                        )
                    if nh["ip"] != nexthop:
                        continue
                    return None
        return "{0}, {1}, rd {2}, remoteLabel {3}, nexthop {4} not found".format(
            router.name, prefix, rd, label, nexthop
        )

    func = functools.partial(_check, router, prefix, rd, label, nexthop)
    _, result = topotest.run_and_expect(func, None, count=20, wait=0.5)
    assert_msg = "{}, show bgp ipv4 vpn {}, rd {}, label {} nexthop {}".format(
        router.name, prefix, rd, label, nexthop
    )
    assert result is None, assert_msg + " not found"
    logger.info(assert_msg + " found")


def mpls_table_get_entry(router, out_label, out_nexthop):
    """
    Get the in_label from tuple (out_label, out_nexthop)
    * 'router': the router to check
    * 'out_label': The outgoing label expected
    * 'out_nexthop': The outgoing nexthop expected
    """
    dump = router.vtysh_cmd("show mpls table json", isjson=True)
    for in_label, label_info in dump.items():
        for nh in label_info["nexthops"]:
            if nh["type"] != "BGP" or "installed" not in nh.keys():
                continue
            if "nexthop" in nh.keys():
                if nh["nexthop"] != out_nexthop:
                    continue
            if "outLabelStack" in nh.keys():
                if out_label not in nh["outLabelStack"]:
                    continue
            return in_label
    return None


def mpls_table_check_entry(router, out_label, out_nexthop):
    """
    Dump and check 'show mpls table json' output. An assert is triggered in case test fails
    * 'router': the router to check
    * 'out_label': The outgoing label expected
    * 'out_nexthop': The outgoing nexthop expected
    """
    logger.info("Checking MPLS labels on {}".format(router.name))
    dump = router.vtysh_cmd("show mpls table json", isjson=True)
    for in_label, label_info in dump.items():
        for nh in label_info["nexthops"]:
            if nh["type"] != "BGP" or "installed" not in nh.keys():
                continue
            if "nexthop" in nh.keys():
                if nh["nexthop"] != out_nexthop:
                    continue
            if "outLabelStack" in nh.keys():
                if out_label not in nh["outLabelStack"]:
                    continue
            logger.info(
                "{}, show mpls table, entry in_label {} out_label {} out_nexthop {} found".format(
                    router.name, in_label, nh["outLabelStack"], nh["nexthop"]
                )
            )
            return None
    return "{}, show mpls table, entry matching in_label {} out_label {} out_nexthop {} not found".format(
        router.name, in_label, out_label, out_nexthop
    )


def check_show_mpls_table_entry_label_not_found(router, inlabel):
    output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
    expected = {"inLabel": inlabel, "installed": True}
    ret = topotest.json_cmp(output, expected)
    if ret is None:
        return "not good"
    return None


def check_show_bgp_vpn_ok(router, vpnv4_entries):
    """
    Check on router that BGP l3vpn entries are present
    Check there is an MPLS entry bound to that BGP L3VPN entry
    Extract the Label value and check on the distributed router the BGP L3VPN entry
    If check fail, an assert is triggered.
    * 'router': the router to check BGP VPN RIB
    * 'vpnv4_entries': dictionary that contains the list of prefixes, and the distributed router to look after
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    vpnv4_nexthops = {"r1": "192.0.2.2", "r3": "192.168.1.2"}
    vpnv4_nht = {"192.0.2.1": "192.168.0.1", "192.168.1.3": "192.168.1.3"}
    label_ip_entries = {}

    def _return_remote_label_nh_rd(router, prefix):
        dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
        assert_msg = (
            "{}, prefix {} not available or label not found",
            router.name,
            prefix,
        )
        assert dump, assert_msg
        for rd, pathes in dump.items():
            for path in pathes["paths"]:
                if "remoteLabel" not in path.keys():
                    assert 0, assert_msg
                for nh in path["nexthops"]:
                    if "ip" in nh.keys():
                        return path["remoteLabel"], nh["ip"], rd
        assert 0, assert_msg

    def _check_nexthop_available(router, prefix):
        dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
        if not dump:
            return "{0}, {1}, route distinguisher not present".format(
                router.name, prefix
            )
        for _, pathes in dump.items():
            for path in pathes["paths"]:
                if "remoteLabel" not in path.keys():
                    return "{0}, {1}, remoteLabel not present".format(
                        router.name, prefix
                    )
                if "nexthops" not in path.keys():
                    return "{0}, {1}, no nexthop available".format(router.name, prefix)
                return None

    for prefix, rname_to_test in vpnv4_entries.items():
        func = functools.partial(_check_nexthop_available, router, prefix)
        _, result = topotest.run_and_expect(func, None, count=20, wait=0.5)
        assert result is None, "Failed to detect prefix {} on router {}".format(
            prefix, router.name
        )

    for prefix, rname_to_test in vpnv4_entries.items():
        l3vpn_label, l3vpn_nh, l3vpn_rd = _return_remote_label_nh_rd(router, prefix)
        logger.info(
            "{0}, {1}, label value is {2}, nh is {3}".format(
                router.name, prefix, l3vpn_label, l3vpn_nh
            )
        )
        test_func = functools.partial(
            mpls_table_check_entry, router, l3vpn_label, vpnv4_nht[l3vpn_nh]
        )
        success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, result

        in_label = mpls_table_get_entry(router, l3vpn_label, vpnv4_nht[l3vpn_nh])
        label_ip_entries[prefix] = in_label

        bgp_vpnv4_prefix_check(
            tgen.gears[rname_to_test],
            l3vpn_rd,
            prefix,
            in_label,
            vpnv4_nexthops[rname_to_test],
        )

        return label_ip_entries


def test_protocols_convergence():
    """
    Assert that all protocols have converged
    Check that Labels are as expected in r1, r2,and r3
    Check ping connectivity between h1 and h2
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    # check that r2 peerings are ok
    logger.info("Checking BGP ipv4 vpn summary for r2")
    router = tgen.gears["r2"]
    json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name)
    expected = json.loads(open(json_file).read())
    test_func = partial(
        topotest.router_json_cmp,
        router,
        "show bgp ipv4 vpn summary json",
        expected,
    )
    _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
    assertmsg = '"{}" JSON output mismatches'.format(router.name)
    assert result is None, assertmsg


def test_mpls_setup_ok():
    """
    tests for the r1 to r3 direction: checks for prefix=('172.31.1.0/24','172.31.2.0/24','172.31.3.0/24')
               r2. get label from 'prefix'
    check that r2. show mpls table has an entry with outbound label set to the label from 172.31.1.0/24
               r2. get label from mpls entry
    check that r1: show bgp ipv4 vpn 172.31.1.0/24 has label from r2.mpls entry
    tests for the r3 to r1 direction
               r2. get label from 172.31.0.0/24
    check that r2. show mpls table has an entry with outbound label set that includes the label from 172.31.0.0/24
               r2. get label from mpls entry
    check that r3: show bgp ipv4 vpn 172.31.0.0/24 has label from r2.mpls entry
    check that h1. ping 172.31.1.10 (h2) is ok.
    check that h1. ping 172.31.2.10 (h3) is ok.
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)
    router = tgen.gears["r2"]

    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)

    vpnv4_checks = {
        "172.31.1.0/24": "r1",
        "172.31.2.0/24": "r1",
        "172.31.3.0/24": "r1",
        "172.31.0.0/24": "r3",
    }
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format(
            router.name
        )
    )
    check_show_bgp_vpn_ok(router, vpnv4_checks)

    logger.info("h1, check that ping from h1 to (h2,h3) is ok")
    check_ping("h1", "172.31.1.10", True, 20, 0.5)
    check_ping("h1", "172.31.2.10", True, 20, 0.5)


def test_r3_prefixes_removed():
    """
    Remove BGP redistributed updates from r3.
    Check that the BGP VPN updates from the updates are not present on r2.
    Check that the 'show bgp ipv4 vpn' and 'show mpls table' are ok for 172.31.3.0/24
    Remove the 172.31.3.0/24 update from BGP on r3.
    Check that the BGP VPN updates from r3 are not present on r2.
    Check that the 'show mpls table' entry previously seen disappeared
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)
    router = tgen.gears["r3"]
    logger.info("{}, keeping only 172.31.3.0/24 network".format(router.name))
    router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nshutdown\n")
    router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nshutdown\n")

    router = tgen.gears["r2"]
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' has only 172.31.3.0/24 network from r3".format(
            router.name
        )
    )

    for prefix in ("172.31.1.0/24", "172.31.2.0/24"):
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_not_found,
            router,
            "ipv4",
            prefix,
            "444:3",
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)

    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)

    prefix = "172.31.3.0/24"
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
            router.name
        )
    )
    vpnv4_checks = {
        prefix: "r1",
    }
    label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)

    router = tgen.gears["r3"]
    logger.info("{}, removing {} network".format(router.name, prefix))
    router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nshutdown\n")

    router = tgen.gears["r2"]
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' has not {} network from r3".format(
            router.name, prefix
        )
    )
    test_func = functools.partial(
        check_show_bgp_vpn_prefix_not_found,
        router,
        "ipv4",
        prefix,
        "444:3",
    )
    success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
    assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)

    logger.info(
        "{}, check that 'show mpls table {}' is not present".format(
            router.name, label_ip_entries[prefix]
        )
    )
    test_func = functools.partial(
        check_show_mpls_table_entry_label_not_found, router, label_ip_entries[prefix]
    )
    success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
    assert success, "r1, mpls entry with in_label {} still present".format(
        label_ip_entries[prefix]
    )


def test_r3_prefixes_added_back():
    """
    Add back the 172.31.3.0/24 network from r3
    Check on r2 that MPLS switching entry appears when the 1st BGP update is received
    Check the IP connectivity (h1,h2) and (h1,h3)
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)
    router = tgen.gears["r3"]
    prefix = "172.31.3.0/24"
    logger.info("{}, restoring the {} network from r3".format(router.name, prefix))
    router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nno shutdown\n")

    router = tgen.gears["r2"]
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' has {} network from r3".format(
            router.name, prefix
        )
    )

    test_func = functools.partial(
        check_show_bgp_vpn_prefix_found,
        router,
        "ipv4",
        prefix,
        "444:3",
    )
    success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
    assert success, "{}, vpnv4 update {} not present".format(router.name, prefix)

    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
            router.name
        )
    )
    vpnv4_checks = {
        prefix: "r1",
    }
    check_show_bgp_vpn_ok(router, vpnv4_checks)

    router = tgen.gears["r3"]
    logger.info(
        "{}, restoring the redistribute connected prefixes from r3".format(router.name)
    )
    router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nno shutdown\n")
    router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nno shutdown\n")
    router = tgen.gears["r2"]
    for prefix in ("172.31.1.0/24", "172.31.2.0/24"):
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_found,
            router,
            "ipv4",
            prefix,
            "444:3",
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, vpnv4 update {} not present".format(router.name, prefix)

    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)


def test_unconfigure_nexthop_change_nexthop_self():
    """
    Get the list of labels advertised from r2 to r1
    On r2, disable next-hop-self for 192.0.2.100 neighbor
    Check that the list of labels are not present in 'show mpls table'
    Check that r1 received the prefixes with the original (next-hop,label)
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    router = tgen.gears["r2"]
    vpnv4_checks = {
        "172.31.1.0/24": "r1",
        "172.31.2.0/24": "r1",
        "172.31.3.0/24": "r1",
    }
    logger.info(
        "{}, Get the list of labels allocated for prefixes from r3".format(router.name)
    )
    label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)

    logger.info(
        "{}, disable next-hop-self for 192.0.2.100 neighbor".format(router.name)
    )
    router = tgen.gears["r2"]
    router.vtysh_cmd(
        "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 next-hop-self\n"
    )

    for prefix, label in label_ip_entries.items():
        logger.info(
            "{}, check mpls entry for {} with in_label {} is not present'".format(
                router.name, prefix, label
            )
        )
        test_func = functools.partial(
            check_show_mpls_table_entry_label_not_found, router, label
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "r1, mpls entry for {} with in_label {} still present".format(
            prefix, label
        )

    router = tgen.gears["r1"]
    for prefix, label in label_ip_entries.items():
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_not_found,
            router,
            "ipv4",
            prefix,
            "444:3",
            label=label,
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, mpls vpn update {} label {} is present".format(
            router.name, prefix, label
        )
    for prefix, label in label_ip_entries.items():
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_found,
            router,
            "ipv4",
            prefix,
            "444:3",
            nexthop="192.168.1.3",
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, mpls vpn update {} label {} is present".format(
            router.name, prefix, label
        )

    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)


def test_reconfigure_nexthop_change_nexthop_self():
    """
    Get the list of labels advertised from r2 to r1
    On r2, enable next-hop-self for 192.0.2.100 neighbor
    Check that the list of labels are present in 'show mpls table'
    Check that r1 received the prefixes with the original (next-hop,label)
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    router = tgen.gears["r2"]
    logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(router.name))
    router.vtysh_cmd(
        "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 next-hop-self\n"
    )
    vpnv4_checks = {
        "172.31.1.0/24": "r1",
        "172.31.2.0/24": "r1",
        "172.31.3.0/24": "r1",
    }
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
            router.name
        )
    )
    check_show_bgp_vpn_ok(router, vpnv4_checks)

    logger.info("h1, check that ping from h1 to (h2,h3) is ok")
    check_ping("h1", "172.31.1.10", True, 20, 0.5)
    check_ping("h1", "172.31.2.10", True, 20, 0.5)
    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)


def test_declare_vpn_network_with_different_label():
    """
    declare a vpnv4 network on r3.
    check that a new VPNv4 entry is received on r2.
    Check that the list of labels are present in 'show mpls table'
    Check that r1 received the prefixes with the new (next-hop,label)
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    router = tgen.gears["r3"]
    logger.info(
        "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format(
            router.name
        )
    )
    router.vtysh_cmd(
        "configure terminal\nrouter bgp 65501\nno bgp network import-check\n"
    )
    router.vtysh_cmd(
        "configure terminal\nrouter bgp 65501\naddress-family ipv4 vpn\nnetwork 33.33.33.33/32 rd 444:3 label 33\n"
    )

    router = tgen.gears["r2"]
    vpnv4_entries = {
        "172.31.1.0/24": None,
        "172.31.2.0/24": None,
        "172.31.3.0/24": None,
        "33.33.33.33/32": 33,
    }

    for prefix, label in vpnv4_entries.items():
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_found,
            router,
            "ipv4",
            prefix,
            "444:3",
            label=label,
            nexthop="192.168.1.3",
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, vpnv4 update {}, label {} not present".format(
            router.name, prefix, label
        )

    vpnv4_checks = {
        "172.31.1.0/24": "r1",
        "172.31.2.0/24": "r1",
        "172.31.3.0/24": "r1",
        "33.33.33.33/32": "r1",
    }
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
            router.name
        )
    )
    check_show_bgp_vpn_ok(router, vpnv4_checks)


def test_filter_vpn_network_from_r1():
    """
    Get the list of labels in 'show mpls table'
    filter network from r1
    check that the vpnv4 entry on r2 is not present
    Check that the associated mpls entry is not present
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    router = tgen.gears["r2"]

    vpnv4_checks = {
        "172.31.0.0/24": "r3",
    }
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r3".format(
            router.name
        )
    )
    label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)

    for prefix, label in label_ip_entries.items():
        logger.info("{}, filter prefix {} from r1".format(router.name, prefix))
        router.vtysh_cmd(
            "configure terminal\nroute-map rmap deny 1\nmatch ip next-hop address 192.0.2.1\n"
        )
        router.vtysh_cmd(
            "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 route-map rmap in\n"
        )
        logger.info(
            "{}, check that prefix {} is not present".format(router.name, prefix)
        )
        test_func = functools.partial(
            check_show_bgp_vpn_prefix_not_found,
            router,
            "ipv4",
            "172.31.0.0/24",
            "444:1",
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "{}, vpnv4 update {}, is still present".format(
            router.name, prefix
        )

        # diagnostic
        logger.info("Dumping mplsvpn nexthop table")
        router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)

        logger.info(
            "{}, check that show mpls table {} is not present".format(
                router.name, label
            )
        )
        test_func = functools.partial(
            check_show_mpls_table_entry_label_not_found, router, int(label)
        )
        success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
        assert success, "r1, mpls entry for {} with in_label {} still present".format(
            prefix, label
        )


def test_unfilter_vpn_network_from_r1():
    """
    unfilter network from r1
    check that the vpnv4 entry on r2 is present
    Check that the list of labels are present in 'show mpls table'
    Check that r3 received the prefixes with the new (next-hop,label)
    """
    tgen = get_topogen()
    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    router = tgen.gears["r2"]
    prefix = "172.31.0.0/24"

    logger.info("{}, filter prefix {} from r1".format(router.name, prefix))
    router.vtysh_cmd(
        "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 route-map rmap in\n"
    )

    logger.info("{}, check that prefix {} is present".format(router.name, prefix))
    test_func = functools.partial(
        check_show_bgp_vpn_prefix_found, router, "ipv4", prefix, "444:1"
    )
    success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
    assert success, "{}, vpnv4 update {}, is not present".format(router.name, prefix)

    vpnv4_checks = {
        "172.31.0.0/24": "r3",
    }
    logger.info(
        "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format(
            router.name
        )
    )
    check_show_bgp_vpn_ok(router, vpnv4_checks)

    # diagnostic
    logger.info("Dumping mplsvpn nexthop table")
    router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)


def test_memory_leak():
    "Run the memory leak test and report results."
    tgen = get_topogen()
    if not tgen.is_memleak_enabled():
        pytest.skip("Memory leak test/report is disabled")

    tgen.report_memory_leaks()


if __name__ == "__main__":
    args = ["-s"] + sys.argv[1:]
    sys.exit(pytest.main(args))
