blob: a57baff953a780276c95c05d31d5e6b558f513a0 [file] [log] [blame]
#!/bin/bash
# Copyright 2022 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
SCRIPT_DIR="$(dirname "$(realpath -e "${BASH_SOURCE[0]}")")"
function print_help {
cat "${SCRIPT_DIR}/labtunnel_usage.txt"
echo ''
cat <<END
For more details and examples, view the README at "${SCRIPT_DIR}/README.md".
END
}
if [ $# -lt 2 ]; then
print_help
exit 1
fi
# Parse args and options
START_PORT="2200"
HOST_PROXY="access@chromeos1-proxy"
HOST_DUT=""
HOST_PCAP=""
HOST_CALLBOX=""
HOST_ROUTER=""
NO_PCAP=0
TUNNEL_TYPE=""
HOST_PARAM=""
DO_NOT_OPEN_VNC=0
DO_LEASE=0
LEASE_OPTIONS=(
"--reason=\"manual_testing\""
"--minutes=100"
)
LEASE_RETAIN=0
SSH_OPTIONS=(
"-o StrictHostKeyChecking=no"
"-o ExitOnForwardFailure=yes"
"-o UserKnownHostsFile=/dev/null"
"-o LogLevel=ERROR"
)
while [[ $# -gt 2 ]]; do
case $1 in
--help|help|-h)
print_help
exit
;;
--port|-p)
START_PORT="$2"
shift 2
;;
--dut)
HOST_DUT="$2"
HOST_PARAM="$2"
shift 2
;;
--pcap)
HOST_PCAP="$2"
shift 2
;;
--no-pcap)
NO_PCAP=1
shift 1
;;
--router)
HOST_ROUTER="$2"
shift 2
;;
--callbox)
HOST_CALLBOX="$2"
shift 2
;;
--proxy)
HOST_PROXY="$2"
shift 2
;;
--do-not-open-vnc)
DO_NOT_OPEN_VNC=1
shift 1
;;
--lease)
DO_LEASE=1
shift 1
;;
--lease-retain)
LEASE_RETAIN=1
shift 1
;;
--lease-option)
LEASE_OPTIONS+=("$2")
shift 2
;;
--lease-options)
IFS=' ' read -r -a LEASE_OPTIONS <<< "$2"
shift 2
;;
--ssh-option)
SSH_OPTIONS+=("$2")
shift 2
;;
--ssh-options)
IFS=' ' read -r -a SSH_OPTIONS <<< "$2"
shift 2
;;
*)
echo "Error: Invalid option '$1'"
print_help
exit 1
break
;;
esac
done
if [ "${HOST_PARAM}" == "" ]; then
if [ ! $# -eq 2 ]; then
echo "Error: Invalid params"
print_help
exit 1
fi
TUNNEL_TYPE="$1"
HOST_PARAM="$2"
else
if [ ! $# -eq 1 ]; then
echo "Error: Invalid params"
print_help
exit 1
fi
TUNNEL_TYPE="$1"
fi
if [ "${TUNNEL_TYPE}" == "dutvnc" ] && [ "${DO_NOT_OPEN_VNC}" -eq 0 ] \
&& ! command -v xtigervncviewer &> /dev/null; then
cat <<END
Error: Required xtigervncviewer not found
You can install TigerVNC on gLinux with 'sudo apt install tigervnc-viewer'
END
exit 1
fi
# Resolve undefined hosts where possible
function resolve_hostname {
local RESOLVED_NAME="$1"
local SUFFIX_TO_ADD="$2"
# Convert crosfleet name to host name by removing prefix
if [ "${RESOLVED_NAME:0:7}" = "crossk-" ]; then
local RESOLVED_NAME=${RESOLVED_NAME:7}
fi
# Add suffix, keeping any existing '.cros' suffix
if [ "${SUFFIX_TO_ADD}" != "" ]; then
if [ "${RESOLVED_NAME: -5}" == ".cros" ]; then
local RESOLVED_NAME="${RESOLVED_NAME:0:-5}${SUFFIX_TO_ADD}.cros"
else
local RESOLVED_NAME="${RESOLVED_NAME}${SUFFIX_TO_ADD}"
fi
fi
echo -n "${RESOLVED_NAME}"
}
function resolve_host_params {
if [ "${HOST_PARAM}" != "" ]; then
if [ "${HOST_DUT}" == "" ]; then
HOST_DUT=$(resolve_hostname "${HOST_PARAM}")
fi
if [ "${HOST_PCAP}" == "" ]; then
HOST_PCAP=$(resolve_hostname "${HOST_DUT}" "-pcap")
fi
if [ "${HOST_ROUTER}" == "" ]; then
HOST_ROUTER=$(resolve_hostname "${HOST_DUT}" "-router")
fi
fi
}
resolve_host_params
LEASED_DUT=""
function handle_dut_lease_start {
if [ "${DO_LEASE}" -eq 1 ]; then
LEASED_DUT="${HOST_PARAM}"
echo "Leasing DUT '${LEASED_DUT}' with options '${LEASE_OPTIONS[*]}'..."
local LEASE_OUTPUT
LEASE_OUTPUT=$(crosfleet dut lease "${LEASE_OPTIONS[@]}" \
--host="${LEASED_DUT}" 2>&1)
echo "${LEASE_OUTPUT}"
local MSG='Found 1 DUT(s) (0 busy) matching the provided DUT dimensions'
if echo "${LEASE_OUTPUT}" | grep -q "${MSG}"; then
echo "Successfully leased DUT '${LEASED_DUT}'"
else
echo "Failed to lease DUT '${LEASED_DUT}'"
kill_children_and_exit
fi
fi
}
function handle_dut_lease_stop {
if [ "${LEASED_DUT}" != "" ]; then
if [ "${LEASE_RETAIN}" -eq 1 ]; then
echo "Reminder: crosfleet dut '${LEASED_DUT}' is still leased"
else
echo "Abandoning leased bot '${LEASED_DUT}'..."
crosfleet dut abandon "${LEASED_DUT}"
fi
fi
}
PIDS=()
TMUX_SESSIONS=()
function kill_children {
echo -e "\nClosing labtunnel..."
for SNAME in "${TMUX_SESSIONS[@]}"; do
if tmux has-session -t "${SNAME}" 2>/dev/null; then
echo "Closing tmux session '${SNAME}'..."
tmux send-keys -t "${SNAME}" C-c "exit" ENTER
sleep 1s
tmux kill-session -t "${SNAME}" 2>/dev/null
fi
done
echo -e "Killing child processes..."
for PID in "${PIDS[@]}"; do
if [ -d "/proc/${PID}" ]; then
pkill --signal SIGQUIT -P "${PID}"
fi
done
handle_dut_lease_stop
}
function kill_children_and_exit {
trap - SIGINT SIGQUIT SIGHUP
kill_children
exit
}
TUNNEL_SUMMARY="Successfully created tunnels"
LAST_TUNNEL_ENDPOINT=""
function make_tunnel {
PIDS+=("$$")
local TUNNEL_NAME=$1
local REMOTE_HOST=$2
local LOCAL_PORT=$3
local HOST=$4
local REMOTE_PORT=$5
local LOCAL_ENDPOINT="localhost:${LOCAL_PORT}"
local REMOTE_ENDPOINT="${REMOTE_HOST}:${REMOTE_PORT}"
local TUNNEL_DESC="${TUNNEL_NAME}: ${LOCAL_ENDPOINT} ->"
TUNNEL_DESC="${TUNNEL_DESC} ${HOST} -> ${REMOTE_ENDPOINT}"
echo "Creating SSH tunnel ${TUNNEL_DESC}..."
local TUNNEL_PATH="${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT}"
ssh "${SSH_OPTIONS[@]}" -fNL "${TUNNEL_PATH}" "${HOST}"
local ERROR_CODE=$?
if [ ! "${ERROR_CODE}" -eq 0 ]; then
echo "Failed to create SSH tunnel ${TUNNEL_DESC} (error=${ERROR_CODE})."
echo "Please address the ssh error and try again."
kill_children_and_exit
fi
TUNNEL_SUMMARY="${TUNNEL_SUMMARY}\n ${TUNNEL_DESC}"
LAST_TUNNEL_ENDPOINT=${LOCAL_ENDPOINT}
}
TMUX_SESSION_NAME=""
function ssh_run_cmd {
PIDS+=("$$")
local HOST=$1
local CMD=$2
TMUX_SESSION_NAME="labtunnel_tmux_ssh_$(date +%s)"
TMUX_SESSIONS+=("${TMUX_SESSION_NAME}")
echo "Running '${CMD}' on '${HOST}' in local tmux session '" \
"${TMUX_SESSION_NAME}'..."
local SSH_CMD=("ssh" "${SSH_OPTIONS[@]}" "${HOST}")
tmux new-session -d -s "${TMUX_SESSION_NAME}" "${SSH_CMD[@]}"
tmux send-keys -t "${TMUX_SESSION_NAME}" "${CMD}" ENTER
tmux send-keys -t "${TMUX_SESSION_NAME}" "exit" ENTER
}
trap kill_children_and_exit SIGINT SIGQUIT SIGHUP
case ${TUNNEL_TYPE} in
wificell)
handle_dut_lease_start
make_tunnel "DUT" "localhost" "${START_PORT}" "${HOST_DUT}" 22
DUT=${LAST_TUNNEL_ENDPOINT}
make_tunnel "router" "localhost" $((START_PORT+1)) "${HOST_ROUTER}" 22
ROUTER=${LAST_TUNNEL_ENDPOINT}
if [ "${NO_PCAP}" -eq 0 ]; then
make_tunnel "pcap" "localhost" $((START_PORT+2)) "${HOST_PCAP}" 22
PCAP=${LAST_TUNNEL_ENDPOINT}
fi
echo -e "${TUNNEL_SUMMARY}"
echo -e "\nExample tast call (in chroot):"
if [ "${NO_PCAP}" -eq 0 ]; then
echo "tast run -var=router=${ROUTER} -var=pcap=${PCAP} ${DUT} <test>"
else
echo "tast run -var=router=${ROUTER} ${DUT} <test>"
fi
;;
callbox)
if [ "${HOST_CALLBOX}" = "" ]; then
echo "Error: --callbox param required for callbox tunnels"
print_help
exit 1
fi
handle_dut_lease_start
make_tunnel "DUT" "localhost" "${START_PORT}" "${HOST_DUT}" 22
DUT=${LAST_TUNNEL_ENDPOINT}
make_tunnel "callboxManager" "localhost" $((START_PORT+1)) \
"${HOST_PROXY}" 5000
CALLBOX_MANAGER=${LAST_TUNNEL_ENDPOINT}
make_tunnel "callbox" "${HOST_CALLBOX}" 5025 "${HOST_PROXY}" 5025
echo -e "${TUNNEL_SUMMARY}"
echo -e "\nExample tast call (in chroot):"
echo "tast run -var=callbox=${HOST_CALLBOX} " \
"-var=callboxManager=${CALLBOX_MANAGER} ${DUT} <test>"
;;
dut)
handle_dut_lease_start
make_tunnel "DUT" "localhost" "${START_PORT}" "${HOST_DUT}" 22
DUT=${LAST_TUNNEL_ENDPOINT}
echo -e "${TUNNEL_SUMMARY}"
echo -e "\nExample tast call (in chroot):"
echo "tast run ${DUT} <test>"
;;
dutvnc)
handle_dut_lease_start
make_tunnel "DUT_VNC" "localhost" 5900 "${HOST_DUT}" 5900
echo -e "${TUNNEL_SUMMARY}"
echo "Starting kmsvnc on dut..."
ssh_run_cmd "${HOST_DUT}" "kmsvnc"
sleep 5s
if ! tmux has-session -t "${TMUX_SESSION_NAME}" 2>/dev/null; then
echo "Failed to start kmsvnc on dut"
kill_children_and_exit
fi
if [ "${DO_NOT_OPEN_VNC}" -eq 0 ]; then
echo "Launching TigerVNC..."
xtigervncviewer localhost:5900 -Log "*:stderr:0" &
else
echo "DUT VNC available at localhost:5900"
fi
;;
*)
echo "Error: Invalid tunnel type '${TUNNEL_TYPE}'"
print_help
exit 1
;;
esac
echo -e "\nTo shut down tunnels and sub-processes, exit this process " \
"(pid=$$) with SIGHUP, SIGINT, or SIGQUIT"
sleep infinity