tbot.machine.connector

Connectors are one of the three parts of a machine: The connector is responsible for establishing the initial channel for a machine. This can work in many different ways, either a simple subprocess, an ssh-connection, a serial-console device, or a telnet session.

All connectors should inherit from the Connector base-class.

Provided Connectors

For a lot of commonly used cases, tbot already has connectors at hand. These are:

Subprocess

class tbot.machine.connector.SubprocessConnector[source]

Bases: tbot.machine.connector.connector.Connector

Connector using a subprocess shell.

Arguably the simplest connector; simply spawns a subprocess with a shell. This is the connector used by the default local lab-host.

Example:

from tbot.machine import connector, linux

class MyMachine(
    connector.SubprocessConnector,
    linux.Bash,
):
    pass

with MyMachine() as localhost:
    localhost.exec0("echo", "Hello!")
clone() → SelfSubprocess[source]

Clone this machine.

Paramiko

class tbot.machine.connector.ParamikoConnector(other: Optional[tbot.machine.connector.paramiko.ParamikoConnector] = None)[source]

Bases: tbot.machine.connector.connector.Connector

Connect to an ssh server using Paramiko.

When inheriting from this connector, you should overwrite the attributes documented below to make it connect to your remote.

Example:

from tbot.machine import connector, linux

class MyMachine(
    connector.ParamikoConnector,
    linux.Bash,
):
    hostname = "78.79.32.85"
    username = "tbot-user"

with MyMachine() as remotehost:
    remotehost.exec0("uname", "-a")
Parameters

other (ParamikoConnector) – Build this connection by opening a new channel in an existing ssh-connection.

abstract property hostname

Hostname of this remote.

You must always specify this parameter in your Lab config!

property username

Username to log in as.

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

property authenticator

Return an authenticator that allows logging in on this machine.

See tbot.machine.linux.auth for available authenticators.

Return type

tbot.machine.linux.auth.Authenticator

property port

Port the remote SSH server is listening on.

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

property ignore_hostkey

Ignore remote 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.

clone() → Self[source]

Clone this machine.

Note that an ssh-session cannot hold an umlimited number of channels so cloning too much might lead to issues. The exact limit is dependent on the server configuration.

Serial Console

class tbot.machine.connector.ConsoleConnector(mach: tbot.machine.linux.linux_shell.LinuxShell)[source]

Bases: tbot.machine.connector.connector.Connector

Connector for serial-consoles.

As this can work in many different ways, this connector is intentionally as generic as possible. To configure a serial connection, you need to implement the ConsoleConnector.connect() method. That methods gets a lab-host channel which it should transform into a channel connected to the board’s serial console.

Example:

import tbot
from tbot.machine import board, connector

class MyBoard(
    connector.ConsoleConnector,
    board.Board,
):
    def connect(self, mach):
        return mach.open_channel("picocom", "-b", "115200", "/dev/ttyACM0")

with tbot.acquire_local() as lo:
    with MyBoard(lo) as b:
        ...
Parameters

mach (LinuxShell) – A cloneable lab-host machine. The ConsoleConnector will try to clone this machine’s connection and use that to connect to the board. This means that you have to make sure you give the correct lab-host for your respecitive board to the constructor here.

abstract connect(mach: tbot.machine.linux.linux_shell.LinuxShell) → ContextManager[tbot.machine.channel.channel.Channel][source]

Connect a machine to the serial console.

Overwrite this method with the necessary logic to connect the given machine mach to a channel connected to the board’s serial console.

In most cases you’ll accomplish this using mach.open_channel(...).

clone() → SelfConsole[source]

This machine is not cloneable.

Plain SSH

class tbot.machine.connector.SSHConnector(host: Optional[tbot.machine.linux.linux_shell.LinuxShell] = None)[source]

Bases: tbot.machine.connector.connector.Connector

Connect to remote using ssh by starting off from an existing machine.

An SSHConnector is different from a ParamikoConnector as it requires an existing machine to start the connection from. This allows jumping via one host to a second.

Example:

import tbot
from tbot.machine import connector, linux

# Connect into a container running on the (possibly remote) lab-host
class MyRemote(
    connector.SSHConnector,
    linux.Bash,
):
    hostname = "localhost"
    port = 20220
    username = "root"

with tbot.acquire_lab() as lh:
    # lh might be a ParamikoConnector machine.
    with MyRemote(lh) as ssh_session:
        ssh_session.exec0("uptime")
property ignore_hostkey

Ignore host key.

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

abstract property hostname

Return the hostname of this machine.

Return type

str

property username

Return the username for logging in on this machine.

Defaults to the username on the labhost.

property authenticator

Return an authenticator that allows logging in on this machine.

See tbot.machine.linux.auth for available authenticators.

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

property port

Return the port the SSH server is listening on.

Return type

int

property ssh_config

Add additional ssh config options when connecting.

Example:

class MySSHMach(connector.SSHConnector, linux.Bash):
    ssh_config = ["ProxyJump=foo@example.com"]
Return type

list(str)

New in version 0.6.2.

clone() → tbot.machine.connector.ssh.SSHConnector[source]

Todo

Make this machine cloneable at some point.

Base-Class

class tbot.machine.connector.Connector[source]

Bases: tbot.machine.machine.Machine

Base-class for machine connectors.

abstract _connect() → ContextManager[tbot.machine.channel.channel.Channel][source]

Establish the channel.

This method will be called during machine-initialization and should yield a channel which will then be used for the machine.

This method’s return type is annotated as typing.ContextManager[channel.Channel], to allow more complex setup & teardown. As channels implement the context-manager protocol, simple connectors can just return the channel. A more complex connector can use the following pattern:

import contextlib

class MyConnector(Connector):
    @contextlib.contextmanager
    def _connext(self) -> typing.Iterator[channel.Channel]:
        try:
            # Do setup
            ...
            yield ch
        finally:
            # Do teardown
            ...
abstract clone() → Self[source]

Create a duplicate of this machine.

For a lot of connections, it is trivial to open a second one in parallel. This can be exploited to easily connect further from one host to the next, thus building a tunnel.

On the other hand, a serial connection to a board is unique and can’t be cloned. Such connectors should raise an exception is .clone() is called.