Source code for tbot.tc.shell

# tbot, Embedded Automation Tool
# Copyright (C) 2018  Harald Seiler
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import typing
import tbot
from tbot.machine import linux
from tbot.machine.linux import auth

__all__ = ("copy",)

H1 = typing.TypeVar("H1", bound=linux.LinuxMachine)
H2 = typing.TypeVar("H2", bound=linux.LinuxMachine)


def _scp_copy(
    *,
    local_path: linux.Path[H1],
    remote_path: linux.Path[H2],
    copy_to_remote: bool,
    username: str,
    hostname: str,
    ignore_hostkey: bool,
    port: int,
    ssh_config: typing.List[str],
    authenticator: auth.Authenticator,
) -> None:
    local_host = local_path.host

    hk_disable = ["-o", "StrictHostKeyChecking=no"] if ignore_hostkey else []

    scp_command = [
        "scp",
        *["-P", str(port)],
        *hk_disable,
        *[arg for opt in ssh_config for arg in ["-o", opt]],
    ]

    if isinstance(authenticator, auth.PrivateKeyAuthenticator):
        scp_command += ["-o", "BatchMode=yes", "-i", authenticator.key_file]
    elif isinstance(authenticator, auth.NoneAuthenticator):
        scp_command += ["-o", "BatchMode=yes"]
    elif isinstance(authenticator, auth.PasswordAuthenticator):
        scp_command = ["sshpass", "-p", authenticator.password] + scp_command

    if copy_to_remote:
        local_host.exec0(
            *scp_command,
            local_path,
            f"{username}@{hostname}:{remote_path._local_str()}",
        )
    else:
        local_host.exec0(
            *scp_command,
            f"{username}@{hostname}:{remote_path._local_str()}",
            local_path,
        )


[docs]@tbot.testcase def copy(p1: linux.Path[H1], p2: linux.Path[H2]) -> None: """ Copy a file, possibly from one host to another. The following transfers are currently supported: * ``H`` 🢥 ``H`` (transfer without changing host) * :class:`~tbot.machine.linux.LabHost` 🢥 :class:`~tbot.machine.linux.SSHMachine` (Using ``scp``) * :class:`~tbot.machine.linux.SSHMachine` 🢥 :class:`~tbot.machine.linux.LabHost` (Using ``scp``) * :class:`~tbot.machine.linux.lab.LocalLabHost` 🢥 :class:`~tbot.machine.linux.lab.SSHLabHost` (Using ``scp``) * :class:`~tbot.machine.linux.lab.SSHLabHost` 🢥 :class:`~tbot.machine.linux.lab.LocalLabHost` (Using ``scp``) The following transfers are **not** supported: * :class:`~tbot.machine.linux.SSHMachine` 🢥 :class:`~tbot.machine.linux.SSHMachine` (There is no guarantee that two remote hosts can connect to each other. If you need this, transfer to the lab-host first and then to the other remote) * :class:`~tbot.machine.linux.LabHost` 🢥 :class:`~tbot.machine.board.BoardMachine` (Transfers over serial are not (yet) implemented. To 'upload' files, connect to your target via ssh or use a tftp download) :param linux.Path p1: Exisiting path to be copied :param linux.Path p2: Target where ``p1`` should be copied """ if isinstance(p1.host, p2.host.__class__) or isinstance(p2.host, p1.host.__class__): # Both paths are on the same host p2_w1 = linux.Path(p1.host, p2) p1.host.exec0("cp", p1, p2_w1) return elif isinstance(p1.host, linux.SSHMachine) and p1.host.labhost is p2.host: # Copy from an SSH machine _scp_copy( local_path=p2, remote_path=p1, copy_to_remote=False, username=p1.host.username, hostname=p1.host.hostname, ignore_hostkey=p1.host.ignore_hostkey, port=p1.host.port, ssh_config=p1.host.ssh_config, authenticator=p1.host.authenticator, ) elif isinstance(p2.host, linux.SSHMachine) and p2.host.labhost is p1.host: # Copy to an SSH machine _scp_copy( local_path=p1, remote_path=p2, copy_to_remote=True, username=p2.host.username, hostname=p2.host.hostname, ignore_hostkey=p2.host.ignore_hostkey, port=p2.host.port, ssh_config=p2.host.ssh_config, authenticator=p2.host.authenticator, ) elif isinstance(p1.host, linux.lab.LocalLabHost) and isinstance( p2.host, linux.lab.SSHLabHost ): # Copy from local to ssh labhost _scp_copy( local_path=p1, remote_path=p2, copy_to_remote=True, username=p2.host.username, hostname=p2.host.hostname, ignore_hostkey=p2.host.ignore_hostkey, port=p2.host.port, ssh_config=[], authenticator=p2.host.authenticator, ) elif isinstance(p2.host, linux.lab.LocalLabHost) and isinstance( p1.host, linux.lab.SSHLabHost ): # Copy to local from ssh labhost _scp_copy( local_path=p2, remote_path=p1, copy_to_remote=False, username=p1.host.username, hostname=p1.host.hostname, ignore_hostkey=p1.host.ignore_hostkey, port=p1.host.port, ssh_config=[], authenticator=p1.host.authenticator, ) else: raise NotImplementedError(f"Can't copy from {p1.host} to {p2.host}!")