first commit
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Purpose : Start, reach and stop the smoketest's QEMU virtual machines. Defines
|
||||
# what CoreDesktop's smoketest/lib/scenario.sh expects of a driver:
|
||||
# vm_exec, vm_put, vm_reboot, VM_GIT_SSH and VM_GIT_URL.
|
||||
|
||||
SMOKETEST_STATE="${XDG_CACHE_HOME:-$HOME/.cache}/coredesktop-dev/smoketest"
|
||||
IMAGES="$SMOKETEST_STATE/images"
|
||||
# Shared with every VM as its /var/cache/pacman/pkg, so packages download once
|
||||
PKG_CACHE="$SMOKETEST_STATE/pkg"
|
||||
SSH_KEY="$SMOKETEST_STATE/id_ed25519"
|
||||
KVM_DEVICE="${KVM_DEVICE:-/dev/kvm}"
|
||||
|
||||
VM_USER=tester
|
||||
VM_MEMORY="${SMOKETEST_MEMORY:-8G}"
|
||||
VM_CPUS="${SMOKETEST_CPUS:-4}"
|
||||
# Longest a single command in the VM may run (an install takes an hour or more)
|
||||
VM_EXEC_TIMEOUT="${SMOKETEST_EXEC_TIMEOUT:-4h}"
|
||||
# Seconds to wait for the VM to answer on SSH, and for GNOME to start
|
||||
VM_SSH_TIMEOUT="${SMOKETEST_SSH_TIMEOUT:-900}"
|
||||
VM_DESKTOP_TIMEOUT="${SMOKETEST_DESKTOP_TIMEOUT:-300}"
|
||||
|
||||
SSH_OPTS=(
|
||||
-i "$SSH_KEY"
|
||||
-o IdentitiesOnly=yes
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o LogLevel=ERROR
|
||||
-o BatchMode=yes
|
||||
-o ConnectTimeout=10
|
||||
-o ServerAliveInterval=15
|
||||
-o ServerAliveCountMax=8
|
||||
)
|
||||
|
||||
# Commands in the VM run as they would in a GNOME Terminal: with the GNOME
|
||||
# session's environment, in an interactive bash that has read ~/.bashrc
|
||||
VM_SESSION_ENV='eval "$(systemctl --user show-environment 2> /dev/null | sed "s/^/export /")"
|
||||
export TERM="${TERM:-dumb}"'
|
||||
|
||||
ensure_ssh_key() {
|
||||
mkdir -p "$SMOKETEST_STATE"
|
||||
[ -f "$SSH_KEY" ] || ssh-keygen -q -t ed25519 -N '' -C coredesktop-smoketest -f "$SSH_KEY"
|
||||
}
|
||||
|
||||
free_port() {
|
||||
local port i
|
||||
for i in $(seq 50); do
|
||||
port=$((20000 + RANDOM % 20000))
|
||||
[ -z "$(ss -Hltn "( sport = :$port )" 2> /dev/null)" ] && { echo "$port"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# vm_start DIR DISK [QEMU ARGS...]: boot DISK in the background. DIR gets the
|
||||
# pid file and the serial console's log.
|
||||
vm_start() {
|
||||
VM_DIR="$1"
|
||||
local disk="$2"
|
||||
shift 2
|
||||
local display=(-display none)
|
||||
[ -n "${SMOKETEST_VNC:-}" ] && display+=(-vnc "127.0.0.1:$SMOKETEST_VNC")
|
||||
|
||||
ensure_ssh_key
|
||||
mkdir -p "$VM_DIR" "$PKG_CACHE"
|
||||
VM_PORT="$(free_port)" || die "Couldn't find a free port for the VM's SSH."
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-name coredesktop-smoketest \
|
||||
-machine q35,accel=kvm \
|
||||
-cpu host \
|
||||
-smp "$VM_CPUS" \
|
||||
-m "$VM_MEMORY" \
|
||||
-drive "file=$disk,if=virtio,format=qcow2,discard=unmap" \
|
||||
-nic "user,model=virtio-net-pci,hostfwd=tcp:127.0.0.1:$VM_PORT-:22" \
|
||||
-virtfs "local,path=$PKG_CACHE,mount_tag=pkgcache,security_model=mapped-xattr" \
|
||||
-vga virtio \
|
||||
"${display[@]}" \
|
||||
-serial "file:$VM_DIR/serial.log" \
|
||||
-pidfile "$VM_DIR/qemu.pid" \
|
||||
-daemonize \
|
||||
"$@" || die "QEMU didn't start."
|
||||
|
||||
VM_GIT_SSH="ssh$(printf ' %q' "${SSH_OPTS[@]}")"
|
||||
VM_GIT_URL="ssh://$VM_USER@127.0.0.1:$VM_PORT"
|
||||
export VM_GIT_SSH VM_GIT_URL
|
||||
}
|
||||
|
||||
vm_pid() {
|
||||
cat "$VM_DIR/qemu.pid" 2> /dev/null
|
||||
}
|
||||
|
||||
vm_running() {
|
||||
local pid
|
||||
pid="$(vm_pid)" && [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null
|
||||
}
|
||||
|
||||
vm_ssh() {
|
||||
ssh "${SSH_OPTS[@]}" -p "$VM_PORT" "$VM_USER@127.0.0.1" "$@"
|
||||
}
|
||||
|
||||
# The command to SSH in by hand
|
||||
vm_ssh_command() {
|
||||
echo "ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $VM_PORT $VM_USER@127.0.0.1"
|
||||
}
|
||||
|
||||
wait_for_ssh() {
|
||||
local deadline=$((SECONDS + VM_SSH_TIMEOUT))
|
||||
until vm_ssh true < /dev/null 2> /dev/null; do
|
||||
vm_running || die "The VM stopped. Its console log: $VM_DIR/serial.log"
|
||||
(( SECONDS < deadline )) || die "The VM didn't answer on SSH. Its console log: $VM_DIR/serial.log"
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
# Wait for the automatic GNOME login. Not fatal: the health checks catch a
|
||||
# desktop that didn't start.
|
||||
wait_for_desktop() {
|
||||
local deadline=$((SECONDS + VM_DESKTOP_TIMEOUT))
|
||||
until vm_ssh "pgrep -u $VM_USER -x gnome-shell" < /dev/null &> /dev/null; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
echo "Warning: GNOME didn't start in the VM within ${VM_DESKTOP_TIMEOUT}s."
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
vm_wait_up() {
|
||||
wait_for_ssh
|
||||
wait_for_desktop
|
||||
}
|
||||
|
||||
# vm_exec SCRIPT: run SCRIPT with bash in the VM, as tester, in the GNOME
|
||||
# session's environment. Interactive bash's complaints about having no
|
||||
# terminal are dropped.
|
||||
vm_exec() {
|
||||
timeout "$VM_EXEC_TIMEOUT" ssh "${SSH_OPTS[@]}" -p "$VM_PORT" -T "$VM_USER@127.0.0.1" \
|
||||
"bash -ic $(printf '%q' "$VM_SESSION_ENV
|
||||
$1")" < /dev/null \
|
||||
2> >(grep -v -e '^bash: cannot set terminal process group' -e '^bash: no job control in this shell' >&2)
|
||||
}
|
||||
|
||||
# vm_put LOCAL REMOTE
|
||||
vm_put() {
|
||||
scp -q "${SSH_OPTS[@]}" -P "$VM_PORT" "$1" "$VM_USER@127.0.0.1:$2"
|
||||
}
|
||||
|
||||
vm_boot_id() {
|
||||
vm_ssh 'cat /proc/sys/kernel/random/boot_id' < /dev/null 2> /dev/null
|
||||
}
|
||||
|
||||
vm_reboot() {
|
||||
local before deadline=$((SECONDS + VM_SSH_TIMEOUT))
|
||||
echo "== reboot"
|
||||
before="$(vm_boot_id)"
|
||||
vm_ssh 'sudo systemctl reboot' < /dev/null &> /dev/null || true
|
||||
until [ "$(vm_boot_id)" != "$before" ] && [ -n "$(vm_boot_id)" ]; do
|
||||
vm_running || die "The VM stopped while rebooting. Its console log: $VM_DIR/serial.log"
|
||||
(( SECONDS < deadline )) || die "The VM didn't come back after rebooting. Its console log: $VM_DIR/serial.log"
|
||||
sleep 5
|
||||
done
|
||||
wait_for_desktop
|
||||
}
|
||||
|
||||
# Shut the VM down cleanly (so its disk can be kept), killing it if it hangs
|
||||
vm_stop() {
|
||||
vm_running || return 0
|
||||
vm_ssh 'sudo systemctl poweroff' < /dev/null &> /dev/null || true
|
||||
local deadline=$((SECONDS + 120))
|
||||
while vm_running && (( SECONDS < deadline )); do
|
||||
sleep 2
|
||||
done
|
||||
vm_kill
|
||||
}
|
||||
|
||||
vm_kill() {
|
||||
local pid
|
||||
pid="$(vm_pid)" || return 0
|
||||
[ -n "$pid" ] && kill "$pid" 2> /dev/null
|
||||
rm -f "$VM_DIR/qemu.pid"
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user