
- Introduced VMLEGACY environment variable to ensure that old VMs utilize the legacy media share located at /lmn/media/USER. - This change is aimed at maintaining compatibility with older virtual machines that rely on the previous media share structure.
413 lines
14 KiB
Bash
Executable file
413 lines
14 KiB
Bash
Executable file
#!/usr/bin/bash
|
|
# create and run clone
|
|
|
|
set -eu
|
|
|
|
show_help() {
|
|
cat << EOF >&2
|
|
Usage: $(basename "$0") [options] vmname"
|
|
Create a new clone, start the vm (if not yet running) and run virt-viewer.
|
|
Squid-Proxy will be started too.
|
|
options:
|
|
-n|--new new clone will be created, even if exists
|
|
-p|--persistent new clone will be created persistent, so available after reboot too
|
|
-s|--system qemu:///system instead of default qemu:///session
|
|
--no-viewer start without viewer
|
|
--heads n number of displays
|
|
--memory sizeMB memory size in MB
|
|
--cpu num number of CPUs
|
|
--os OS operating system (win10|linux|..)
|
|
--data-disk size additional data-disk
|
|
--bridge virbrX additional network interface on bridge virbrX
|
|
--uid uid set uid on guest
|
|
--gid gid set gid on guest
|
|
--macvtap additional network interface on device macvtap
|
|
--options options additional options for virt-install command
|
|
EOF
|
|
}
|
|
|
|
exit_script() {
|
|
echo "run-vm.sh ${VM_NAME} terminated by trap!" >> "/tmp/${UID}/exit-run-vm.log"
|
|
virsh --connect="${QEMU}" destroy "${VM_NAME}-clone"
|
|
trap - SIGHUP SIGINT SIGTERM # clear the trap
|
|
kill -- -$$ # Sends SIGTERM to child/sub processes
|
|
}
|
|
|
|
check_images() {
|
|
# sync vm-torrent and TPM data
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${VM_NAME}.qcow2.torrent" "${VM_NAME}.permall"
|
|
[[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${VM_NAME}.qcow2"
|
|
|
|
BACKINGARRAY=()
|
|
imgfile="${VM_SYSDIR}/${VM_NAME}.qcow2" && [[ -f "${VM_DIR}/${VM_NAME}.qcow2" ]] && imgfile="${VM_DIR}/${VM_NAME}.qcow2"
|
|
BACKINGARRAY+=("${imgfile}")
|
|
echo "Imgfile=$imgfile"
|
|
if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
|
|
if [[ ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2.torrent" ]]; then
|
|
echo "no base VM disk '${VM_NAME}.qcow2' found and/or ${VM_NAME} not found on server" >&2
|
|
exit 1
|
|
fi
|
|
# sync vm-disk image by torrent
|
|
echo "Try to sync VM ${VM_NAME} by torrent"
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${VM_NAME}"
|
|
fi
|
|
|
|
backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
|
|
while [[ -n "${backingfile}" ]]; do
|
|
echo "Backingfile required: ${backingfile}"
|
|
imgfile="${VM_SYSDIR}/${backingfile}" && [[ -f "${VM_DIR}/${backingfile}" ]] && imgfile="${VM_DIR}/${backingfile}"
|
|
BACKINGARRAY+=("${imgfile}")
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${backingfile}.torrent"
|
|
[[ -f "${VM_SYSDIR}/${backingfile}" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${backingfile}"
|
|
if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
|
|
# sync vm-disk image by torrent
|
|
echo "Try to sync backingfile ${backingfile} by torrent"
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${backingfile%.qcow2}"
|
|
fi
|
|
backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
|
|
done
|
|
|
|
echo "VM-Image and required backingfiles available"
|
|
echo "Now, let's check the images."
|
|
|
|
# Check VM-Images in reverse order
|
|
for ((i=${#BACKINGARRAY[@]}-1; i>=0; i--))
|
|
do
|
|
echo "Checking ${BACKINGARRAY[$i]}"
|
|
if ! qemu-img check -U "${BACKINGARRAY[$i]}" 2>/dev/null; then
|
|
echo "check failed!"
|
|
echo "sync ${BACKINGARRAY[$i]} again"
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${BACKINGARRAY[$i]}.torrent"
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "$(basename "${BACKINGARRAY[$i]}" .qcow2)"
|
|
fi
|
|
done
|
|
echo "VM-Image and required backingfiles available and checked"
|
|
|
|
sudo -u lmnsynci /usr/local/bin/vm-sync update_usage_information ${BACKINGARRAY[*]}
|
|
}
|
|
|
|
create_clone() {
|
|
local VM_NAME="$1"
|
|
|
|
if ! [[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2" ]]; then
|
|
echo "qcow2 File does not exists." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Create User-VM-Dir and link system VM-Images
|
|
[[ -d "${VM_DIR}" ]] || mkdir -p "${VM_DIR}"
|
|
IMAGE="${VM_NAME}.qcow2"
|
|
while [[ -n ${IMAGE} ]]; do
|
|
if [[ "${PERSISTENT}" -eq 1 ]]; then
|
|
sudo /usr/local/bin/vm-link-images -p "${IMAGE}"
|
|
else
|
|
sudo /usr/local/bin/vm-link-images "${IMAGE}"
|
|
fi
|
|
IMAGE="$(qemu-img info -U "${VM_DIR}/${IMAGE}" | grep "^backing file:" | cut -d ' ' -f 3)"
|
|
done
|
|
|
|
# Create backing file
|
|
cd "${VM_DIR}"
|
|
qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-clone.qcow2"
|
|
|
|
if [[ -f "${VM_SYSDIR}/${VM_NAME}.permall" ]]; then
|
|
# Copy tpm file
|
|
if [[ ! -f "${VM_NAME}.permall" ]]; then
|
|
echo "copy tpm-file"
|
|
cp "${VM_SYSDIR}/${VM_NAME}.permall" .
|
|
fi
|
|
# create tpm-clone file
|
|
echo "create tpm-clone-file"
|
|
cp "${VM_NAME}.permall" "${VM_NAME}-clone.permall"
|
|
fi
|
|
|
|
}
|
|
|
|
create_printerlist() {
|
|
## Prepare .printerlist.csv
|
|
mkdir -p "${VM_MEDIADIR}"
|
|
chgrp "$(id -g)" "${VM_MEDIADIR}"
|
|
echo "Name;IppURL" > "${VM_MEDIADIR}/.printerlist.csv"
|
|
for p in $(lpstat -v | cut -f 3 -d" " | sed 's/:$//'); do
|
|
echo "$p;ipp://192.168.122.1/printers/$p" >> "${VM_MEDIADIR}/.printerlist.csv"
|
|
done
|
|
}
|
|
|
|
create_mountlist() {
|
|
NETHOMEPART="${NETHOME#/srv/samba/schools}"
|
|
cat << EOF > "${VMINFO_DIR}/.mounts.csv"
|
|
Drive;Remotepath
|
|
H;\\\\server.pn.steinbeis.schule${NETHOMEPART//\//\\}
|
|
T;\\\\server.pn.steinbeis.schule\\default-school\\share
|
|
EOF
|
|
echo "${USER}" > "/${VMINFO_DIR}/.user"
|
|
}
|
|
|
|
start_virtiofs_service() {
|
|
local target_name=$1
|
|
local shared_dir=$2
|
|
local drive_letter=$3
|
|
local socket="/run/user/${UID}/virtiofs-${VM_NAME}-${target_name,,}.sock"
|
|
|
|
systemd-run --user /usr/local/bin/virtiofsd --uid-map=":${GUEST_UID}:${UID}:1:" --gid-map=":${GUEST_GID}:$(id -g):1:" \
|
|
--socket-path "${socket}" --shared-dir "${shared_dir}" --syslog
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Error starting virtiofsd for ${target_name}." >&2
|
|
return 1
|
|
fi
|
|
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=${target_name},xpath1.set=./source/@socket=${socket}"
|
|
}
|
|
|
|
start_virtiofsd() {
|
|
# BEGIN temporary fix, while linux-starter are not migrated to --uid and --gid
|
|
if [[ "$LIBVIRTOSINFO" =~ debian.* ]]; then
|
|
[[ "$GUEST_UID" == 0 ]] && GUEST_UID=1010
|
|
[[ "$GUEST_GID" == 0 ]] && GUEST_GID=1010
|
|
fi
|
|
# END temporary fix
|
|
|
|
# start_virtiofs_service "VM-Data" "/lmn/media/${USER}" "Y"
|
|
# start_virtiofs_service "default-school" "/srv/samba/schools/default-school" "Y"
|
|
|
|
# Home@PC / VM-Data
|
|
# if the environment variable VMLEGACY is set, /lmn/media/USER is forced
|
|
if [[ "${HOME}" != "${NETHOME}" && ! -v VMLEGACY ]]; then
|
|
start_virtiofs_service "Home_Linux" "${HOME}" "Y"
|
|
else
|
|
start_virtiofs_service "VM-Data" "/lmn/media/${USER}" "Y"
|
|
fi
|
|
}
|
|
|
|
ask_really_persistent() {
|
|
cat << EOF >&2
|
|
|
|
!!!!!!!!!!!!!!! Wichtig !!!!!!!!!!!!!!
|
|
|
|
Auf dem Computer existiert noch keine persistente VM mit dem Namen ${VM_NAME}.
|
|
Das Anlegen persistenter Maschinen sollte nur auf Computern geschehen,
|
|
die dem jeweiligen Benutzer zugeordnet sind.
|
|
In Klassenzimmern oder Computerräumen ist das Verwenden persistenter
|
|
Maschinen normalerweise nicht sinnvoll und sprengt die verfügbaren
|
|
Festplattenkapazität.
|
|
|
|
EOF
|
|
read -rp "Ist die Installation einer persistenten VM wirklich gewünscht? ja/nein " answer
|
|
if [[ "${answer,,}" == "ja" ]]; then
|
|
VM_DIR="${VM_DIR_PERSISTENT}"
|
|
echo "Die VM ${VM_NAME} wird persistent auf der Festplatte angelegt!"
|
|
sleep 5
|
|
else
|
|
PERSISTENT=0;
|
|
VM_DIR="${VM_DIR_CONF}"
|
|
echo "Die VM ${VM_NAME} wird nicht persistent gestartet!"
|
|
sleep 5
|
|
fi
|
|
}
|
|
|
|
QEMU='qemu:///session'
|
|
|
|
NEWCLONE=0
|
|
PERSISTENT=0
|
|
LIBVIRTOSINFO="win10"
|
|
LIBVIRTOPTS=""
|
|
NO_VIEWER=0
|
|
GUEST_UID=0
|
|
GUEST_GID=0
|
|
|
|
source /etc/lmn/vm.conf
|
|
VM_DIR_CONF="${VM_DIR}"
|
|
|
|
TEMP=$(getopt -o no:ps --long new,no-viewer,options:,persistent,system,memory:,data-disk:,heads:,cpu:,bridge:,macvtap,os:,uid:,gid:,help -n $0 -- "$@")
|
|
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
|
|
|
|
eval set -- "$TEMP"
|
|
|
|
while true; do
|
|
case "$1" in
|
|
-p | --persistent )
|
|
PERSISTENT=1;
|
|
VM_DIR="${VM_DIR_PERSISTENT}"
|
|
shift
|
|
;;
|
|
-n | --new )
|
|
NEWCLONE=1
|
|
shift
|
|
;;
|
|
-s | --system )
|
|
QEMU='qemu:///system'
|
|
shift
|
|
;;
|
|
-o | --options )
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} $2"
|
|
shift 2
|
|
;;
|
|
--no-viewer )
|
|
NO_VIEWER=1
|
|
shift
|
|
;;
|
|
--data-disk )
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --disk ${VM_DIR}/data.qcow2,size=$2,sparse=yes"
|
|
shift 2
|
|
;;
|
|
--heads )
|
|
for i in $(seq $2)
|
|
do
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --video model.heads=$i"
|
|
done
|
|
shift 2
|
|
;;
|
|
--memory )
|
|
mem_avail=$(sed -En "s/^MemAvailable:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
|
|
mem_avail=$((mem_avail / 1024 - 2048))
|
|
if (( $2 < mem_avail )); then
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --memory $2"
|
|
else
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --memory ${mem_avail}"
|
|
fi
|
|
shift 2
|
|
;;
|
|
--cpu )
|
|
#cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
|
|
cpu=$(lscpu | grep "^CPU(s):" | sed 's/.* //g')
|
|
if (( $2 < cpu )); then
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu $2"
|
|
else
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu ${cpu}"
|
|
fi
|
|
shift 2
|
|
;;
|
|
--bridge )
|
|
if ip link | grep $2; then
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --network=bridge=$2,model.type=virtio"
|
|
fi
|
|
shift 2
|
|
;;
|
|
--macvtap )
|
|
for interface in $(ip link | sed -En 's/.*(macvtap-.*)@.*/\1/p'); do
|
|
mac="$(ip link | grep -A1 "${interface}" | \
|
|
sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
|
|
type="ethernet,mac=${mac},target.dev=${interface},xpath1.set=./target/@managed=no,model.type=virtio"
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --network type=$type"
|
|
done
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --check mac_in_use=off"
|
|
shift
|
|
;;
|
|
--os )
|
|
LIBVIRTOSINFO=$2
|
|
shift 2
|
|
;;
|
|
--uid )
|
|
GUEST_UID=$2
|
|
shift 2
|
|
;;
|
|
--gid )
|
|
GUEST_GID=$2
|
|
shift 2
|
|
;;
|
|
--help )
|
|
show_help
|
|
exit 1
|
|
;;
|
|
-- ) shift; break ;;
|
|
* ) break ;;
|
|
esac
|
|
done
|
|
|
|
# if less than one arguments supplied, display usage
|
|
if [[ $# -ne 1 ]] ; then
|
|
show_help
|
|
exit 1
|
|
fi
|
|
|
|
VM_NAME=$1
|
|
|
|
systemctl --user restart usersquid.service &
|
|
|
|
# check, if persistent VM is really wanted
|
|
if [[ "${PERSISTENT}" == 1 ]] && [[ ! -f "${VM_DIR_PERSISTENT}/${VM_NAME}.qcow2" ]]; then
|
|
ask_really_persistent
|
|
fi
|
|
|
|
# because virsh has problems with long pathnames, using diffent configdir
|
|
export XDG_CONFIG_HOME="/var/tmp/vm/${UID}"
|
|
|
|
if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
|
|
echo "VM not yet running."
|
|
# only when school-network is reachable
|
|
if nslookup "${SEEDBOX_HOST}"; then
|
|
check_images
|
|
fi
|
|
if [[ "${NEWCLONE}" = 1 ]] || [[ ! -f "${VM_DIR}/${VM_NAME}-clone.qcow2" ]]; then
|
|
create_clone "${VM_NAME}"
|
|
fi
|
|
# delete the old vm
|
|
virsh --connect=qemu:///session undefine --nvram "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist"
|
|
#trap exit_script SIGHUP SIGINT SIGTERM
|
|
|
|
if id | grep -q teachers; then
|
|
NETHOME=/srv/samba/schools/default-school/teachers/$USER
|
|
else
|
|
NETHOME=(/srv/samba/schools/default-school/students/*/"$USER")
|
|
fi
|
|
if [[ "${HOME}" != "${NETHOME}" ]]; then
|
|
VMINFO_DIR="${HOME}"
|
|
else
|
|
VMINFO_DIR="/lmn/media/${USER}"
|
|
fi
|
|
create_printerlist
|
|
create_mountlist
|
|
|
|
# start virtiofsd-service
|
|
[[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd
|
|
|
|
# Create VMInfo Json file
|
|
#( umask 027; ./vm-create-vminfo > "${VMINFO_DIR}/.vminfo.json" )
|
|
# Start vminfo.timer
|
|
systemctl --user start vminfo.timer
|
|
|
|
uuid=$(openssl rand -hex 16)
|
|
uuid="${uuid:0:8}-${uuid:8:4}-${uuid:12:4}-${uuid:16:4}-${uuid:20:12}"
|
|
|
|
if [[ -f "${VM_DIR}/${VM_NAME}-clone.permall" ]]; then
|
|
mkdir -p "/var/tmp/vm/${UID}/.config/libvirt/qemu/swtpm/${uuid}/tpm2/"
|
|
ln "${VM_DIR}/${VM_NAME}-clone.permall" "/var/tmp/vm/${UID}/.config/libvirt/qemu/swtpm/${uuid}/tpm2/tpm2-00.permall"
|
|
LIBVIRTOPTS="${LIBVIRTOPTS} --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb "
|
|
fi
|
|
|
|
# finally, create the new vm
|
|
|
|
virt-install \
|
|
--uuid="${uuid}" \
|
|
--osinfo "${LIBVIRTOSINFO}" \
|
|
--name "${VM_NAME}-clone" \
|
|
--import \
|
|
--clock hpet_present=yes,hypervclock_present=yes \
|
|
--features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
|
|
--memorybacking source.type=memfd,access.mode=shared \
|
|
--disk "${VM_DIR}/${VM_NAME}-clone.qcow2",driver.discard=unmap,target.bus=scsi,cache=writeback \
|
|
--network=bridge=virbr0,model.type=virtio \
|
|
--controller type=scsi,model=virtio-scsi \
|
|
--check path_in_use=off \
|
|
--connect="${QEMU}" \
|
|
--noautoconsole \
|
|
--redirdev usb,type=spicevmc \
|
|
--redirdev usb,type=spicevmc \
|
|
--redirdev usb,type=spicevmc \
|
|
--redirdev usb,type=spicevmc \
|
|
--redirdev usb,type=spicevmc \
|
|
${LIBVIRTOPTS}
|
|
|
|
# --dry-run \
|
|
# --print-xml \
|
|
# > /tmp/vm.xml
|
|
# --features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
|
|
# --network type=ethernet,target.dev=vm-macvtap,xpath1.set=./target/@managed=no \
|
|
# virsh --connect="${QEMU}" start "${VM_NAME}-clone"
|
|
fi
|
|
if [[ $NO_VIEWER == 0 ]] ; then
|
|
echo "starting viewer"
|
|
trap exit_script SIGHUP SIGINT SIGTERM
|
|
virt-viewer --connect="${QEMU}" --full-screen "${VM_NAME}-clone"
|
|
fi
|