ops-sat-rs/scripts/bld-deploy-remote.py

194 lines
5.8 KiB
Python
Raw Normal View History

2024-04-08 16:47:31 +02:00
#!/usr/bin/env python3
"""Small portable helper script to build, deply and run a Rust application
on a remote machine, e.g. a Raspberry Pi"""
import argparse
import os
import sys
import platform
import time
from typing import Final
# TODO: Should we make this configurable?
BUILDER = "cross"
# This script can easily be adapted to other remote machines, Linux boards and
# remote configurations by tweaking / hardcoding these parameter, which generally are constant
# for a given board
DEFAULT_USER_NAME: Final = "root"
DEFAULT_ADDRESS: Final = "192.254.108.30"
DEFAULT_TOOLCHAIN: Final = "armv7-unknown-linux-gnueabihf"
DEFAULT_APP_NAME: Final = "ops-sat-sw"
DEFAULT_TARGET_FOLDER: Final = "/tmp"
DEFAULT_DEBUG_PORT: Final = "1234"
DEFAULT_GDB_APP = "gdb-multiarch"
def main():
bld_deploy_run(parse_arguments())
def parse_arguments():
desc = (
"Rust Remote Deployment Helper."
"Builds the image and can optionally transfer and run "
"it on the target system as well."
)
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
"-u",
"--user",
default=f"{DEFAULT_USER_NAME}",
help=f"Username for ssh access. Default: {DEFAULT_USER_NAME}",
)
parser.add_argument(
"-a",
"--address",
default=f"{DEFAULT_ADDRESS}",
help=f"Remote SSH address. Default: {DEFAULT_ADDRESS}",
)
parser.add_argument(
"--tc",
default=f"{DEFAULT_TOOLCHAIN}",
help=f"Target toolchain. Default: {DEFAULT_TOOLCHAIN}",
)
parser.add_argument(
"--app",
default=f"{DEFAULT_APP_NAME}",
help=f"Target appname. Default: {DEFAULT_APP_NAME}",
)
parser.add_argument(
"--source",
help="Target destination path. Default: Built from other arguments",
)
parser.add_argument(
"--dest",
default=f"{DEFAULT_TARGET_FOLDER}",
help=f"Target destination path. Default: {DEFAULT_TARGET_FOLDER}",
)
parser.add_argument(
"other", nargs=argparse.REMAINDER, help="Argument forwarded to cargo build"
)
parser.add_argument(
"-b",
"--build",
action="store_true",
help="Build application",
)
parser.add_argument(
"-t",
"--transfer",
action="store_true",
help="Transfer application to remote machine",
)
parser.add_argument(
"-r",
"--run",
action="store_true",
help="Run application on remote machine",
)
parser.add_argument(
"-d",
"--debug",
action="store_true",
help="Run gdbserver on remote machine for remote debugging",
)
parser.add_argument(
"-s",
"--start",
action="store_true",
help="Start local GDB session, connecting to the remote GDB server",
)
parser.add_argument("--gdb", default="gdb-multiarch", help="GDB application to use")
parser.add_argument(
"-p",
"--port",
default=f"{DEFAULT_DEBUG_PORT}",
help="Port to use for remote debugging",
)
parser.add_argument(
"-e",
"--sshenv",
action="store_true",
help="Take password from environmental variable",
)
parser.add_argument(
"--release",
action="store_true",
help="Supply --release to build command",
)
parser.add_argument(
"-f",
"--sshfile",
help="SSH key file. Otherwise, use password from environmental variable SSHPASS",
)
return parser.parse_args()
def bld_deploy_run(args):
cargo_opts = ""
build_folder = "debug"
if args.release:
cargo_opts += "--release"
build_folder = "release"
for other in args.other:
cargo_opts += f"{other}"
sshpass_args = ""
if args.sshfile:
sshpass_args = f"-f {args.sshfile}"
elif args.sshenv:
sshpass_args = "-e"
ssh_target_ident = f"{args.user}@{args.address}"
sshpass_cmd = ""
if platform.system() != "Windows":
sshpass_cmd = f"sshpass {sshpass_args}"
dest_path = f"{args.dest}/{args.app}"
if not args.source:
source_path = f"{os.getcwd()}/target/{args.tc}/{build_folder}/{args.app}"
else:
source_path = args.source
build_cmd = f"{BUILDER} build {cargo_opts}"
if args.build:
print(f"Running build command: {build_cmd}")
os.system(build_cmd)
if args.transfer:
if not os.path.exists(source_path):
print(f"No application found at {source_path}")
sys.exit(1)
scp_target_dest = f'{ssh_target_ident}:"{dest_path}"'
transfer_cmd = f"{sshpass_cmd} scp {source_path} {scp_target_dest}"
print(f"Running transfer command: {transfer_cmd}")
os.system(transfer_cmd)
if args.run:
run_cmd = f"{sshpass_cmd} ssh {ssh_target_ident} {dest_path}"
print(f"Running target application: {run_cmd}")
os.system(run_cmd)
elif args.debug:
# Kill all running gdbserver applications first
# Then start the GDB server
debug_shell_cmd = (
f"sh -c 'killall -q gdbserver; gdbserver *:{args.port} {dest_path}'"
)
# Execute the command above and also set up port forwarding. This allows to connect
# to localhost:17777 on the local development machine
ssh_flags = "-L"
if args.start:
ssh_flags += " -f"
debug_cmd = (
f"{sshpass_cmd} ssh {ssh_flags} {args.port}:localhost:{args.port} {ssh_target_ident} "
f'"{debug_shell_cmd}"'
)
print(f"Running debug command: {debug_cmd}")
os.system(debug_cmd)
if args.start:
time.sleep(0.2)
start_cmd = f"{args.gdb} -q -x gdb.gdb {source_path}"
print(f"Running start command: {start_cmd}")
os.system(start_cmd)
if __name__ == "__main__":
main()