diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md index 1130672cb376..54002941d634 100644 --- a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md +++ b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md @@ -24,6 +24,39 @@ back into the test driver command line upon its completion. This allows you to inspect the state of the VMs after the test (e.g. to debug the test script). +## Shell access in interactive mode {#sec-nixos-test-shell-access} + +The function `.shell_interact()` grants access to a shell running +inside a virtual machine. To use it, replace `` with the name of a +virtual machine defined in the test, for example: `machine.shell_interact()`. +Keep in mind that this shell may not display everything correctly as it is +running within an interactive Python REPL, and logging output from the virtual +machine may overwrite input and output from the guest shell: + +```py +>>> machine.shell_interact() +machine: Terminal is ready (there is no initial prompt): +$ hostname +machine +``` + +As an alternative, you can proxy the guest shell to a local TCP server by first +starting a TCP server in a terminal using the command: + +```ShellSession +$ socat 'READLINE,PROMPT=$ ' tcp-listen:4444,reuseaddr` +``` + +In the terminal where the test driver is running, connect to this server by +using: + +```py +>>> machine.shell_interact("tcp:127.0.0.1:4444") +``` + +Once the connection is established, you can enter commands in the socat terminal +where socat is running. + ## Reuse VM state {#sec-nixos-test-reuse-vm-state} You can re-use the VM states coming from a previous run by setting the diff --git a/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml b/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml index 16db709f8b91..30aa24f26e63 100644 --- a/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml +++ b/nixos/doc/manual/from_md/development/running-nixos-tests-interactively.section.xml @@ -25,6 +25,46 @@ $ ./result/bin/nixos-test-driver completion. This allows you to inspect the state of the VMs after the test (e.g. to debug the test script). +
+ Shell access in interactive mode + + The function + <yourmachine>.shell_interact() grants + access to a shell running inside a virtual machine. To use it, + replace <yourmachine> with the name of a + virtual machine defined in the test, for example: + machine.shell_interact(). Keep in mind that + this shell may not display everything correctly as it is running + within an interactive Python REPL, and logging output from the + virtual machine may overwrite input and output from the guest + shell: + + +>>> machine.shell_interact() +machine: Terminal is ready (there is no initial prompt): +$ hostname +machine + + + As an alternative, you can proxy the guest shell to a local TCP + server by first starting a TCP server in a terminal using the + command: + + +$ socat 'READLINE,PROMPT=$ ' tcp-listen:4444,reuseaddr` + + + In the terminal where the test driver is running, connect to this + server by using: + + +>>> machine.shell_interact("tcp:127.0.0.1:4444") + + + Once the connection is established, you can enter commands in the + socat terminal where socat is running. + +
Reuse VM state diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py index 6af964a0f588..8f01833bffb4 100644 --- a/nixos/lib/test-driver/test_driver/machine.py +++ b/nixos/lib/test-driver/test_driver/machine.py @@ -549,18 +549,27 @@ class Machine: return (rc, output.decode()) - def shell_interact(self) -> None: - """Allows you to interact with the guest shell + def shell_interact(self, address: Optional[str] = None) -> None: + """Allows you to interact with the guest shell for debugging purposes. - Should only be used during test development, not in the production test.""" + @address string passed to socat that will be connected to the guest shell. + Check the `Running Tests interactivly` chapter of NixOS manual for an example. + """ self.connect() - self.log("Terminal is ready (there is no initial prompt):") + + if address is None: + address = "READLINE,prompt=$ " + self.log("Terminal is ready (there is no initial prompt):") assert self.shell - subprocess.run( - ["socat", "READLINE,prompt=$ ", f"FD:{self.shell.fileno()}"], - pass_fds=[self.shell.fileno()], - ) + try: + subprocess.run( + ["socat", address, f"FD:{self.shell.fileno()}"], + pass_fds=[self.shell.fileno()], + ) + # allow users to cancel this command without breaking the test + except KeyboardInterrupt: + pass def console_interact(self) -> None: """Allows you to interact with QEMU's stdin