Initial Commit
This commit is contained in:
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
# =====================================================================
|
||||
# Import a prebuilt base image instead of building one.
|
||||
#
|
||||
# sudo ./vm/images/import.sh ubuntu
|
||||
# sudo ./vm/images/import.sh windows
|
||||
# sudo ./vm/images/import.sh both
|
||||
#
|
||||
# WHY THIS EXISTS
|
||||
#
|
||||
# Building Ubuntu takes about 25 unattended minutes. Windows has no
|
||||
# unattended path at all - it is a person clicking through setup in a VNC
|
||||
# console, loading a virtio driver by hand, and knowing when to stop. For
|
||||
# anyone who just wants the sub-agent working, downloading a finished
|
||||
# image is the difference between an afternoon and a coffee.
|
||||
#
|
||||
# WHY IT IS A SCRIPT AND NOT A LIST OF STEPS
|
||||
#
|
||||
# Three things have to be exactly right or the result fails later, in
|
||||
# ways that do not point at their cause:
|
||||
#
|
||||
# * the owner must be owuivmadm, or the maintenance tool cannot thaw it
|
||||
# * the mode must be 0444, because FROZEN IS THE OWNER WRITE BIT and
|
||||
# nothing else - a 0644 base means vmd refuses every session with
|
||||
# "freeze it and try again"
|
||||
# * the .sha256 must land beside the disk, or every saved fast-start
|
||||
# machine is treated as stale and sessions silently boot from cold
|
||||
#
|
||||
# A checklist gets those wrong. This does not.
|
||||
# =====================================================================
|
||||
set -euo pipefail
|
||||
|
||||
BASE=${OWUIVM_BASE_DIR:-/var/lib/owuivm/base}
|
||||
# Override to host your own. Any web server will do: the script only ever
|
||||
# does a ranged GET, and it verifies what it gets.
|
||||
SOURCE=${OWUIVM_IMAGE_SOURCE:-https://nextcloud.grantlan.com/s/OWUIToolsVMDisks}
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || { echo "import.sh must run as root (it writes $BASE)" >&2; exit 1; }
|
||||
command -v curl >/dev/null || { echo "FAILED: curl is not installed" >&2; exit 1; }
|
||||
getent passwd owuivmadm >/dev/null || {
|
||||
echo "FAILED: there is no owuivmadm user - run vm/host/install.sh first" >&2; exit 1; }
|
||||
|
||||
# Roughly what each one needs, so a download does not die at 95% with the
|
||||
# filesystem full. qcow2 is sparse but these are not thin.
|
||||
declare -A NEED=( [ubuntu]=11 [windows]=44 )
|
||||
|
||||
fetch() { # fetch <remote-name> <destination>
|
||||
local name="$1" dest="$2"
|
||||
# Nextcloud's public-share download endpoint. `-C -` resumes, which
|
||||
# matters: windows.qcow2 is 40 GB and a dropped connection three
|
||||
# quarters of the way through should not mean starting again.
|
||||
curl -fSL --retry 3 --retry-delay 5 -C - \
|
||||
-o "$dest" "$SOURCE/download?path=%2F&files=$name"
|
||||
}
|
||||
|
||||
import_one() {
|
||||
local name="$1"
|
||||
local disk="$BASE/$name.qcow2" vars="$BASE/$name-OVMF_VARS.fd" sums="$BASE/$name.sha256"
|
||||
|
||||
# A FINISHED IMAGE IS ONE THAT IS FROZEN. A part-downloaded one is not.
|
||||
#
|
||||
# The obvious guard - refuse if the file exists - contradicts the promise
|
||||
# that this is resumable: a download interrupted at 30 GB could never be
|
||||
# re-run. So the test is the same one everything else here uses, the
|
||||
# owner write bit. import.sh sets it 0444 only after the checksum has
|
||||
# passed, so a writable disk is by definition an unfinished one.
|
||||
if [ -e "$disk" ] && [ "$(( 8#$(stat -c %a "$disk") & 8#200 ))" -eq 0 ]; then
|
||||
echo "FAILED: $disk is already here and frozen." >&2
|
||||
echo " Importing would replace a base image you may have changed. Delete it" >&2
|
||||
echo " first if that is what you want - and note that any saved fast-start" >&2
|
||||
echo " machine for it becomes stale." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -e "$disk" ]; then
|
||||
echo "==> $name: an unfinished download is here; resuming it"
|
||||
fi
|
||||
|
||||
local free_gb
|
||||
free_gb=$(df -BG --output=avail "$BASE" | tail -1 | tr -dc '0-9')
|
||||
if [ "$free_gb" -lt "${NEED[$name]}" ]; then
|
||||
echo "FAILED: $name needs about ${NEED[$name]} GB and $BASE has ${free_gb} GB free" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> $name: downloading (${NEED[$name]} GB or so; resumable, ^C and re-run is safe)"
|
||||
install -d -o owuivmadm -g owuivmadm -m 0775 "$BASE"
|
||||
fetch "$name.sha256" "$sums"
|
||||
fetch "$name-OVMF_VARS.fd" "$vars"
|
||||
fetch "$name.qcow2" "$disk"
|
||||
|
||||
echo "==> $name: verifying"
|
||||
# BY BASENAME, NOT `sha256sum -c`.
|
||||
#
|
||||
# The manifest carries absolute paths - /var/lib/owuivm/base/... - because
|
||||
# that is where the freeze wrote it. `sha256sum -c` would therefore check
|
||||
# the files at THOSE paths, which are the ones already installed, not the
|
||||
# ones just downloaded. At the default location they are the same file and
|
||||
# it passes for the wrong reason; with OWUIVM_BASE_DIR set it verifies
|
||||
# somebody else's disk, or fails on a file that is not there.
|
||||
#
|
||||
# Measured: a test import into a scratch directory reported both files OK
|
||||
# while the scratch copy was a quarter downloaded.
|
||||
local bad=0 want got file
|
||||
while read -r want file; do
|
||||
[ -n "$want" ] || continue
|
||||
got=$(sha256sum "$BASE/$(basename "$file")" | awk '{print $1}')
|
||||
if [ "$want" = "$got" ]; then
|
||||
echo " $(basename "$file"): OK"
|
||||
else
|
||||
echo " $(basename "$file"): MISMATCH" >&2
|
||||
bad=1
|
||||
fi
|
||||
done < "$sums"
|
||||
if [ "$bad" -ne 0 ]; then
|
||||
echo "FAILED: the download is corrupt or incomplete." >&2
|
||||
echo " Re-run to resume, or delete $BASE/$name.* and start again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> $name: freezing"
|
||||
chown owuivmadm:owuivmadm "$disk" "$vars" "$sums"
|
||||
# 0444 IS the freeze. See the header.
|
||||
chmod 0444 "$disk" "$vars"
|
||||
chmod 0644 "$sums"
|
||||
echo " $name imported and frozen"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
ubuntu|windows) import_one "$1" ;;
|
||||
both) import_one ubuntu; import_one windows ;;
|
||||
*) echo "usage: import.sh {ubuntu|windows|both}" >&2; exit 64 ;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "Done. Open Tool Server VM Maintenance and the row should read 'frozen'."
|
||||
echo "Sessions boot these from cold; press 'Save fast start' to make them resume"
|
||||
echo "in seconds instead."
|
||||
Reference in New Issue
Block a user