tbot.machine.linux Module

tbot.machine.linux

class tbot.machine.linux.LinuxMachine[source]

Bases: tbot.machine.machine.Machine, tbot.machine.machine.InteractiveMachine

Generic machine that is running Linux.

Connect to this machine.

shell

Shell that is in use on the board.

username

Return the username for logging in on this machine.

workdir

Return a path where testcases can store data on this host.

fsroot

Return a path to the filesystem root.

This is a helper to allow the following:

vers = lh.fsroot / "proc" / "version"

New in version 0.6.6.

build_command(*args, stdout: Optional[tbot.machine.linux.path.Path[Self]] = None) → str[source]

Build the string representation of a command.

Parameters:
  • args – Each arg is a token that will be sent to the shell. Can be either a str, a Special or a Path that is associated with this host. Arguments will be escaped (a str like “a b” will not result in separate arguments to the command)
  • stdout (Path) – File where stdout should be directed to
Returns:

A string that would be sent to the machine to execute the command

Return type:

str

exec(*args, stdout: Optional[tbot.machine.linux.path.Path[Self]] = None, timeout: Optional[float] = None) → Tuple[int, str][source]

Run a command on this machine.

Parameters:
  • args – Each arg is a token that will be sent to the shell. Can be either a str, a Special or a Path that is associated with this host. Arguments will be escaped (a str like “a b” will not result in separate arguments to the command)
  • stdout (Path) – File where stdout should be directed to
Returns:

Tuple with the exit code and a string containing the combined stdout and stderr of the command (with a trailing newline).

Return type:

(int, str)

exec0(*args, stdout: Optional[tbot.machine.linux.path.Path[Self]] = None, timeout: Optional[float] = None) → str[source]

Run a command on this machine and ensure it succeeds.

Parameters:
  • args – Each arg is a token that will be sent to the shell. Can be either a str, a Special or a Path that is associated with this host. Arguments will be escaped (a str like “a b” will not result in separate arguments to the command)
  • stdout (Path) – File where stdout should be directed to
Returns:

A string containing the combined stdout and stderr of the command (with a trailing newline).

Return type:

str

test(*args, stdout: Optional[tbot.machine.linux.path.Path[Self]] = None, timeout: Optional[float] = None) → bool[source]

Run a command and test if it succeeds.

Parameters:
  • args – Each arg is a token that will be sent to the shell. Can be either a str, a Special or a Path that is associated with this host. Arguments will be escaped (a str like “a b” will not result in separate arguments to the command)
  • stdout (Path) – File where stdout should be directed to
Returns:

True if the return code is 0, else False.

Return type:

bool

env(var: str, value: Union[str, tbot.machine.linux.special.Special[Self], tbot.machine.linux.path.Path[Self], None] = None) → str[source]

Get or set the value of an environment variable.

Parameters:
  • var (str) – The variable’s name
  • value (str) – The value the var should be set to. If this parameter is given, env will set, else it will just read a variable
Return type:

str

Returns:

Value of the environment variable

New in version 0.6.2.

Changed in version 0.6.5: You can use env() to set environment variables as well.

interactive() → None[source]

Drop into an interactive session on this machine.

subshell(*args, shell: Optional[Type[tbot.machine.linux.shell.shell.Shell]] = None) → tbot.machine.linux.machine._SubshellContext[source]

Start a subshell for isolating environment changes.

If no arguments are supplied, the shell defined for this machine is used. If arguments are given, they are expected to open a shell of type shell which defaults to the shell specified for this machine.

Example:

with tbot.acquire_lab() as lh:
    lh.exec0("echo", linux.Env("FOOVAR"))  # Empty result

    with lh.subshell():
        lh.env("FOOVAR", "123")
        lh.exec0("echo", linux.Env("FOOVAR"))  # 123

    lh.exec0("echo", linux.Env("FOOVAR"))  # Empty result
Parameters:shell (Shell) – Shell that is started

New in version 0.6.1.

class tbot.machine.linux.Path(host: H, *args)[source]

Bases: pathlib.PurePosixPath, typing.Generic

A path that is associated with a tbot machine.

A path can only be used with its associated host. Using it with any other host will raise an exception and will be detected by a static typechecker.

Apart from that, Path behaves like a pathlib.Path:

from tbot.machine import linux

p = linux.Path(mach, "/foo/bar")
p2 = p / "bar" / "baz"
if not p2.exists():
    mach.exec0("mkdir", "-p", p2.parent)
    mach.exec0("touch", p2)
elif not p2.is_file():
    raise Exception(f"{p2} must be a normal file!")

Create a new path.

Parameters:
  • host (linux.LinuxMachine) – Host this path should be associated with
  • args – pathlib.PurePosixPath constructor arguments
host

Host associated with this path.

stat() → os.stat_result[source]

Return the result of stat on this path.

Tries to imitate the results of pathlib.Path.stat(), returns a os.stat_result.

exists() → bool[source]

Whether this path exists.

is_dir() → bool[source]

Whether this path points to a directory.

is_file() → bool[source]

Whether this path points to a normal file.

Whether this path points to a symlink.

is_block_device() → bool[source]

Whether this path points to a block device.

is_char_device() → bool[source]

Whether this path points to a character device.

is_fifo() → bool[source]

Whether this path points to a pipe(fifo).

is_socket() → bool[source]

Whether this path points to a unix domain-socket.

parent

Parent of this path.

class tbot.machine.linux.Workdir[source]

Bases: object

Helper for defining a machines workdir.

classmethod static(host: H, path: str) → tbot.machine.linux.path.Path[H][source]

Create a new static workdir.

Should be used inside the workdir() method, like this:

class MyMachine(...):
    ...

    @property
    def workdir(self) -> "linux.Path[MyMachine]":
        return linux.Workdir.static(self, "/tmp/tbot-workdir")
Parameters:
  • host (linux.LinuxMachine) – Host for which this workdir should be defined.
  • path (str) – Fixed workdir path
Return type:

linux.Path

Returns:

A tbot-path to the workdir

classmethod athome(host: H, subdir: str) → tbot.machine.linux.path.Path[H][source]

Create a new workdir below the users home.

Should be used inside the workdir() method, like this:

class MyMachine(...):
    ...

    @property
    def workdir(self) -> "linux.Path[MyMachine]":
        return linux.Workdir.athome(self, "tbot-work")
Parameters:
  • host (linux.LinuxMachine) – Host for which this workdir should be defined.
  • subdir (str) – Name of the subdirectory in $HOME which should be used as a workdir.
Return type:

linux.Path

Returns:

A tbot-path to the workdir

Command Specials

class tbot.machine.linux.Env(name: str)[source]

Bases: tbot.machine.linux.special.Special

Expand an environment variable or shell variable.

Create a new environment variable accessor.

Example:

m.exec0(linux.Env("CC"), "-c", m.workdir / "main.c")
Parameters:name (str) – Name of the env var.
class tbot.machine.linux.F(fmt: str, *args, quote: bool = True)[source]

Bases: tbot.machine.linux.special.Special

Format string.

Create a format string.

Example:

m.exec0("export", linux.F("PATH={}:{}", p1, linux.Env("PATH"), quote=False))

All normal python formatters are supported.

Parameters:
  • fmt (str) – Format string
  • args – Format arguments. Can be tbot paths as well.
  • quote (bool) – Whether to escape the resulting string.
tbot.machine.linux.Pipe

Special character for the | pipe to send command output to another command

Example:

m.exec0("dmesg", linux.Pipe, "grep", "usb0")
tbot.machine.linux.Then

Special character for the ; separator to run multiple commands

Example:

m.exec0("sleep", "1", linux.Then, "date")
tbot.machine.linux.Background

Special character for the & separator to run a command in the background

Example:

m.exec0("daemon", linux.Background)
tbot.machine.linux.OrElse

Special character for the || separator to run a command if the first one failed

Example:

m.exec0("test", "-d", "/foo/bar", linux.OrElse, "mkdir", "-p", "/foo/bar")
tbot.machine.linux.AndThen

Special character for the && separator to run a command if the first one succeeded

Example:

m.exec0("test", "-d", ".git", linux.AndThen, "git", "describe", "--tags")
class tbot.machine.linux.Raw(text: str)[source]

Bases: tbot.machine.linux.special.Special

Raw unescaped string.

Create a new unescaped string.

Example:

m.exec0(linux.Raw('FOOBAR="${USER}@$(hostname):${PWD}"'))
Parameters:text (str) – The raw string

Implementations

class tbot.machine.linux.LabHost[source]

Bases: tbot.machine.linux.machine.LinuxMachine

Generic LabHost abstraction.

Connect to this machine.

new_channel(*args) → tbot.machine.channel.channel.Channel[source]

Create a new channel for a new machine instance via this LabHost.

If *args is non-empty, it is interpreted as a command that will be run on the LabHost to open a connection to the new machine. Once this command finished, the new channel will be closed.

If *args is empty, a shell on the LabHost is opened, that you can run commands using eg. raw_command().

Example:

with tbot.acquire_lab() as lh:
    bdi_ch = lh.new_channel("telnet", "bdi4")

    # Do something with the bdi
build() → tbot.machine.linux.build.machine.BuildMachine[source]

Return the default build-host for this lab.

lh

Return this labhost.

class tbot.machine.linux.BuildMachine[source]

Bases: tbot.machine.linux.machine.LinuxMachine

Generic buildhost machine.

Connect to this machine.

toolchains

Return a dictionary of all toolchains that exist on this buildhost.

Example:

@property
def toolchains(self) -> typing.Dict[str, linux.build.Toolchain]:
    return {
        "generic-armv7a": linux.build.EnvScriptToolchain(
            linux.Path(
                self,
                "/path/to/environment-setup-armv7a-neon-poky-linux-gnueabi",
            )
        ),
        "generic-armv7a-hf": linux.build.EnvScriptToolchain(
            linux.Path(
                self,
                "/path/to/environment-setup-armv7ahf-neon-poky-linux-gnueabi",
            )
        ),
    }
enable(arch: str) → tbot.machine.linux.build.machine._ToolchainContext[source]

Enable the toolchain for arch on this BuildHost instance.

Example:

with lh.build() as bh:
    # Now we are on the buildhost

    with bh.enable("generic-armv7a-hf"):
        # Toolchain is enabled here
        bh.exec0(linux.Env("CC"), "--version")
class tbot.machine.linux.SSHMachine(labhost: tbot.machine.linux.lab.machine.LabHost)[source]

Bases: tbot.machine.linux.machine.LinuxMachine

Generic machine that can be reached via SSH from the LabHost.

Connect to this SSH machine.

Parameters:labhost (tbot.machine.linux.LabHost) – LabHost from where to attempt connecting
ignore_hostkey

Ignore host key.

Set this to true if the remote changes its host key often.

hostname

Return the hostname of this machine.

Return type:str
username

Return the username for logging in on this machine.

Defaults to the username on the labhost.

authenticator

Return an authenticator that allows logging in on this machine.

Danger

It is strongly advised to use key authentication. If you use password auth, THE PASSWORD WILL BE LEAKED and MIGHT EASILY BE STOLEN by other users on your labhost. It will also be visible in the log file.

If you decide to use this, you’re doing this on your own risk.

The only case where I support using passwords is when connecting to a test board with a default password.

Return type:tbot.machine.linux.auth.Authenticator
port

Return the port the SSH server is listening on.

Return type:int
ssh_config

Add additional ssh config options when connecting.

Example:

class MySSHMach(linux.SSHMachine):
    ssh_config = ["ProxyJump=foo@example.com"]
Return type:list(str)

New in version 0.6.2.

destroy() → None[source]

Destory this SSHMachine instance.

lh

Return the lab-host that was used to establish this machines connection.

tbot.machine.linux.auth

class tbot.machine.linux.auth.Authenticator[source]

Bases: abc.ABC

Authenticate a connection to a linux host.

class tbot.machine.linux.auth.PasswordAuthenticator(password: str)[source]

Bases: tbot.machine.linux.auth.Authenticator

Authenticate using a password.

Initialize this authenticator with a given password.

Parameters:password (str) – The password that will be used with this authenticator.
class tbot.machine.linux.auth.PrivateKeyAuthenticator(key_file: pathlib.PurePath)[source]

Bases: tbot.machine.linux.auth.Authenticator

Authenticate using a private key.

Initialize this authenticator with a path to a private key.

Parameters:key_file (pathlib.Path) – Path to the private key file
class tbot.machine.linux.auth.NoneAuthenticator[source]

Bases: tbot.machine.linux.auth.Authenticator

Authenticates without key or password.

Useful if an ssh-agent is running in the background on lab-hosts.

New in version 0.6.4.

tbot.machine.linux.lab

class tbot.machine.linux.lab.LocalLabHost[source]

Bases: tbot.machine.linux.lab.machine.LabHost

LabHost on the host tbot is running on.

Makes use of the SubprocessChannel.

LocalLabHost can be instanciated as is, but if you need customization, you should subclass it.

Create a new instance of a LocalLabHost.

workdir

Return a path to a workdir for tbot.

Defaults to /tmp/tbot-wd, but can be overwritten in a lab config.

username

Return the name of the user running tbot.

destroy() → None[source]

Destroy this instance of a LocalLabHost.

class tbot.machine.linux.lab.SSHLabHost[source]

Bases: tbot.machine.linux.lab.machine.LabHost

LabHost that can be connected to via SSH.

Makes use of ParamikoChannel.

To use a LabHost that is connected via SSH, you must subclass SSHLabHost in you lab config.

By default SSHLabHost tries to get connection info from ~/.ssh/config, but you can also overwrite this in your config.

Example:

from tbot.machine.linux import lab
from tbot.machine import linux


class MySSHLab(lab.SSHLabHost):
    name = "my-ssh-lab"
    hostname = "lab.example.com"
    username = "admin"

    @property
    def workdir(self) -> "linux.Path[MySSHLab]":
        p = linux.Path(self, "/tmp/tbot-wd")
        self.exec0("mkdir", "-p", p)
        return p


LAB = MySSHLab

Create a new instance of this SSH LabHost.

ignore_hostkey

Ignore host key.

Set this to true if the remote changes its host key often.

Defaults to False or the value of StrictHostKeyChecking in ~/.ssh/config.

hostname

Return the hostname of this lab.

You must always specify this parameter in your Lab config!

username

Return the username to login as.

Defaults to the username from ~/.ssh/config or the local username.

authenticator

Return an Authenticator that allows logging in on this LabHost.

Defaults to a private key authenticator using ~/.ssh/id_rsa or the first key specified in ~/.ssh/config.

port

Return the port the remote SSH server is listening on.

Defaults to 22 or the value of Port in ~/.ssh/config.

destroy() → None[source]

Destroy this LabHost instance.

Warning

Closes all channels that are still open. You should not call this method unless you know what you are doing. The preferred way is to use a context block using with.

tbot.machine.linux.build

class tbot.machine.linux.build.Toolchain[source]

Bases: abc.ABC

Generic toolchain type.

enable(host: tbot.machine.linux.machine.LinuxMachine) → None[source]

Enable this toolchain on the given host.

class tbot.machine.linux.build.EnvScriptToolchain(path: tbot.machine.linux.path.Path[H])[source]

Bases: tbot.machine.linux.build.toolchain.Toolchain

Toolchain that is initialized using an env script.

Create a new EnvScriptToolchain.

Parameters:path (linux.Path) – Path to the env script
enable(host: H) → None[source]

Enable this toolchain on the given host.

tbot.machine.linux.shell

class tbot.machine.linux.shell.Shell[source]

Bases: object

Generic sh shell.

name: str = "sh"
name = 'sh'

Name of this shell.

command = ['sh']

Command for starting this shell.

New in version 0.6.4.

static set_prompt(prompt: str) → str[source]

Set the prompt of this shell to prompt.

Parameters:prompt (str) – The new PS1
Return type:str
Returns:The command to set the prompt in this shell.
static set_prompt2(prompt: str) → Optional[str][source]

Set the secondary prompt of this shell to prompt.

Parameters:prompt (str) – The new PS2
Return type:str, None
Returns:The command to set PS2 in this shell if it exists.
static disable_editing() → Optional[str][source]

Disable readline or equivalents in this shell.

Return type:str, None
Returns:The command to disable line editing in this shell if it exists.
static enable_editing() → Optional[str][source]

Enable readline or equivalents in this shell.

Return type:str, None
Returns:The command to enable line editing in this shell if it exists.
static disable_history() → Optional[str][source]

Disable writing to the history file.

Return type:str, None
Returns:The command to disable history in this shell if it exists.
class tbot.machine.linux.shell.Bash[source]

Bases: tbot.machine.linux.shell.shell.Shell

Bourne-Again SHell.

name: str = "bash"
static set_prompt(prompt: str) → str[source]

Set the prompt of this shell to prompt.

Parameters:prompt (str) – The new PS1
Return type:str
Returns:The command to set the prompt in this shell.
static disable_editing() → Optional[str][source]

Disable readline or equivalents in this shell.

Return type:str, None
Returns:The command to disable line editing in this shell if it exists.
static enable_editing() → Optional[str][source]

Enable readline or equivalents in this shell.

Return type:str, None
Returns:The command to enable line editing in this shell if it exists.
static disable_history() → Optional[str][source]

Disable writing to the history file.

Return type:str, None
Returns:The command to disable history in this shell if it exists.
class tbot.machine.linux.shell.Ash[source]

Bases: tbot.machine.linux.shell.shell.Shell

Ash shell.

name: str = "ash"