Configuration

tbot has a configuration mechanism which allows selecting a board and lab from the commandline using the -l and -b switches. The configuration files are just python scripts defining the classes for lab-host, board-hardware and board-software.

Lab Config

The lab config describes your “lab” setup which consists mostly of your lab-host, the machine from which you open the (serial-)connection to your board. This is often directly your local computer in which case the config does not need to contain much or could be not necessary at all. But tbot also supports a lab-host which you connect to via SSH, for example.

The lab can be selected using -l lab-config.py on the command-line and the selected lab will then be available to testcases by using the tbot.acquire_lab() function. The lab-config can be any Python file defining a lab-host machine. To allow tbot to detect the machine, the python module should have a global LAB which points to the machine’s class. For best compatibility, the lab-host machine should inherit from tbot.machine.linux.Lab.

If your board is connected directly to your computer, you lab-host is your localhost. The config (for simple cases you need none at all) might look like this:

Example (local): local-lab.py

from tbot.machine import connector, linux, board

class MyLabHost(
    connector.SubprocessConnector,
    linux.Bash,
    linux.Lab,
):
    name = "my-lab"

    @property
    def workdir(self):
        return linux.Workdir.static(self, f"/work/{self.username}/tbot-workdir")

# Tell tbot about the class by defining a global `LAB`
LAB = MyLabHost

In this instance, tbot will use localhost as the lab-host and store files in /work/<user>/tbot-workdir. You could try out your config using:

$ tbot -l local-lab.py selftest_machine_labhost_shell

Alternatively, your lab might be remote and you have to connect to it using SSH. In that case, an example config might look like this:

Example (remote, ssh): ssh-lab.py

from tbot.machine import connector, linux, board

class MyLabHost(
    connector.ParamikoConnector,
    linux.Bash,
    linux.Lab,
):
    name = "my-lab"

    hostname = "vlab.my-host.com"
    username = "ken"

    @property
    def workdir(self):
        return linux.Workdir.static(self, f"/work/{self.username}/tbot-workdir")

# Tell tbot about the class by defining a global `LAB`
LAB = MyLabHost

For further information about the configurable options, look at the docs for the individual classes:

Board Config

The board configuration is split into two parts: The first part configures the physical hardware and how to access it (called the Board) and the second part defines the software running on it.

Todo

At the moment, both the hardware and the software config live in the same file. We plan to (optionally) separate this in the future to allow even more generic configurations.

Board-Hardware Config

The hardware is configured as a machine which turns on the board when accessed and opens a serial-console. It does not do any further interaction with the board, to not make any assumptions about the software running on it.

Typically, the board config might look similar to this:

from tbot.machine import connector, board

class MyBoard(
    connector.ConsoleConnector,
    board.PowerControl,
    board.Board,
):
    name = "myboard"

    def poweron(self):
        self.host.exec0("power-control.sh", "on")

    def poweroff(self):
        self.host.exec0("power-control.sh", "off")

    def connect(self, mach):
        # Open the serial console
        return mach.open_channel("picocom", "-b", "115200", "/dev/ttyUSB0")


# Similarl to the `LAB`, the board needs to be made available as `BOARD`
BOARD = MyBoard

This should be enough to allow accessing the board using the interactive_board testcase:

$ tbot -l lab-config.py -b board-config.py -vv interactive_board

Next up, you need to configure the software running on your board. If you have U-Boot and want to access it from testcases, continue to the next section: U-Boot Config. If you do not have U-Boot or you don’t need access to U-Boot from your testcases, you can jump to Linux (without U-Boot) Config.

U-Boot Config

U-Boot is configured as another machine ontop of the ‘hardware machine’. It looks like this:

from tbot.machine import board

class MyBoardUBoot(
    board.Connector,
    board.UBootShell,
):
    prompt = "=> "

# Make visible to tbot:
UBOOT = MyBoardUBoot

If your U-Boot is configured with autoboot, you should also inherit the UBootAutobootIntercept:

from tbot.machine import board

class MyBoardUBoot(
    board.Connector,
    board.UBootAutobootIntercept,
    board.UBootShell,
):
    prompt = "=> "

# Make visible to tbot:
UBOOT = MyBoardUBoot

If everything is configured correctly, you should now be able to access the U-Boot shell using the interactive_uboot testcase:

$ tbot -l lab-config.py -b board-config.py -vv interactive_uboot

In testcases, you can now access U-Boot by first acquiring the hardware using tbot.acquire_board() and the acquiring U-Boot using tbot.acquire_uboot():

@tbot.testcase
def footest():
    with tbot.acquire_board(lh) as b:
        with tbot.acquire_uboot(b) as ub:
            ub.exec0("version")

Linux (from U-Boot) Config

Todo

This is a stub. Look at the docs for Board Linux, tbot.machine.board.LinuxUbootConnector, and tbot.machine.board.LinuxBootLogin for now …

class BeagleBoneLinux(
    board.LinuxUbootConnector,
    board.LinuxBootLogin,
    linux.Bash,
):
    uboot = BeagleBoneUBoot
    username = "root"
    password = None

    def do_boot(self, ub: board.UBootShell) -> channel.Channel:
        ub.env("serverip", "192.168.1.1")
        ub.env("netmask", "255.255.0.0")
        ub.env("ipaddr", "192.168.1.2")
        ub.exec0("mw", "0x81000000", "0", "0x4000")
        ub.exec0("tftp", "0x81000000", "bbb/tbot/env.txt")
        ub.exec0("env", "import", "-t", "0x81000000")
        ub.env("rootpath", "/path/to/core-image-lsb-sdk-generic-armv7a-hf")

        return ub.boot("run", "netnfsboot")


UBOOT = BeagleBoneUBoot
LINUX = BeagleBoneLinux

Linux (without U-Boot) Config

Todo

This is a stub. Look at the docs for Board Linux and tbot.machine.board.LinuxBootLogin for now …

class BeagleBoneLinux(
    board.Connector,
    board.LinuxBootLogin,
    linux.Ash,
):
    username = "root"
    password = None


LINUX = BeagleBoneLinux

Initializing a Machine

After the basic setup sometimes you might need additional steps to bring the machine into the state you need it in. Example might be network setup in U-Boot or disabling kernel console-logging in Linux. You can do this by defining a tbot.machine.Machine.init() function:

class MyBoardLinux(..., linux.Ash):
    name = "myboard-lnx"

    ...

    def init(self):
        self.exec0("sysctl", "kernel.printk=1 4 1 4")