tests: switch to munet

Signed-off-by: Christian Hopps <chopps@labn.net>
This commit is contained in:
Christian Hopps 2023-03-24 13:06:38 +00:00 committed by Christian Hopps
parent 352ddc72b7
commit 60e037780e
8 changed files with 264 additions and 1448 deletions

View File

@ -7,21 +7,23 @@ import glob
import os import os
import pdb import pdb
import re import re
import resource
import subprocess import subprocess
import sys import sys
import time import time
import resource
import pytest import pytest
import lib.fixtures import lib.fixtures
from lib import topolog from lib import topolog
from lib.micronet import Commander, proc_error from lib.micronet_compat import Mininet
from lib.micronet_cli import cli
from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous
from lib.topogen import diagnose_env, get_topogen from lib.topogen import diagnose_env, get_topogen
from lib.topolog import logger from lib.topolog import logger
from lib.topotest import g_extra_config as topotest_extra_config from lib.topotest import g_extra_config as topotest_extra_config
from lib.topotest import json_cmp_result from lib.topotest import json_cmp_result
from munet.base import Commander, proc_error
from munet.cleanup import cleanup_current, cleanup_previous
from munet import cli
def pytest_addoption(parser): def pytest_addoption(parser):
@ -501,7 +503,7 @@ def pytest_runtest_makereport(item, call):
# Really would like something better than using this global here. # Really would like something better than using this global here.
# Not all tests use topogen though so get_topogen() won't work. # Not all tests use topogen though so get_topogen() won't work.
if Mininet.g_mnet_inst: if Mininet.g_mnet_inst:
cli(Mininet.g_mnet_inst, title=title, background=False) cli.cli(Mininet.g_mnet_inst, title=title, background=False)
else: else:
logger.error("Could not launch CLI b/c no mininet exists yet") logger.error("Could not launch CLI b/c no mininet exists yet")
@ -515,7 +517,7 @@ def pytest_runtest_makereport(item, call):
user = user.strip() user = user.strip()
if user == "cli": if user == "cli":
cli(Mininet.g_mnet_inst) cli.cli(Mininet.g_mnet_inst)
elif user == "pdb": elif user == "pdb":
pdb.set_trace() # pylint: disable=forgotten-debug-statement pdb.set_trace() # pylint: disable=forgotten-debug-statement
elif user: elif user:

View File

@ -21,7 +21,8 @@ try:
import grpc import grpc
import grpc_tools import grpc_tools
from micronet import commander sys.path.append(os.path.dirname(CWD))
from munet.base import commander
commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .") commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
commander.cmd_raises( commander.cmd_raises(

File diff suppressed because it is too large Load Diff

View File

@ -1,306 +0,0 @@
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# July 24 2021, Christian Hopps <chopps@labn.net>
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
#
import argparse
import logging
import os
import pty
import re
import readline
import select
import socket
import subprocess
import sys
import tempfile
import termios
import tty
ENDMARKER = b"\x00END\x00"
def lineiter(sock):
s = ""
while True:
sb = sock.recv(256)
if not sb:
return
s += sb.decode("utf-8")
i = s.find("\n")
if i != -1:
yield s[:i]
s = s[i + 1 :]
def spawn(unet, host, cmd):
if sys.stdin.isatty():
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())
try:
master_fd, slave_fd = pty.openpty()
# use os.setsid() make it run in a new process group, or bash job
# control will not be enabled
p = unet.hosts[host].popen(
cmd,
preexec_fn=os.setsid,
stdin=slave_fd,
stdout=slave_fd,
stderr=slave_fd,
universal_newlines=True,
)
while p.poll() is None:
r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25)
if sys.stdin in r:
d = os.read(sys.stdin.fileno(), 10240)
os.write(master_fd, d)
elif master_fd in r:
o = os.read(master_fd, 10240)
if o:
os.write(sys.stdout.fileno(), o)
finally:
# restore tty settings back
if sys.stdin.isatty():
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
def doline(unet, line, writef):
def host_cmd_split(unet, cmd):
csplit = cmd.split()
for i, e in enumerate(csplit):
if e not in unet.hosts:
break
hosts = csplit[:i]
if not hosts:
hosts = sorted(unet.hosts.keys())
cmd = " ".join(csplit[i:])
return hosts, cmd
line = line.strip()
m = re.match(r"^(\S+)(?:\s+(.*))?$", line)
if not m:
return True
cmd = m.group(1)
oargs = m.group(2) if m.group(2) else ""
if cmd == "q" or cmd == "quit":
return False
if cmd == "hosts":
writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys())))
elif cmd in ["term", "vtysh", "xterm"]:
args = oargs.split()
if not args or (len(args) == 1 and args[0] == "*"):
args = sorted(unet.hosts.keys())
hosts = [unet.hosts[x] for x in args if x in unet.hosts]
for host in hosts:
if cmd == "t" or cmd == "term":
host.run_in_window("bash", title="sh-%s" % host)
elif cmd == "v" or cmd == "vtysh":
host.run_in_window("vtysh", title="vt-%s" % host)
elif cmd == "x" or cmd == "xterm":
host.run_in_window("bash", title="sh-%s" % host, forcex=True)
elif cmd == "sh":
hosts, cmd = host_cmd_split(unet, oargs)
for host in hosts:
if sys.stdin.isatty():
spawn(unet, host, cmd)
else:
if len(hosts) > 1:
writef("------ Host: %s ------\n" % host)
output = unet.hosts[host].cmd_legacy(cmd)
writef(output)
if len(hosts) > 1:
writef("------- End: %s ------\n" % host)
writef("\n")
elif cmd == "h" or cmd == "help":
writef(
"""
Commands:
help :: this help
sh [hosts] <shell-command> :: execute <shell-command> on <host>
term [hosts] :: open shell terminals for hosts
vtysh [hosts] :: open vtysh terminals for hosts
[hosts] <vtysh-command> :: execute vtysh-command on hosts\n\n"""
)
else:
hosts, cmd = host_cmd_split(unet, line)
for host in hosts:
if len(hosts) > 1:
writef("------ Host: %s ------\n" % host)
output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd))
writef(output)
if len(hosts) > 1:
writef("------- End: %s ------\n" % host)
writef("\n")
return True
def cli_server_setup(unet):
sockdir = tempfile.mkdtemp("-sockdir", "pyt")
sockpath = os.path.join(sockdir, "cli-server.sock")
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(10)
sock.bind(sockpath)
sock.listen(1)
return sock, sockdir, sockpath
except Exception:
unet.cmd_status("rm -rf " + sockdir)
raise
def cli_server(unet, server_sock):
sock, addr = server_sock.accept()
# Go into full non-blocking mode now
sock.settimeout(None)
for line in lineiter(sock):
line = line.strip()
def writef(x):
xb = x.encode("utf-8")
sock.send(xb)
if not doline(unet, line, writef):
return
sock.send(ENDMARKER)
def cli_client(sockpath, prompt="unet> "):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect(sockpath)
# Go into full non-blocking mode now
sock.settimeout(None)
print("\n--- Micronet CLI Starting ---\n\n")
while True:
if sys.version_info[0] == 2:
line = raw_input(prompt) # pylint: disable=E0602
else:
line = input(prompt)
if line is None:
return
# Need to put \n back
line += "\n"
# Send the CLI command
sock.send(line.encode("utf-8"))
def bendswith(b, sentinel):
slen = len(sentinel)
return len(b) >= slen and b[-slen:] == sentinel
# Collect the output
rb = b""
while not bendswith(rb, ENDMARKER):
lb = sock.recv(4096)
if not lb:
return
rb += lb
# Remove the marker
rb = rb[: -len(ENDMARKER)]
# Write the output
sys.stdout.write(rb.decode("utf-8"))
def local_cli(unet, outf, prompt="unet> "):
print("\n--- Micronet CLI Starting ---\n\n")
while True:
if sys.version_info[0] == 2:
line = raw_input(prompt) # pylint: disable=E0602
else:
line = input(prompt)
if line is None:
return
if not doline(unet, line, outf.write):
return
def cli(
unet,
histfile=None,
sockpath=None,
force_window=False,
title=None,
prompt=None,
background=True,
):
logger = logging.getLogger("cli-client")
if prompt is None:
prompt = "unet> "
if force_window or not sys.stdin.isatty():
# Run CLI in another window b/c we have no tty.
sock, sockdir, sockpath = cli_server_setup(unet)
python_path = unet.get_exec_path(["python3", "python"])
us = os.path.realpath(__file__)
cmd = "{} {}".format(python_path, us)
if histfile:
cmd += " --histfile=" + histfile
if title:
cmd += " --prompt={}".format(title)
cmd += " " + sockpath
try:
unet.run_in_window(cmd, new_window=True, title=title, background=background)
return cli_server(unet, sock)
finally:
unet.cmd_status("rm -rf " + sockdir)
if not unet:
logger.debug("client-cli using sockpath %s", sockpath)
try:
if histfile is None:
histfile = os.path.expanduser("~/.micronet-history.txt")
if not os.path.exists(histfile):
if unet:
unet.cmd("touch " + histfile)
else:
subprocess.run("touch " + histfile)
if histfile:
readline.read_history_file(histfile)
except Exception:
pass
try:
if sockpath:
cli_client(sockpath, prompt=prompt)
else:
local_cli(unet, sys.stdout, prompt=prompt)
except EOFError:
pass
except Exception as ex:
logger.critical("cli: got exception: %s", ex, exc_info=True)
raise
finally:
readline.write_history_file(histfile)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log")
logger = logging.getLogger("cli-client")
logger.info("Start logging cli-client")
parser = argparse.ArgumentParser()
parser.add_argument("--histfile", help="file to user for history")
parser.add_argument("--prompt-text", help="prompt string to use")
parser.add_argument("socket", help="path to pair of sockets to communicate over")
args = parser.parse_args()
prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> "
cli(None, args.histfile, args.socket, prompt=prompt)

View File

@ -3,140 +3,43 @@
# #
# July 11 2021, Christian Hopps <chopps@labn.net> # July 11 2021, Christian Hopps <chopps@labn.net>
# #
# Copyright (c) 2021, LabN Consulting, L.L.C # Copyright (c) 2021-2023, LabN Consulting, L.L.C
# #
import ipaddress
import glob
import logging
import os import os
import signal
import time
from lib.micronet import LinuxNamespace, Micronet from munet import cli
from lib.micronet_cli import cli from munet.base import BaseMunet, LinuxNamespace
def get_pids_with_env(has_var, has_val=None):
result = {}
for pidenv in glob.iglob("/proc/*/environ"):
pid = pidenv.split("/")[2]
try:
with open(pidenv, "rb") as rfb:
envlist = [
x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0")
]
envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist]
envdict = dict(envlist)
if has_var not in envdict:
continue
if has_val is None:
result[pid] = envdict
elif envdict[has_var] == str(has_val):
result[pid] = envdict
except Exception:
# E.g., process exited and files are gone
pass
return result
def _kill_piddict(pids_by_upid, sig):
for upid, pids in pids_by_upid:
logging.info(
"Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid
)
for pid in pids:
try:
os.kill(int(pid), sig)
except Exception:
pass
def _get_our_pids():
ourpid = str(os.getpid())
piddict = get_pids_with_env("MICRONET_PID", ourpid)
pids = [x for x in piddict if x != ourpid]
if pids:
return {ourpid: pids}
return {}
def _get_other_pids():
piddict = get_pids_with_env("MICRONET_PID")
unet_pids = {d["MICRONET_PID"] for d in piddict.values()}
pids_by_upid = {p: set() for p in unet_pids}
for pid, envdict in piddict.items():
pids_by_upid[envdict["MICRONET_PID"]].add(pid)
# Filter out any child pid sets whos micronet pid is still running
return {x: y for x, y in pids_by_upid.items() if x not in y}
def _get_pids_by_upid(ours):
if ours:
return _get_our_pids()
return _get_other_pids()
def _cleanup_pids(ours):
pids_by_upid = _get_pids_by_upid(ours).items()
if not pids_by_upid:
return
_kill_piddict(pids_by_upid, signal.SIGTERM)
# Give them 5 second to exit cleanly
logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
for _ in range(0, 5):
pids_by_upid = _get_pids_by_upid(ours).items()
if not pids_by_upid:
return
time.sleep(1)
pids_by_upid = _get_pids_by_upid(ours).items()
_kill_piddict(pids_by_upid, signal.SIGKILL)
def cleanup_current():
"""Attempt to cleanup preview runs.
Currently this only scans for old processes.
"""
logging.info("reaping current micronet processes")
_cleanup_pids(True)
def cleanup_previous():
"""Attempt to cleanup preview runs.
Currently this only scans for old processes.
"""
logging.info("reaping past micronet processes")
_cleanup_pids(False)
class Node(LinuxNamespace): class Node(LinuxNamespace):
"""Node (mininet compat).""" """Node (mininet compat)."""
def __init__(self, name, **kwargs): def __init__(self, name, rundir=None, **kwargs):
"""
Create a Node.
"""
self.params = kwargs
nkwargs = {}
if "unet" in kwargs:
nkwargs["unet"] = kwargs["unet"]
if "private_mounts" in kwargs: if "private_mounts" in kwargs:
private_mounts = kwargs["private_mounts"] nkwargs["private_mounts"] = kwargs["private_mounts"]
else:
private_mounts = kwargs.get("privateDirs", [])
logger = kwargs.get("logger") # This is expected by newer munet CLI code
self.config_dirname = ""
self.config = {"kind": "frr"}
self.mgmt_ip = None
self.mgmt_ip6 = None
super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts) super().__init__(name, **nkwargs)
self.rundir = self.unet.rundir.joinpath(self.name)
def cmd(self, cmd, **kwargs): def cmd(self, cmd, **kwargs):
"""Execute a command, joins stdout, stderr, ignores exit status.""" """Execute a command, joins stdout, stderr, ignores exit status."""
return super(Node, self).cmd_legacy(cmd, **kwargs) return super(Node, self).cmd_legacy(cmd, **kwargs)
def config(self, lo="up", **params): def config_host(self, lo="up", **params):
"""Called by Micronet when topology is built (but not started).""" """Called by Micronet when topology is built (but not started)."""
# mininet brings up loopback here. # mininet brings up loopback here.
del params del params
@ -148,20 +51,76 @@ class Node(LinuxNamespace):
def terminate(self): def terminate(self):
return return
def add_vlan(self, vlanname, linkiface, vlanid):
self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid)
ip_path = self.get_exec_path("ip")
assert ip_path, "XXX missing ip command!"
self.cmd_raises(
[
ip_path,
"link",
"add",
"link",
linkiface,
"name",
vlanname,
"type",
"vlan",
"id",
vlanid,
]
)
self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"])
def add_loop(self, loopname):
self.logger.debug("Adding Linux iface: %s", loopname)
ip_path = self.get_exec_path("ip")
assert ip_path, "XXX missing ip command!"
self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"])
self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"])
def add_l3vrf(self, vrfname, tableid):
self.logger.debug("Adding Linux VRF: %s", vrfname)
ip_path = self.get_exec_path("ip")
assert ip_path, "XXX missing ip command!"
self.cmd_raises(
[ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid]
)
self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"])
def del_iface(self, iface):
self.logger.debug("Removing Linux Iface: %s", iface)
ip_path = self.get_exec_path("ip")
assert ip_path, "XXX missing ip command!"
self.cmd_raises([ip_path, "link", "del", iface])
def attach_iface_to_l3vrf(self, ifacename, vrfname):
self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname)
ip_path = self.get_exec_path("ip")
assert ip_path, "XXX missing ip command!"
if vrfname:
self.cmd_raises(
[ip_path, "link", "set", "dev", ifacename, "master", vrfname]
)
else:
self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"])
set_cwd = LinuxNamespace.set_ns_cwd
class Topo(object): # pylint: disable=R0205 class Topo(object): # pylint: disable=R0205
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
raise Exception("Remove Me") raise Exception("Remove Me")
class Mininet(Micronet): class Mininet(BaseMunet):
""" """
Mininet using Micronet. Mininet using Micronet.
""" """
g_mnet_inst = None g_mnet_inst = None
def __init__(self): def __init__(self, rundir=None):
""" """
Create a Micronet. Create a Micronet.
""" """
@ -179,7 +138,146 @@ class Mininet(Micronet):
# to set permissions to root:frr 770 to make this unneeded in that case # to set permissions to root:frr 770 to make this unneeded in that case
# os.umask(0) # os.umask(0)
super(Mininet, self).__init__() super(Mininet, self).__init__(pid=False, rundir=rundir)
# From munet/munet/native.py
with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
f.write(f"{self.pid}\n")
with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
f.write(f'{" ".join([str(x) for x in self.pids])}\n')
hosts_file = os.path.join(self.rundir, "hosts.txt")
with open(hosts_file, "w", encoding="ascii") as hf:
hf.write(
f"""127.0.0.1\tlocalhost {self.name}
::1\tip6-localhost ip6-loopback
fe00::0\tip6-localnet
ff00::0\tip6-mcastprefix
ff02::1\tip6-allnodes
ff02::2\tip6-allrouters
"""
)
self.bind_mount(hosts_file, "/etc/hosts")
# Common CLI commands for any topology
cdict = {
"commands": [
#
# Window commands.
#
{
"name": "pcap",
"format": "pcap NETWORK",
"help": (
"capture packets from NETWORK into file capture-NETWORK.pcap"
" the command is run within a new window which also shows"
" packet summaries. NETWORK can also be an interface specified"
" as HOST:INTF. To capture inside the host namespace."
),
"exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap",
"top-level": True,
"new-window": {"background": True},
},
{
"name": "term",
"format": "term HOST [HOST ...]",
"help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all",
"exec": "bash",
"new-window": True,
},
{
"name": "vtysh",
"exec": "/usr/bin/vtysh",
"format": "vtysh ROUTER [ROUTER ...]",
"new-window": True,
"kinds": ["frr"],
},
{
"name": "xterm",
"format": "xterm HOST [HOST ...]",
"help": "open XTerm[s] on HOST[S], * for all",
"exec": "bash",
"new-window": {
"forcex": True,
},
},
{
"name": "logd",
"exec": "tail -F %RUNDIR%/{}.log",
"format": "logd HOST [HOST ...] DAEMON",
"help": (
"tail -f on the logfile of the given "
"DAEMON for the given HOST[S]"
),
"new-window": True,
},
{
"name": "stdlog",
"exec": (
"[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log "
"|| tail -F /var/log/frr.log"
),
"format": "stdlog HOST [HOST ...]",
"help": "tail -f on the `frr.log` for the given HOST[S]",
"new-window": True,
},
{
"name": "stdout",
"exec": "tail -F %RUNDIR%/{0}.err",
"format": "stdout HOST [HOST ...] DAEMON",
"help": (
"tail -f on the stdout of the given DAEMON for the given HOST[S]"
),
"new-window": True,
},
{
"name": "stderr",
"exec": "tail -F %RUNDIR%/{0}.out",
"format": "stderr HOST [HOST ...] DAEMON",
"help": (
"tail -f on the stderr of the given DAEMON for the given HOST[S]"
),
"new-window": True,
},
#
# Non-window commands.
#
{
"name": "",
"exec": "vtysh -c '{}'",
"format": "[ROUTER ...] COMMAND",
"help": "execute vtysh COMMAND on the router[s]",
"kinds": ["frr"],
},
{
"name": "sh",
"format": "[HOST ...] sh <SHELL-COMMAND>",
"help": "execute <SHELL-COMMAND> on hosts",
"exec": "{}",
},
{
"name": "shi",
"format": "[HOST ...] shi <INTERACTIVE-COMMAND>",
"help": "execute <INTERACTIVE-COMMAND> on HOST[s]",
"exec": "{}",
"interactive": True,
},
]
}
cli.add_cli_config(self, cdict)
# shellopt = (
# self.pytest_config.getoption("--shell") if self.pytest_config else None
# )
# shellopt = shellopt if shellopt is not None else ""
# if shellopt == "all" or "." in shellopt.split(","):
# self.run_in_window("bash")
# This is expected by newer munet CLI code
self.config_dirname = ""
self.config = {}
self.logger.debug("%s: Creating", self) self.logger.debug("%s: Creating", self)
@ -217,12 +315,15 @@ class Mininet(Micronet):
host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf)) host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf))
# can be used by munet cli
host.mgmt_ip = ipaddress.ip_address(ip)
if "defaultRoute" in params: if "defaultRoute" in params:
host.cmd_raises( host.cmd_raises(
"ip route add default {}".format(params["defaultRoute"]) "ip route add default {}".format(params["defaultRoute"])
) )
host.config() host.config_host()
self.configured_hosts.add(name) self.configured_hosts.add(name)
@ -248,4 +349,4 @@ class Mininet(Micronet):
Mininet.g_mnet_inst = None Mininet.g_mnet_inst = None
def cli(self): def cli(self):
cli(self) cli.cli(self)

View File

@ -212,7 +212,7 @@ class Topogen(object):
# Mininet(Micronet) to build the actual topology. # Mininet(Micronet) to build the actual topology.
assert not inspect.isclass(topodef) assert not inspect.isclass(topodef)
self.net = Mininet() self.net = Mininet(rundir=self.logdir)
# Adjust the parent namespace # Adjust the parent namespace
topotest.fix_netns_limits(self.net) topotest.fix_netns_limits(self.net)
@ -752,8 +752,8 @@ class TopoRouter(TopoGear):
""" """
super(TopoRouter, self).__init__(tgen, name, **params) super(TopoRouter, self).__init__(tgen, name, **params)
self.routertype = params.get("routertype", "frr") self.routertype = params.get("routertype", "frr")
if "privateDirs" not in params: if "private_mounts" not in params:
params["privateDirs"] = self.PRIVATE_DIRS params["private_mounts"] = self.PRIVATE_DIRS
# Propagate the router log directory # Propagate the router log directory
logfile = self._setup_tmpdir() logfile = self._setup_tmpdir()
@ -1100,7 +1100,7 @@ class TopoHost(TopoGear):
* `ip`: the IP address (string) for the host interface * `ip`: the IP address (string) for the host interface
* `defaultRoute`: the default route that will be installed * `defaultRoute`: the default route that will be installed
(e.g. 'via 10.0.0.1') (e.g. 'via 10.0.0.1')
* `privateDirs`: directories that will be mounted on a different domain * `private_mounts`: directories that will be mounted on a different domain
(e.g. '/etc/important_dir'). (e.g. '/etc/important_dir').
""" """
super(TopoHost, self).__init__(tgen, name, **params) super(TopoHost, self).__init__(tgen, name, **params)
@ -1120,10 +1120,10 @@ class TopoHost(TopoGear):
def __str__(self): def __str__(self):
gear = super(TopoHost, self).__str__() gear = super(TopoHost, self).__str__()
gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format( gear += ' TopoHost<ip="{}",defaultRoute="{}",private_mounts="{}">'.format(
self.params["ip"], self.params["ip"],
self.params["defaultRoute"], self.params["defaultRoute"],
str(self.params["privateDirs"]), str(self.params["private_mounts"]),
) )
return gear return gear
@ -1146,10 +1146,10 @@ class TopoExaBGP(TopoHost):
(e.g. 'via 10.0.0.1') (e.g. 'via 10.0.0.1')
Note: the different between a host and a ExaBGP peer is that this class Note: the different between a host and a ExaBGP peer is that this class
has a privateDirs already defined and contains functions to handle ExaBGP has a private_mounts already defined and contains functions to handle
things. ExaBGP things.
""" """
params["privateDirs"] = self.PRIVATE_DIRS params["private_mounts"] = self.PRIVATE_DIRS
super(TopoExaBGP, self).__init__(tgen, name, **params) super(TopoExaBGP, self).__init__(tgen, name, **params)
def __str__(self): def __str__(self):

View File

@ -1318,7 +1318,7 @@ def setup_node_tmpdir(logdir, name):
class Router(Node): class Router(Node):
"A Node with IPv4/IPv6 forwarding enabled" "A Node with IPv4/IPv6 forwarding enabled"
def __init__(self, name, **params): def __init__(self, name, *posargs, **params):
# Backward compatibility: # Backward compatibility:
# Load configuration defaults like topogen. # Load configuration defaults like topogen.
@ -1347,7 +1347,7 @@ class Router(Node):
l = topolog.get_logger(name, log_level="debug", target=logfile) l = topolog.get_logger(name, log_level="debug", target=logfile)
params["logger"] = l params["logger"] = l
super(Router, self).__init__(name, **params) super(Router, self).__init__(name, *posargs, **params)
self.daemondir = None self.daemondir = None
self.hasmpls = False self.hasmpls = False
@ -1407,8 +1407,8 @@ class Router(Node):
# pylint: disable=W0221 # pylint: disable=W0221
# Some params are only meaningful for the parent class. # Some params are only meaningful for the parent class.
def config(self, **params): def config_host(self, **params):
super(Router, self).config(**params) super(Router, self).config_host(**params)
# User did not specify the daemons directory, try to autodetect it. # User did not specify the daemons directory, try to autodetect it.
self.daemondir = params.get("daemondir") self.daemondir = params.get("daemondir")

View File

@ -24,7 +24,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S
junit_logging = all junit_logging = all
junit_log_passing_tests = true junit_log_passing_tests = true
norecursedirs = .git example_test example_topojson_test lib docker norecursedirs = .git example_test example_topojson_test lib munet docker
# Directory to store test results and run logs in, default shown # Directory to store test results and run logs in, default shown
# rundir = /tmp/topotests # rundir = /tmp/topotests