123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- #!/usr/bin/env python3
- """Test that all tincd scripts execute in correct order and contain expected env vars."""
- import os
- import typing as T
- from testlib import check, path
- from testlib.log import log
- from testlib.proc import Tinc, Script, ScriptType, TincScript
- from testlib.test import Test
- from testlib.util import random_string
- SUBNET_SERVER = ("10.0.0.1", "fec0::/64")
- SUBNET_CLIENT = ("10.0.0.2", "fec0::/64#5")
- NETNAMES = {
- "server": "net_" + random_string(8),
- "invite": "net_" + random_string(8),
- "client": "net_" + random_string(8),
- }
- # Creation time for the last notification event we've received.
- # Used for checking that scripts are called in the correct order.
- # dict is to avoid angering linters by using `global` to update this value.
- last_time = {"time": -1}
- def init(ctx: Test) -> T.Tuple[Tinc, Tinc]:
- """Initialize new test nodes."""
- server, client = ctx.node(), ctx.node()
- stdin = f"""
- init {server}
- set Port 0
- set DeviceType dummy
- set Address 127.0.0.1
- set AddressFamily ipv4
- add Subnet {SUBNET_SERVER[0]}
- add Subnet {SUBNET_SERVER[1]}
- """
- server.cmd(stdin=stdin)
- for script in (
- *Script,
- server.script_up,
- server.script_down,
- client.script_up,
- client.script_down,
- ):
- server.add_script(script)
- return server, client
- def wait_script(script: TincScript) -> T.Dict[str, str]:
- """Wait for script to finish and check that it was run by tincd *after* the
- script that was used as the argument in the previous call to this function.
- For example, to check that SUBNET_UP is called after TINC_UP:
- wait_script(node[Script.TINC_UP])
- wait_script(node[Script.SUBNET_UP])
- """
- msg = script.wait()
- assert msg.created_at
- log.debug(
- "%s sent %d, prev %d, diff %d",
- script,
- msg.created_at,
- last_time["time"],
- msg.created_at - last_time["time"],
- )
- if msg.created_at <= last_time["time"]:
- raise ValueError(f"script {script} started in wrong order")
- last_time["time"] = msg.created_at
- return msg.env
- def wait_tinc(server: Tinc, script: Script) -> None:
- """Wait for TINC_UP / TINC_DOWN and check env vars."""
- log.info("checking tinc: %s %s", server, script)
- env = wait_script(server[script])
- check.equals(NETNAMES["server"], env["NETNAME"])
- check.equals(server.name, env["NAME"])
- check.equals("dummy", env["DEVICE"])
- def wait_subnet(server: Tinc, script: Script, node: Tinc, subnet: str) -> None:
- """Wait for SUBNET_UP / SUBNET_DOWN and check env vars."""
- log.info("checking subnet: %s %s %s %s", server, script, node, subnet)
- env = wait_script(server[script])
- check.equals(NETNAMES["server"], env["NETNAME"])
- check.equals(server.name, env["NAME"])
- check.equals("dummy", env["DEVICE"])
- check.equals(node.name, env["NODE"])
- if node != server:
- check.equals("127.0.0.1", env["REMOTEADDRESS"])
- check.equals(str(node.port), env["REMOTEPORT"])
- if "#" in subnet:
- addr, weight = subnet.split("#")
- check.equals(addr, env["SUBNET"])
- check.equals(weight, env["WEIGHT"])
- else:
- check.equals(subnet, env["SUBNET"])
- def wait_host(server: Tinc, client: Tinc, script: ScriptType) -> None:
- """Wait for HOST_UP / HOST_DOWN and check env vars."""
- log.info("checking host: %s %s %s", server, client, script)
- env = wait_script(server[script])
- check.equals(NETNAMES["server"], env["NETNAME"])
- check.equals(server.name, env["NAME"])
- check.equals(client.name, env["NODE"])
- check.equals("dummy", env["DEVICE"])
- check.equals("127.0.0.1", env["REMOTEADDRESS"])
- check.equals(str(client.port), env["REMOTEPORT"])
- def test_start_server(server: Tinc) -> None:
- """Start server node and run checks on its scripts."""
- server.cmd("-n", NETNAMES["server"], "start")
- wait_tinc(server, Script.TINC_UP)
- port = server.read_port()
- server.cmd("set", "port", str(port))
- log.info("test server subnet-up")
- for sub in SUBNET_SERVER:
- wait_subnet(server, Script.SUBNET_UP, server, sub)
- def test_invite_client(server: Tinc, client: Tinc) -> str:
- """Check that client invitation scripts work."""
- url, _ = server.cmd("-n", NETNAMES["invite"], "invite", client.name)
- url = url.strip()
- check.true(url)
- env = wait_script(server[Script.INVITATION_CREATED])
- check.equals(NETNAMES["invite"], env["NETNAME"])
- check.equals(server.name, env["NAME"])
- check.equals(client.name, env["NODE"])
- check.equals(url, env["INVITATION_URL"])
- assert os.path.isfile(env["INVITATION_FILE"])
- return url
- def test_join_client(server: Tinc, client: Tinc, url: str) -> None:
- """Test that client joining scripts work."""
- client.cmd("-n", NETNAMES["client"], "join", url)
- env = wait_script(server[Script.INVITATION_ACCEPTED])
- check.equals(NETNAMES["server"], env["NETNAME"])
- check.equals(server.name, env["NAME"])
- check.equals(client.name, env["NODE"])
- check.equals("dummy", env["DEVICE"])
- check.equals("127.0.0.1", env["REMOTEADDRESS"])
- def test_start_client(server: Tinc, client: Tinc) -> None:
- """Start client and check its script work."""
- client.randomize_port()
- stdin = f"""
- set Address {client.address}
- set ListenAddress {client.address}
- set Port {client.port}
- set DeviceType dummy
- add Subnet {SUBNET_CLIENT[0]}
- add Subnet {SUBNET_CLIENT[1]}
- start
- """
- client.cmd(stdin=stdin)
- log.info("test client scripts")
- wait_host(server, client, Script.HOST_UP)
- wait_host(server, client, client.script_up)
- log.info("test client subnet-up")
- for sub in SUBNET_CLIENT:
- wait_subnet(server, Script.SUBNET_UP, client, sub)
- def test_stop_server(server: Tinc, client: Tinc) -> None:
- """Stop server and check that its scripts work."""
- server.cmd("stop")
- wait_host(server, client, Script.HOST_DOWN)
- wait_host(server, client, client.script_down)
- log.info("test client subnet-down")
- for sub in SUBNET_CLIENT:
- wait_subnet(server, Script.SUBNET_DOWN, client, sub)
- log.info("test server subnet-down")
- for sub in SUBNET_SERVER:
- wait_subnet(server, Script.SUBNET_DOWN, server, sub)
- log.info("test tinc-down")
- wait_tinc(server, Script.TINC_DOWN)
- def run_tests(ctx: Test) -> None:
- """Run all tests."""
- server, client = init(ctx)
- log.info("start server")
- test_start_server(server)
- log.info("invite client")
- url = test_invite_client(server, client)
- log.info('join client via url "%s"', url)
- test_join_client(server, client, url)
- log.info("start client")
- test_start_client(server, client)
- log.info("stop server")
- test_stop_server(server, client)
- def run_script_interpreter_test(ctx: Test) -> None:
- """Check that tincd scripts run with a custom script interpreter."""
- foo = ctx.node()
- stdin = f"""
- init {foo}
- set Port 0
- set DeviceType dummy
- set ScriptsInterpreter {path.PYTHON_PATH}
- """
- foo_up = foo.add_script(Script.TINC_UP)
- foo.cmd(stdin=stdin)
- foo.cmd("start")
- foo_up.wait()
- foo.cmd("stop")
- with Test("scripts test") as context:
- run_tests(context)
- if os.name != "nt":
- with Test("works with ScriptInterpreter") as context:
- run_script_interpreter_test(context)
|