scripts.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env python3
  2. """Test that all tincd scripts execute in correct order and contain expected env vars."""
  3. import os
  4. import typing as T
  5. from testlib import check, path
  6. from testlib.log import log
  7. from testlib.proc import Tinc, Script, ScriptType, TincScript
  8. from testlib.test import Test
  9. from testlib.util import random_string
  10. SUBNET_SERVER = ("10.0.0.1", "fec0::/64")
  11. SUBNET_CLIENT = ("10.0.0.2", "fec0::/64#5")
  12. NETNAMES = {
  13. "server": "net_" + random_string(8),
  14. "invite": "net_" + random_string(8),
  15. "client": "net_" + random_string(8),
  16. }
  17. # Creation time for the last notification event we've received.
  18. # Used for checking that scripts are called in the correct order.
  19. # dict is to avoid angering linters by using `global` to update this value.
  20. last_time = {"time": -1}
  21. def init(ctx: Test) -> T.Tuple[Tinc, Tinc]:
  22. """Initialize new test nodes."""
  23. server, client = ctx.node(), ctx.node()
  24. stdin = f"""
  25. init {server}
  26. set Port 0
  27. set DeviceType dummy
  28. set Address 127.0.0.1
  29. set AddressFamily ipv4
  30. add Subnet {SUBNET_SERVER[0]}
  31. add Subnet {SUBNET_SERVER[1]}
  32. """
  33. server.cmd(stdin=stdin)
  34. for script in (
  35. *Script,
  36. server.script_up,
  37. server.script_down,
  38. client.script_up,
  39. client.script_down,
  40. ):
  41. server.add_script(script)
  42. return server, client
  43. def wait_script(script: TincScript) -> T.Dict[str, str]:
  44. """Wait for script to finish and check that it was run by tincd *after* the
  45. script that was used as the argument in the previous call to this function.
  46. For example, to check that SUBNET_UP is called after TINC_UP:
  47. wait_script(node[Script.TINC_UP])
  48. wait_script(node[Script.SUBNET_UP])
  49. """
  50. msg = script.wait()
  51. assert msg.created_at
  52. log.debug(
  53. "%s sent %d, prev %d, diff %d",
  54. script,
  55. msg.created_at,
  56. last_time["time"],
  57. msg.created_at - last_time["time"],
  58. )
  59. if msg.created_at <= last_time["time"]:
  60. raise ValueError(f"script {script} started in wrong order")
  61. last_time["time"] = msg.created_at
  62. return msg.env
  63. def wait_tinc(server: Tinc, script: Script) -> None:
  64. """Wait for TINC_UP / TINC_DOWN and check env vars."""
  65. log.info("checking tinc: %s %s", server, script)
  66. env = wait_script(server[script])
  67. check.equals(NETNAMES["server"], env["NETNAME"])
  68. check.equals(server.name, env["NAME"])
  69. check.equals("dummy", env["DEVICE"])
  70. def wait_subnet(server: Tinc, script: Script, node: Tinc, subnet: str) -> None:
  71. """Wait for SUBNET_UP / SUBNET_DOWN and check env vars."""
  72. log.info("checking subnet: %s %s %s %s", server, script, node, subnet)
  73. env = wait_script(server[script])
  74. check.equals(NETNAMES["server"], env["NETNAME"])
  75. check.equals(server.name, env["NAME"])
  76. check.equals("dummy", env["DEVICE"])
  77. check.equals(node.name, env["NODE"])
  78. if node != server:
  79. check.equals("127.0.0.1", env["REMOTEADDRESS"])
  80. check.equals(str(node.port), env["REMOTEPORT"])
  81. if "#" in subnet:
  82. addr, weight = subnet.split("#")
  83. check.equals(addr, env["SUBNET"])
  84. check.equals(weight, env["WEIGHT"])
  85. else:
  86. check.equals(subnet, env["SUBNET"])
  87. def wait_host(server: Tinc, client: Tinc, script: ScriptType) -> None:
  88. """Wait for HOST_UP / HOST_DOWN and check env vars."""
  89. log.info("checking host: %s %s %s", server, client, script)
  90. env = wait_script(server[script])
  91. check.equals(NETNAMES["server"], env["NETNAME"])
  92. check.equals(server.name, env["NAME"])
  93. check.equals(client.name, env["NODE"])
  94. check.equals("dummy", env["DEVICE"])
  95. check.equals("127.0.0.1", env["REMOTEADDRESS"])
  96. check.equals(str(client.port), env["REMOTEPORT"])
  97. def test_start_server(server: Tinc) -> None:
  98. """Start server node and run checks on its scripts."""
  99. server.cmd("-n", NETNAMES["server"], "start")
  100. wait_tinc(server, Script.TINC_UP)
  101. port = server.read_port()
  102. server.cmd("set", "port", str(port))
  103. log.info("test server subnet-up")
  104. for sub in SUBNET_SERVER:
  105. wait_subnet(server, Script.SUBNET_UP, server, sub)
  106. def test_invite_client(server: Tinc, client: Tinc) -> str:
  107. """Check that client invitation scripts work."""
  108. url, _ = server.cmd("-n", NETNAMES["invite"], "invite", client.name)
  109. url = url.strip()
  110. check.true(url)
  111. env = wait_script(server[Script.INVITATION_CREATED])
  112. check.equals(NETNAMES["invite"], env["NETNAME"])
  113. check.equals(server.name, env["NAME"])
  114. check.equals(client.name, env["NODE"])
  115. check.equals(url, env["INVITATION_URL"])
  116. assert os.path.isfile(env["INVITATION_FILE"])
  117. return url
  118. def test_join_client(server: Tinc, client: Tinc, url: str) -> None:
  119. """Test that client joining scripts work."""
  120. client.cmd("-n", NETNAMES["client"], "join", url)
  121. env = wait_script(server[Script.INVITATION_ACCEPTED])
  122. check.equals(NETNAMES["server"], env["NETNAME"])
  123. check.equals(server.name, env["NAME"])
  124. check.equals(client.name, env["NODE"])
  125. check.equals("dummy", env["DEVICE"])
  126. check.equals("127.0.0.1", env["REMOTEADDRESS"])
  127. def test_start_client(server: Tinc, client: Tinc) -> None:
  128. """Start client and check its script work."""
  129. client.randomize_port()
  130. stdin = f"""
  131. set Address {client.address}
  132. set ListenAddress {client.address}
  133. set Port {client.port}
  134. set DeviceType dummy
  135. add Subnet {SUBNET_CLIENT[0]}
  136. add Subnet {SUBNET_CLIENT[1]}
  137. start
  138. """
  139. client.cmd(stdin=stdin)
  140. log.info("test client scripts")
  141. wait_host(server, client, Script.HOST_UP)
  142. wait_host(server, client, client.script_up)
  143. log.info("test client subnet-up")
  144. for sub in SUBNET_CLIENT:
  145. wait_subnet(server, Script.SUBNET_UP, client, sub)
  146. def test_stop_server(server: Tinc, client: Tinc) -> None:
  147. """Stop server and check that its scripts work."""
  148. server.cmd("stop")
  149. wait_host(server, client, Script.HOST_DOWN)
  150. wait_host(server, client, client.script_down)
  151. log.info("test client subnet-down")
  152. for sub in SUBNET_CLIENT:
  153. wait_subnet(server, Script.SUBNET_DOWN, client, sub)
  154. log.info("test server subnet-down")
  155. for sub in SUBNET_SERVER:
  156. wait_subnet(server, Script.SUBNET_DOWN, server, sub)
  157. log.info("test tinc-down")
  158. wait_tinc(server, Script.TINC_DOWN)
  159. def run_tests(ctx: Test) -> None:
  160. """Run all tests."""
  161. server, client = init(ctx)
  162. log.info("start server")
  163. test_start_server(server)
  164. log.info("invite client")
  165. url = test_invite_client(server, client)
  166. log.info('join client via url "%s"', url)
  167. test_join_client(server, client, url)
  168. log.info("start client")
  169. test_start_client(server, client)
  170. log.info("stop server")
  171. test_stop_server(server, client)
  172. def run_script_interpreter_test(ctx: Test) -> None:
  173. """Check that tincd scripts run with a custom script interpreter."""
  174. foo = ctx.node()
  175. stdin = f"""
  176. init {foo}
  177. set Port 0
  178. set DeviceType dummy
  179. set ScriptsInterpreter {path.PYTHON_PATH}
  180. """
  181. foo_up = foo.add_script(Script.TINC_UP)
  182. foo.cmd(stdin=stdin)
  183. foo.cmd("start")
  184. foo_up.wait()
  185. foo.cmd("stop")
  186. with Test("scripts test") as context:
  187. run_tests(context)
  188. if os.name != "nt":
  189. with Test("works with ScriptInterpreter") as context:
  190. run_script_interpreter_test(context)