diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 4d935b9538c5..1514a30eabf4 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -1099,6 +1099,26 @@ def has_type(self, rtype): def has_mpls(self): return self.net.hasmpls + def start_dplane_server(self): + "Starts FRR FPM Simulator for this router." + dir_path = f"{self.logdir}/{self.name}" + log_path = f"{dir_path}/dplaneserver.log" + json_path = f"{dir_path}/output.json" + self.cmd(f"mkdir -p {dir_path}") + run_cmd = f"/usr/lib/frr/dplaneserver -f {json_path} -d > {log_path} 2>&1 &" + file_cmd = f"touch {json_path}" + self.cmd_raises(file_cmd, warn=False) + try: + self.cmd_raises(run_cmd, warn=False) + except subprocess.CalledProcessError as error: + self.logger.error( + '%s: Failed to launch "%s" daemon (%d) using: %s:', + self, + "FPM ProtoBuf Server", + error.returncode, + error.cmd, + ) + class TopoSwitch(TopoGear): """ diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6c2d42ef40ab..114824b4fc17 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -56,6 +56,7 @@ markers = staticd: Tests that run against STATICD vrrpd: Tests that run against VRRPD snmp: Tests that run against snmp changes + dplane: Tests that run against dplane in zebra [topogen] # Default configuration values diff --git a/tests/topotests/zebra_fpm_pb/__init__.py b/tests/topotests/zebra_fpm_pb/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/zebra_fpm_pb/r1/bgpd.conf b/tests/topotests/zebra_fpm_pb/r1/bgpd.conf new file mode 100644 index 000000000000..b7df693f1c21 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/zebra_fpm_pb/r1/ref.json b/tests/topotests/zebra_fpm_pb/r1/ref.json new file mode 100644 index 000000000000..a4c7a48555e6 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/ref.json @@ -0,0 +1,72 @@ +[ + { + "vrfId":0, + "addressFamily":2, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"10.254.254.1", + "prefixLength":32 + }, + { + "vrfId":0, + "addressFamily":10, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"fe80::", + "prefixLength":64 + }, + { + "vrfId":0, + "addressFamily":10, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"2001:db8:1::", + "prefixLength":64 + }, + { + "vrfId":0, + "addressFamily":10, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"2001:db8:1::", + "prefixLength":64 + }, + { + "vrfId":0, + "addressFamily":2, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"10.0.3.2", + "prefixLength":32 + }, + { + "vrfId":0, + "addressFamily":2, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"10.254.254.2", + "prefixLength":32 + }, + { + "vrfId":0, + "addressFamily":10, + "metric":0, + "subAddressFamily":1, + "hasRouteType":1, + "routeType":1, + "prefix":"2001:db8:4::", + "prefixLength":64 + } +] diff --git a/tests/topotests/zebra_fpm_pb/r1/zebra.conf b/tests/topotests/zebra_fpm_pb/r1/zebra.conf new file mode 100644 index 000000000000..7fe5eb218fc1 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/zebra_fpm_pb/r2/bgpd.conf b/tests/topotests/zebra_fpm_pb/r2/bgpd.conf new file mode 100644 index 000000000000..be487b06b8dd --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r2/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 102 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/zebra_fpm_pb/r2/zebra.conf b/tests/topotests/zebra_fpm_pb/r2/zebra.conf new file mode 100644 index 000000000000..5fe582b9f90d --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r2/zebra.conf @@ -0,0 +1,15 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 10.0.3.2/32 +! +interface r2-eth2 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/zebra_fpm_pb/r3/zebra.conf b/tests/topotests/zebra_fpm_pb/r3/zebra.conf new file mode 100644 index 000000000000..96fd08c72901 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r3/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 10.0.3.1/24 +! diff --git a/tests/topotests/zebra_fpm_pb/r4/zebra.conf b/tests/topotests/zebra_fpm_pb/r4/zebra.conf new file mode 100644 index 000000000000..e4f8fd8514a0 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r4/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:4::1/64 +! diff --git a/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py b/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py new file mode 100644 index 000000000000..227a4a514ee4 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# Copyright (C) 2023 Alibaba, Inc. Hongyu Li +# + +""" +test_zebra_fpm_pb.py: Test the FRR Zebra dplane_fpm_pb +""" +import os +import sys +import pytest +import json +import functools +from time import sleep +# 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.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.dplane] + +def setup_module(mod): + "Sets up the pytest environment" + topodef = { + "s1": ("r1", "r2"), + "s2": ("r2", "r3"), + "s3": ("r2", "r4"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + print(f"starting dplaneserver for {rname}") + router.start_dplane_server() + + for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + router.load_config(TopoRouter.RD_ZEBRA, daemon_file,"-M dplane_fpm_pb") + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + router.load_config(TopoRouter.RD_BGP, daemon_file) + # Initialize all routers. + tgen.start_router() + logger.info("start test routers failure") + # Verify if routers are running + for rname, router in router_list.items(): + result = router.check_router_running() + if result != "": + logger.info("{} router running failure".format(rname)) + pytest.skip(result) + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def open_json_file(filename): + try: + with open(filename, "r") as f: + content=f.read() + if content=="": + return json.loads("[]") + return json.loads(content) + except IOError: + assert False, "Could not read file {}".format(filename) + +def do_check(name,result_file, expected_file): + def format_json_file(file): + f=open(file,"r+") + content=f.read() + if len(content) == 0 or content[0] == '[': + logger.info(" {} doesn't need formatting".format(file)) + f.close() + return + logger.info("origin outfile is:") + logger.info(content) + content=content.replace("}","},",content.count("}")-1) + content="[\n"+content+"\n]" + f.close() + f=open(file,"w+") + f.write(content) + f.close() + logger.info("outfile is:") + logger.info(content) + + def _check(name,result_file, expected_file): + logger.info("polling") + + tgen = get_topogen() + router = tgen.gears[name] + dir_path = f"{router.logdir}/{router.name}" + json_path = f"{dir_path}/{result_file}" + fpm_log_file = "dplaneserver.log" + log_path = f"{dir_path}/{fpm_log_file}" + fp = open(log_path,"r+") + log_content = fp.read() + logger.info(log_content) + fp.close() + + format_json_file(json_path) + output = open_json_file(json_path) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, result_file, expected_file)) + tgen = get_topogen() + result = _check(name, result_file, expected_file) + assert result is None, "Failed" + +def test_zebra_dplane_fpm_pb(): + logger.info("start test_zebra_dplane_fpm_pb") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + sleep(5) + do_check("r1", "output.json", "r1/ref.json")