#!/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 --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-torrents and machine definition file sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${VM_NAME}.qcow2.torrent" [[ -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}" if [[ "${PERSISTENT}" -eq 1 ]]; then sudo /usr/local/bin/vm-link-images -p else sudo /usr/local/bin/vm-link-images fi # Create backing file cd "${VM_DIR}" qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-clone.qcow2" } create_printerlist() { ## Prepare .printerlist.csv mkdir -p "${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 } QEMU='qemu:///session' NEWCLONE=0 PERSISTENT=0 LIBVIRTOSINFO="win10" LIBVIRTOPTS="" NO_VIEWER=0 source /etc/lmn/vm.conf TEMP=$(getopt -o no:ps --long new,no-viewer,options:,persistent,system,memory:,data-disk:,heads:,cpu:,bridge:,macvtap,os:,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=$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 shift ;; --os ) LIBVIRTOSINFO=$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 # check, if we have to start squid if ! killall -s 0 squid; then echo "starting squid." /usr/sbin/squid -f /etc/squid/squid-usermode.conf fi # because virsh has problems with long pathnames, using diffent configdir export XDG_CONFIG_HOME="/tmp/${UID}/.config" if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then echo "VM not yet running." sudo /usr/local/bin/desktop-sync check_images 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 "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist" #trap exit_script SIGHUP SIGINT SIGTERM create_printerlist # start virtiofsd-service [[ "${QEMU}" = 'qemu:///session' ]] && sudo /usr/local/bin/vm-virtiofsd "${VM_NAME}" # finally, create the new vm virt-install \ --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 \ --filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=virtiofs,xpath1.set=./source/@socket="/run/user/${UID}/virtiofs-${VM_NAME}.sock" \ --controller type=scsi,model=virtio-scsi \ --check path_in_use=off \ --connect="${QEMU}" \ --noautoconsole \ ${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