mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-13 22:57:45 +00:00
tests: Add library support for BGP-Graceful-Restart automation
1. Adding APIs to common_config.py to support BGP-Graceful-Restart automation 2. Adding APIs to create BGP-GR config to bgp.py 3. Adding verification API for BGP-GR functionality Signed-off-by: Kuldeep Kashyap <kashyapk@vmware.com>
This commit is contained in:
parent
c65a7e26dc
commit
a5a52d6608
File diff suppressed because it is too large
Load Diff
@ -179,7 +179,7 @@ def run_frr_cmd(rnode, cmd, isjson=False):
|
|||||||
raise InvalidCLIError("No actual cmd passed")
|
raise InvalidCLIError("No actual cmd passed")
|
||||||
|
|
||||||
|
|
||||||
def create_common_configuration(tgen, router, data, config_type=None, build=False):
|
def create_common_configuration(tgen, router, data, config_type=None, build=False, load_config=True):
|
||||||
"""
|
"""
|
||||||
API to create object of class FRRConfig and also create frr_json.conf
|
API to create object of class FRRConfig and also create frr_json.conf
|
||||||
file. It will create interface and common configurations and save it to
|
file. It will create interface and common configurations and save it to
|
||||||
@ -216,6 +216,8 @@ def create_common_configuration(tgen, router, data, config_type=None, build=Fals
|
|||||||
|
|
||||||
if build:
|
if build:
|
||||||
mode = "a"
|
mode = "a"
|
||||||
|
elif not load_config:
|
||||||
|
mode = "a"
|
||||||
else:
|
else:
|
||||||
mode = "w"
|
mode = "w"
|
||||||
|
|
||||||
@ -236,12 +238,128 @@ def create_common_configuration(tgen, router, data, config_type=None, build=Fals
|
|||||||
frr_cfg_fd.close()
|
frr_cfg_fd.close()
|
||||||
|
|
||||||
# If configuration applied from build, it will done at last
|
# If configuration applied from build, it will done at last
|
||||||
if not build:
|
if not build and load_config:
|
||||||
load_config_to_router(tgen, router)
|
load_config_to_router(tgen, router)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def kill_router_daemons(tgen, router, daemons):
|
||||||
|
"""
|
||||||
|
Router's current config would be saved to /etc/frr/ for each deamon
|
||||||
|
and deamon would be killed forcefully using SIGKILL.
|
||||||
|
|
||||||
|
* `tgen` : topogen object
|
||||||
|
* `router`: Device under test
|
||||||
|
* `daemons`: list of daemons to be killed
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Entering lib API: kill_router_daemons()")
|
||||||
|
|
||||||
|
try:
|
||||||
|
router_list = tgen.routers()
|
||||||
|
|
||||||
|
# Saving router config to /etc/frr, which will be loaded to router
|
||||||
|
# when it starts
|
||||||
|
router_list[router].vtysh_cmd("write memory")
|
||||||
|
|
||||||
|
# Kill Daemons
|
||||||
|
result = router_list[router].killDaemons(daemons)
|
||||||
|
if len(result) > 0:
|
||||||
|
assert "Errors found post shutdown - details follow:" == 0, result
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
errormsg = traceback.format_exc()
|
||||||
|
logger.error(errormsg)
|
||||||
|
return errormsg
|
||||||
|
|
||||||
|
|
||||||
|
def start_router_daemons(tgen, router, daemons):
|
||||||
|
"""
|
||||||
|
Daemons defined by user would be started
|
||||||
|
|
||||||
|
* `tgen` : topogen object
|
||||||
|
* `router`: Device under test
|
||||||
|
* `daemons`: list of daemons to be killed
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Entering lib API: start_router_daemons()")
|
||||||
|
|
||||||
|
try:
|
||||||
|
router_list = tgen.routers()
|
||||||
|
|
||||||
|
# Start daemons
|
||||||
|
result = router_list[router].startDaemons(daemons)
|
||||||
|
sleep(5)
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
errormsg = traceback.format_exc()
|
||||||
|
logger.error(errormsg)
|
||||||
|
return errormsg
|
||||||
|
|
||||||
|
logger.debug("Exiting lib API: start_router_daemons()")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def kill_mininet_routers_process(tgen):
|
||||||
|
"""
|
||||||
|
Kill all mininet stale router' processes
|
||||||
|
|
||||||
|
* `tgen` : topogen object
|
||||||
|
"""
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
for rname, router in router_list.iteritems():
|
||||||
|
daemon_list = [
|
||||||
|
"zebra",
|
||||||
|
"ospfd",
|
||||||
|
"ospf6d",
|
||||||
|
"bgpd",
|
||||||
|
"ripd",
|
||||||
|
"ripngd",
|
||||||
|
"isisd",
|
||||||
|
"pimd",
|
||||||
|
"ldpd",
|
||||||
|
"staticd",
|
||||||
|
]
|
||||||
|
for daemon in daemon_list:
|
||||||
|
router.run("killall -9 {}".format(daemon))
|
||||||
|
|
||||||
|
|
||||||
|
def check_router_status(tgen):
|
||||||
|
"""
|
||||||
|
Check if all daemons are running for all routers in topology
|
||||||
|
|
||||||
|
* `tgen` : topogen object
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Entering lib API: start_router_daemons()")
|
||||||
|
|
||||||
|
try:
|
||||||
|
router_list = tgen.routers()
|
||||||
|
for router, rnode in router_list.iteritems():
|
||||||
|
|
||||||
|
result = rnode.check_router_running()
|
||||||
|
if result != "":
|
||||||
|
daemons = []
|
||||||
|
if "bgpd" in result:
|
||||||
|
daemons.append("bgpd")
|
||||||
|
if "zebra" in result:
|
||||||
|
daemons.append("zebra")
|
||||||
|
|
||||||
|
rnode.startDaemons(daemons)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
errormsg = traceback.format_exc()
|
||||||
|
logger.error(errormsg)
|
||||||
|
return errormsg
|
||||||
|
|
||||||
|
logger.debug("Exiting lib API: start_router_daemons()")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def reset_config_on_routers(tgen, routerName=None):
|
def reset_config_on_routers(tgen, routerName=None):
|
||||||
"""
|
"""
|
||||||
Resets configuration on routers to the snapshot created using input JSON
|
Resets configuration on routers to the snapshot created using input JSON
|
||||||
@ -414,6 +532,69 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_frr_ipv6_linklocal(tgen, router, intf=None):
|
||||||
|
"""
|
||||||
|
API to get the link local ipv6 address of a perticular interface using
|
||||||
|
FRR command 'show interface'
|
||||||
|
|
||||||
|
* `tgen`: tgen onject
|
||||||
|
* `router` : router for which hightest interface should be
|
||||||
|
calculated
|
||||||
|
* `intf` : interface for which linklocal address needs to be taken
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
linklocal = get_frr_ipv6_linklocal(tgen, router, "intf1", RED_A)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
1) array of interface names to link local ips.
|
||||||
|
"""
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
for rname, rnode in router_list.iteritems():
|
||||||
|
if rname != router:
|
||||||
|
continue
|
||||||
|
|
||||||
|
linklocal = []
|
||||||
|
|
||||||
|
cmd = "show interface"
|
||||||
|
|
||||||
|
ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
|
||||||
|
|
||||||
|
# Fix newlines (make them all the same)
|
||||||
|
ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
|
||||||
|
|
||||||
|
interface = None
|
||||||
|
ll_per_if_count = 0
|
||||||
|
for line in ifaces:
|
||||||
|
# Interface name
|
||||||
|
m = re_search("Interface ([a-zA-Z0-9-]+) is", line)
|
||||||
|
if m:
|
||||||
|
interface = m.group(1).split(" ")[0]
|
||||||
|
ll_per_if_count = 0
|
||||||
|
|
||||||
|
# Interface ip
|
||||||
|
m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)", line)
|
||||||
|
if m1:
|
||||||
|
local = m1.group(1)
|
||||||
|
ll_per_if_count += 1
|
||||||
|
if ll_per_if_count > 1:
|
||||||
|
linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
|
||||||
|
else:
|
||||||
|
linklocal += [[interface, local]]
|
||||||
|
|
||||||
|
if linklocal:
|
||||||
|
if intf:
|
||||||
|
return [_linklocal[1] for _linklocal in linklocal if _linklocal[0] == intf][
|
||||||
|
0
|
||||||
|
].split("/")[0]
|
||||||
|
return linklocal
|
||||||
|
else:
|
||||||
|
errormsg = "Link local ip missing on router {}"
|
||||||
|
return errormsg
|
||||||
|
|
||||||
|
|
||||||
def start_topology(tgen):
|
def start_topology(tgen):
|
||||||
"""
|
"""
|
||||||
Starting topology, create tmp files which are loaded to routers
|
Starting topology, create tmp files which are loaded to routers
|
||||||
@ -1741,15 +1922,13 @@ def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
|
|||||||
if type(next_hop) is not list:
|
if type(next_hop) is not list:
|
||||||
next_hop = [next_hop]
|
next_hop = [next_hop]
|
||||||
|
|
||||||
for index, nh in enumerate(next_hop):
|
for nh in next_hop:
|
||||||
if (
|
for nh_json in rib_routes_json[st_rt][0]["nexthops"]:
|
||||||
rib_routes_json[st_rt][0]["nexthops"][
|
if nh != nh_json["ip"]:
|
||||||
index
|
continue
|
||||||
]["ip"]
|
|
||||||
== nh
|
|
||||||
):
|
|
||||||
nh_found = True
|
nh_found = True
|
||||||
else:
|
|
||||||
|
if not nh_found:
|
||||||
errormsg = (
|
errormsg = (
|
||||||
"Nexthop {} is Missing"
|
"Nexthop {} is Missing"
|
||||||
" for {} route {} in "
|
" for {} route {} in "
|
||||||
|
@ -1195,21 +1195,6 @@ class Router(Node):
|
|||||||
errors = ""
|
errors = ""
|
||||||
if assertOnError and len(errors) > 0:
|
if assertOnError and len(errors) > 0:
|
||||||
assert "Errors found - details follow:" == 0, errors
|
assert "Errors found - details follow:" == 0, errors
|
||||||
|
|
||||||
errors = ""
|
|
||||||
# Check Memory leaks
|
|
||||||
if pytest.config.getoption('--valgrind'):
|
|
||||||
mem_leak, mem_data = self.check_mem_leaks_valgrind(
|
|
||||||
daemon
|
|
||||||
)
|
|
||||||
if mem_leak:
|
|
||||||
for d_name, data in mem_data.items():
|
|
||||||
if data:
|
|
||||||
logger.error(
|
|
||||||
"Memory leaks in router [%s] for "
|
|
||||||
"daemon [%s]", self.name, daemon)
|
|
||||||
errors = "Router [%s] has memory leak, Check" \
|
|
||||||
" logs for details." % self.name
|
|
||||||
else:
|
else:
|
||||||
daemonsNotRunning.append(daemon)
|
daemonsNotRunning.append(daemon)
|
||||||
if len(daemonsNotRunning) > 0:
|
if len(daemonsNotRunning) > 0:
|
||||||
|
Loading…
Reference in New Issue
Block a user