Example #1
0
def swupdate_update_web(lh: linux.Lab,
                        swu_file: linux.Path,
                        target_ip: str,
                        timeout: int = 300) -> None:
    """
    Upload an ``.swu`` file to a running swupdate server.

    :param linux.Lab lh: Optionally the lab-host from where to initiate the update.
    :param linux.Path swu_file: Path to the ``.swu`` file (on the lab-host or locally).
    :param str target_ip: IP-Address of the target host.
    :param int timeout: Timeout.
    """
    with tbot.acquire_local() as lo:  # Needed for the script
        script_path = lh.workdir / "tbot_swupdate_web.py"
        swu_path = lh.workdir / "image.swu"
        script_source = linux.Path(lo, __file__).parent / "swupdate_script.py"
        shell.copy(script_source, script_path)
        shell.copy(swu_file, swu_path)
        lh.exec0("python3", script_path, swu_path, target_ip, str(timeout))
Example #2
0
def minisshd(h: linux.Lab, port: int = 2022) -> typing.Generator:
    """
    Create a new minisshd server and machine.

    Intended for use in a ``with``-statement::

        if check_minisshd(lh):
            with minisshd(lh) as ssh:
                ssh.exec0("uname", "-a")

    :param linux.Lab h: lab-host
    :param int port: Port for the ssh server, defaults to ``2022``.
    :rtype: MiniSSHMachine
    """
    server_dir = h.workdir / "minisshd"
    h.exec0("mkdir", "-p", server_dir)

    key_file = server_dir / "ssh_host_key"
    pid_file = server_dir / "dropbear.pid"

    # Create host key
    if not key_file.exists():
        h.exec0("dropbearkey", "-t", "rsa", "-f", key_file)

    h.exec0("dropbear", "-p", "127.0.0.1:2022", "-r", key_file, "-P", pid_file)

    # Try reading the file again if it does not yet exist
    for i in range(10):
        ret, pid = h.exec("cat", pid_file)
        if ret == 0:
            pid = pid.strip()
            break
    else:
        raise RuntimeError("dropbear did not create a pid-file!")

    try:
        with MiniSSHMachine(h, port) as ssh_machine:
            yield ssh_machine
    finally:
        tbot.log.message("Stopping dropbear ...")
        h.exec0("kill", pid)
Example #3
0
File: git.py Project: Lusus/tbot
def git_prepare(lab: linux.Lab) -> str:
    """Prepare a test git repo."""
    global _GIT

    if _GIT is None:
        # Git committer and author information in case the user's git
        # environment is not set up yet
        lab.env("GIT_AUTHOR_NAME", "tbot selftest")
        lab.env("GIT_AUTHOR_EMAIL", "*****@*****.**")
        lab.env("GIT_COMMITTER_NAME", "tbot selftest")
        lab.env("GIT_COMMITTER_EMAIL", "*****@*****.**")

        p = lab.workdir / "selftest-git-remote"

        if p.exists():
            lab.exec0("rm", "-rf", p)

        tbot.log.message("Setting up test repo ...")

        lab.exec0("mkdir", "-p", p)
        lab.exec0("git", "-C", p, "init")
        repo = git.GitRepository(p, clean=False)

        lab.exec0(
            "echo",
            """\
# tbot Selftest
This repo exists to test tbot's git testcase.

You can safely remove it, but please **do not** modify it as that might
break the tests.""",
            linux.RedirStdout(repo / "README.md"),
        )

        repo.add(repo / "README.md")
        repo.commit("Initial", author="tbot Selftest <none@none>")

        tbot.log.message("Creating test patch ...")
        lab.exec0(
            "echo",
            """\
# File 2
A second file that will have been added by patching.""",
            linux.RedirStdout(repo / "file2.md"),
        )

        repo.add(repo / "file2.md")
        repo.commit("Add file2", author="tbot Selftest <none@none>")
        patch_name = repo.git0("format-patch", "HEAD~1").strip()
        lab.exec0("mv", repo / patch_name, lab.workdir / "selftest-git.patch")

        tbot.log.message("Resetting repo ...")
        repo.reset("HEAD~1", git.ResetMode.HARD)

        _GIT = p._local_str()
        return _GIT
    else:
        return _GIT
Example #4
0
def selftest_decorated_lab(lh: linux.Lab) -> None:
    lh.exec0("uname", "-a")
Example #5
0
    def __init__(self, labhost: linux.Lab, port: int) -> None:
        """Create a new MiniSSHMachine."""
        self._port = port
        self._username = labhost.env("USER")

        super().__init__(labhost)
Example #6
0
def check_minisshd(h: linux.Lab) -> bool:
    """Check if this host can run a minisshd."""
    return h.test("which", "dropbear")
Example #7
0
def testpy(
    lh: linux.Lab,
    *,
    build_host: typing.Optional[linux.Builder] = None,
    uboot_builder: typing.Optional[uboot_tc.UBootBuilder] = None,
    boardenv: typing.Optional[str] = None,
    testpy_args: typing.List[str] = [],
) -> None:
    """
    Run U-Boot's test/py test-framework against the selected board.

    This testcase can be called from the command-line as ``uboot_testpy``.

    :param tbot.machine.linux.Builder build_host: Optional build-host where
        U-Boot should be built (and in this case, where test/py will run).  By
        default, ``tbot.acquire_lab().build()`` is used.
    :param tbot.tc.uboot.UBootBuilder uboot_builder: Optional configuration for
        U-Boot checkout.  By default, ``tbot.acquire_uboot().build`` is used
        (exactly like ``uboot_build`` does).
    :param str boardenv: Optional contents for the ``boardenv.py`` file.  If
        this option is not given, ``UBootBuilder.testpy_boardenv`` is used (or
        nothing).
    :param list(str) testpy_args: Additional arguments to be passed to test/py.
        Can be used, for example, to limit which tests should be run (using
        ``testpy_args=["-k", "sf"]``).

    **Example**:

    The following additions to a board-config make it possible to call
    ``tbot ... -vv uboot_testpy``:

    .. code-block:: python

        from tbot.tc import uboot

        class DHComUBootBuilder(uboot.UBootBuilder):
            name = "dhcom-pdk2"
            defconfig = "dh_imx6_defconfig"
            toolchain = "imx6q"

            testpy_boardenv = r\"""# Config for dhcom pdk2 board

        # A list of sections of Flash memory to be tested.
        env__sf_configs = (
            {
                # Where in SPI Flash should the test operate.
                'offset': 0x140000,
                # This value is optional.
                #   If present, specifies if the test can write to Flash offset
                #   If missing, defaults to False.
                'writeable': True,
            },
        )
        \"""


        class DHComUBoot(board.Connector, board.UBootShell):
            name = "dhcom-uboot"
            prompt = "=> "

            # Don't forget this!
            build = DHComUBootBuilder()
    """
    with contextlib.ExitStack() as cx:
        if build_host is not None:
            bh = cx.enter_context(build_host)
        else:
            bh = cx.enter_context(lh.build())
            if bh is lh:
                tbot.log.warning("""\
The build() method for the selected lab should not return `self` but instead `self.clone()`.

    Otherwise, `uboot_testpy` might not be able to run test/py in parallel to switching board
    power.

    Attempting to call build_host.clone() automatically now ...""")
                bh = cx.enter_context(bh.clone())

        # Spawn a subshell to not mess up the parent shell's environment and PWD
        cx.enter_context(bh.subshell())

        chan_console, chan_command = setup_testhooks(
            bh, cx.enter_context(bh.clone()), cx.enter_context(bh.clone()))

        if uboot_builder is None:
            builder = uboot_tc.UBootBuilder._get_selected_builder()
        else:
            builder = uboot_builder

        uboot_repo = uboot_tc.checkout(builder, clean=False, host=bh)

        # test/py wants to read U-Boot's config.  Run the builder's configure
        # step if no `.config` is available and then also generate `autoconf.mk`.
        dotconfig_missing = not (uboot_repo / ".config").exists()
        autoconfmk_missing = not (uboot_repo / "include" /
                                  "autoconf.mk").exists()

        if dotconfig_missing or autoconfmk_missing:
            with tbot.testcase("uboot_configure"), builder.do_toolchain(bh):
                tbot.log.message("Configuring U-Boot checkout ...")
                bh.exec0("cd", uboot_repo)

                if dotconfig_missing:
                    builder.do_configure(bh, uboot_repo)

                if autoconfmk_missing:
                    bh.exec0("make", "include/autoconf.mk")

        # Initialize the board
        # TODO: Add a parameter to allow passing in a board
        b = cx.enter_context(tbot.acquire_board(lh))  # type: ignore
        assert isinstance(b, board.PowerControl)
        ub = cx.enter_context(tbot.acquire_uboot(b))
        chan_uboot = ub.ch

        # If a boardenv was passed in, copy it to the build-host and set
        # a board-type to make test/py pick it up.
        board_type = "unknown"
        if boardenv is None:
            # If no explicit boardenv was given, maybe the builder has one.
            try:
                boardenv = getattr(builder, "testpy_boardenv")
            except AttributeError:
                pass
        if boardenv is not None:
            board_type = f"tbot-{b.name}"
            bt_filename = board_type.replace("-", "_")
            be_file = uboot_repo / "test" / "py" / f"u_boot_boardenv_{bt_filename}.py"

            be_file.write_text(boardenv)

        # Start test/py as an interactive command
        bh.exec0("cd", uboot_repo)
        chan_testpy = cx.enter_context(
            bh.run(
                "./test/py/test.py",
                "--build-dir",
                ".",
                "--board-type",
                board_type,
                *testpy_args,
            ))

        # We have to deal with incoming data on any of the following channels.
        # The comments denote what needs to be done for each channel:
        readfds = [
            chan_console,  # Send data to U-Boot
            chan_uboot,  # Send data to chan_console (test/py)
            chan_command,  # Powercycle the board
            chan_testpy,  # Read data so the log-event picks it up
        ]

        while True:
            r, _, _ = select.select(readfds, [], [])

            if chan_console in r:
                # Send data to U-Boot
                data = os.read(chan_console.fileno(), 4096)
                os.write(chan_uboot.fileno(), data)
            if chan_uboot in r:
                # Send data to chan_console (test/py)
                data = os.read(chan_uboot.fileno(), 4096)
                os.write(chan_console.fileno(), data)
            if chan_command in r:
                # Powercycle the board
                msg = chan_command.read()

                if msg[:2] == b"RE":
                    b.poweroff()
                    b.poweron()
                else:
                    raise Exception(f"Got unknown command {msg!r}!")
            if chan_testpy in r:
                # Read data so the log-event picks it up.  If a
                # DeathStringException occurs here, test/py finished and we
                # need to properly terminate the LinuxShell.run() context.
                try:
                    chan_testpy.read()
                except linux.CommandEndedException:
                    chan_testpy.terminate0()
                    break