2025-06-26-13-33-49: Cronjob

This commit is contained in:
lmn-client 2025-06-26 13:33:49 +02:00
commit 29049c8da1
127 changed files with 7089 additions and 0 deletions

View file

@ -0,0 +1,4 @@
---
vm_support: false
vm_torrent_serv: "seedbox.{{ domain }}"
vm_uploadseed_pwd: secret = "token:topsecret"

View file

@ -0,0 +1,14 @@
#!/usr/bin/bash
#
# Synchronize desktop starters
#
set -eu
source /etc/lmn/vm.conf
RSYNC_COMMAND=$(rsync -ai --delete --exclude=mimeinfo.cache \
--chown=root:root --chmod=F644,D755 "${DESKTOPSTARTERDIR}" \
/usr/local/share/applications/ | sed '/ \.\//d')
if [[ $? -eq 0 ]] && [[ -n "${RSYNC_COMMAND}" ]]; then
echo "${RSYNC_COMMAND}"
update-desktop-database /usr/local/share/applications
fi

25
roles/lmn_vm/files/lmn-vm Normal file
View file

@ -0,0 +1,25 @@
# vm-sync: Download and synchronize VM-Images and xml-Files
%role-teacher ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
%role-student ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
%examusers ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
# vm-aria2: Start/Stop aria2 as systemd-service for VM-Images
lmnsynci ALL=(root) NOPASSWD: /usr/local/bin/vm-aria2
# vm-link-images: Link VM-Images to User-tmp-directory
%examusers ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
%role-student ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
# vm-virtiofsd: Start Virtiofsd as systemd-service
%examusers ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
%role-student ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
# desktop-sync:
%examusers ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
%role-student ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
# vm-upload:
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-upload

View file

@ -0,0 +1,42 @@
#!/usr/bin/bash
#
# <umount>/usr/local/sbin/pam-umount.sh %(USER) %(USERUID) %(MNTPT)</umount>'
set -eu
usr="$1"
uid="$2"
mtp="$3"
slce="system-virtiofs.slice"
slp=false
shutdownVMs(){
local VM
for VM in $(sudo -u $usr XDG_RUNTIME_DIR="/run/user/$uid" \
XDG_CONFIG_HOME="/var/tmp/vm/$uid" \
XDG_CACHE_HOME="/var/cache/user/$uid/" \
virsh list --state-running | \
sed -nE "s/.*\s+(\S+)\s+running/\1/p") ; do
sudo -u $usr XDG_RUNTIME_DIR="/run/user/$uid" \
XDG_CONFIG_HOME="/var/tmp/vm/$uid" \
XDG_CACHE_HOME="/var/cache/user/$uid/" \
virsh destroy "$VM" 2>&1 | systemd-cat || true
slp=true
done
}
######################
## This is the first mount we need to get rid of:
if [[ "$mtp" =~ "/lmn/media/$usr/share" ]] && [[ -d "/run/user/$uid" ]] ; then
shutdownVMs
[[ "$slp" = true ]] && sleep 5 # leave some time to write caches …
sudo -u ${usr} killall gvfsd | systemd-cat
sudo -u ${usr} killall dbus-daemon | systemd-cat
systemctl -q is-active "$slce" && systemctl kill "$slce"
# debug to find processes blocking umount:
# lsof >> /var/log/lsof.log
fi
## Just umount:
exec umount "$mtp"

View file

@ -0,0 +1,2 @@
[Service]
Environment=HOME=/tmp/pulse.%u

158
roles/lmn_vm/files/sync-vm.sh Executable file
View file

@ -0,0 +1,158 @@
#!/usr/bin/bash
# Push VM-Disk-Image on server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") [-d] [-a] [-t] [vmnames]"
Images from vmnames-List will be synced from server. Default by torrent.
Using flag -d VMs will be synced by rsync
Using flag -a images from images.list and xml-directory will be synced from server.
Using flag -t all torrents and xml-VM-Definitions will be synced
EOF
}
download_image() {
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.qcow2" \
/lmn/vm/
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.xml" \
/lmn/vm/
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.qcow2.torrent" \
/lmn/vm/
/usr/local/bin/vmimage-torrent restart "${VM_NAME}.qcow2"
}
torrent_image() {
if [[ ! -f "/lmn/vm/${VM_NAME}.qcow2.torrent" ]]; then
echo "No torrent-File found"
exit 1
fi
lockfile="/tmp/sync-vm-${VM_NAME}.lock"
if ! flock -n "$lockfile" echo "try to acquire lock"; then
echo torrent seems to be in process.
echo waiting for completion ...
flock -w 3600 "$lockfile" echo "...completed"
sleep 5
else
(
if ! flock -n 200; then
echo "failed to acquire lock"
echo "Bitte noch einmal starten."
echo "Beliebige Taste zum Beenden."
read -n 1
exit 1
fi
torrent="${VM_NAME}.qcow2.torrent"
session="${torrent//./_}"
if vmimage-torrent status | grep -qw ^"$session"; then
vmimage-torrent stop "${VM_NAME}.qcow2"
fi
cd /lmn/vm
ctorrent -e 0 "${VM_NAME}.qcow2.torrent"
/usr/local/bin/vmimage-torrent restart "${VM_NAME}.qcow2"
if ! flock -u 200; then
echo failed to drop lock
exit 1
fi
) 200>"$lockfile"
fi
}
sync_all_images() {
rsync -av --files-from=/lmn/vm/images.list \
rsync://server:/vmimages-download/ /lmn/vm/
rsync -av rsync://server:/vmimages-download/*.xml \
/lmn/vm/
}
delete_old_qcows() {
cd /lmn/vm
for qcow2 in $(find . -maxdepth 1 -name "*.qcow2" -exec basename {} ';'); do
qcowsize=$(stat -c%s "${qcow2}")
if [[ -f "${qcow2}.size" ]] && [[ "${qcowsize}" != $(<"${qcow2}.size") ]]; then
torrent="${qcow2}.torrent"
session="${torrent//./_}"
if vmimage-torrent status | grep -qw ^"$session"; then
vmimage-torrent stop "${qcow2}"
fi
mv "${qcow2}" /tmp/
fi
done
}
sync_all_torrents() {
rsync -ai rsync://server:/vmimages-download/*.torrent /lmn/vm/
rsync -ai rsync://server:/vmimages-download/*.size /lmn/vm/
delete_old_qcows
rsync -ai rsync://server:/vmimages-download/*.xml /lmn/vm/
RSYNC_COMMAND=$(rsync -ai --delete --exclude=mimeinfo.cache rsync://server:/vmimages-download/desktop/ /usr/local/share/applications/ | sed '/ \.\//d')
if [[ $? -eq 0 ]] && [[ -n "${RSYNC_COMMAND}" ]]; then
echo "${RSYNC_COMMAND}"
update-desktop-database /usr/local/share/applications
fi
}
create_starter() {
if [[ ! -f "/usr/share/applications/VM_${VM_NAME}_starter.desktop" ]]; then
cat << EOF >"/usr/share/applications/VM_${VM_NAME}_starter.desktop"
[Desktop Entry]
Version=1.0
Type=Application
Name=VMstart: ${VM_NAME}
GenericName=VM starter ${VM_NAME}
Comment=Start VM ${VM_NAME}
#TryExec=konsole
Exec=/usr/local/bin/run-vm.sh ${VM_NAME}
Icon=clementine
Categories=VM;Engineering;
MimeType=image/vnd.dxf;
Keywords=design;VM;diagrams;graphics
Terminal=true
EOF
update-desktop-database /usr/share/applications
fi
}
if [[ "$(id -nu)" != "lmnsynci" ]]; then
echo "$(basename "$0") must be run as lmnsynci user"
show_help
exit 1
fi
while getopts ':dat' OPTION; do
case "$OPTION" in
d)
DOWNLOAD=1
;;
a)
sync_all_images
exit 0
;;
t)
sync_all_torrents
exit 0
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -lt 1 ]]; then
show_help
exit 1
fi
for VM_NAME in "$@"; do
if [[ -v "DOWNLOAD" ]]; then
echo "Downloading $VM_NAME"
download_image
else
echo "Torrenting $VM_NAME"
torrent_image
fi
done

View file

@ -0,0 +1,13 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Sync Starters
GenericName=Aktualisiert Info über vorhandene VMs
Comment=Sync VM Image information
#TryExec=konsole
Exec=if sudo /usr/local/bin/desktop-sync; then echo 'sync erfolgreich.\nFenster schließt sich in 3 Sekunden.'; sleep 3; else echo "Fehler - sollte nicht vorkommen."; read; fi
Icon=bittorrent-sync
Categories=fvs;
MimeType=image/vnd.dxf;
Keywords=design;VM;diagrams;graphics
Terminal=true

93
roles/lmn_vm/files/uploadseed Executable file
View file

@ -0,0 +1,93 @@
#!/usr/bin/python3
import os, sys
import subprocess
import xmlrpc.client as xc
import ssl
import argparse
parser = argparse.ArgumentParser(description='Upload a file to the bittorrent seeder.')
parser.add_argument('--server', required=True,
help="the server address and RPC port like 'IPaddress:port'")
parser.add_argument('--dht-port', required=True,
help='the DHT port the RPC server is listening on')
pwgrp = parser.add_mutually_exclusive_group(required=True)
pwgrp.add_argument('--passwd',
help='the RPC secret. Either this or --pwdfile needs to be ' \
'provided')
pwgrp.add_argument('--pwdfile',
help="file containing the RPC secret in the form " \
"'secret = \"token:SECRET\"'. " \
'Either this or --secret needs to be provided')
certgrp = parser.add_mutually_exclusive_group(required=True)
certgrp.add_argument('--no-cert', action='store_true',
help='do not use SSL certificate')
certgrp.add_argument('--cert', help='the certificate to use for verification')
parser.add_argument('FILE', help='the file to upload')
args = parser.parse_args()
rpcseeder = 'https://' + args.server + '/rpc'
dhtentry = args.server.split(':')[0] + ':' + args.dht_port
file2send = args.FILE
torrent = '/tmp/' + os.path.basename(file2send) + '.torrent'
if args.passwd:
secret = 'token:' + args.passwd
else:
exec(open(args.pwdfile).read())
ssl_ctx = ssl.create_default_context()
if args.no_cert:
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
print("Certificate verification disabled.")
elif args.cert is not None:
ssl_ctx.load_verify_locations(args.cert)
s = xc.ServerProxy(rpcseeder, context = ssl_ctx)
def make_torrent():
if os.path.isfile(torrent):
print("Torrent file", torrent, "exists already, please (re)move it.")
sys.exit(1)
subprocess.run(["/usr/bin/mktorrent", "-l 24", "-v", "-o", torrent, file2send], check=True)
h = subprocess.check_output(["/usr/bin/aria2c", "-S ", torrent])
for line in h.decode().splitlines():
if "Info Hash" in line:
return line.split(': ')[1]
def check_seeds(bthash):
active_seeds = s.aria2.tellActive(secret)
for seed in active_seeds:
f = seed['bittorrent']['info']['name']
gid = seed['gid']
ihash = seed['infoHash']
if f == os.path.basename(file2send):
print(file2send, "is already seeded with GID:", gid)
print("Info Hash is:", ihash)
if bthash == ihash:
print("The torrent file has not changed, exiting.")
return False
else:
print("The torrent file has changed, replacing torrent.")
s.aria2.remove(secret, gid)
return True
print("="*19, " Uploading new torrent with aria2 now. ", "="*19)
return True
def upload_torrent():
s.aria2.addTorrent(secret, xc.Binary(open(torrent, mode='rb').read()))
subprocess.run(["/usr/bin/aria2c",
"--dht-entry-point=" + dhtentry,
"--check-integrity",
"--dir=" + os.path.dirname(file2send),
torrent])
############################
if __name__ == '__main__':
infoHash = make_torrent()
if check_seeds(infoHash):
upload_torrent()
print("Upload finished.")

BIN
roles/lmn_vm/files/virtiofsd Executable file

Binary file not shown.

33
roles/lmn_vm/files/vm-aria2 Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/bash
set -eu
# if less than one arguments supplied, display usage
if [[ $# -ne 2 ]]; then
echo "This script takes as input the name of the VM " >&2
echo "Usage: $0 [start|stop] vm_name" >&2
exit 1
fi
COMMAND="$1"
VM_NAME="$2"
source /etc/lmn/vm.conf
if [[ "${COMMAND}" = "start" ]]; then
systemd-run --unit=aria2-"${VM_NAME}" \
--slice=system-aria2 \
--uid="$(id -u lmnsynci)" \
--gid="$(id -g lmnsynci)" \
--nice=19 \
--working-directory="${VM_SYSDIR}" \
--collect \
--property=Type=exec \
--property=SuccessExitStatus=1 \
aria2c --bt-hash-check-seed=true --check-integrity=true --seed-ratio=0.0 \
--dht-entry-point="${SEEDBOX_HOST}:${SEEDBOX_PORT}" \
--dht-file-path=$DHTDAT \
"${VM_SYSDIR}/${VM_NAME}.qcow2.torrent"
elif [[ "${COMMAND}" = "stop" ]] && systemctl is-active "aria2-${VM_NAME}.service"; then
systemctl stop "aria2-${VM_NAME}.service"
fi

47
roles/lmn_vm/files/vm-create Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/bash
# create 1st level-Clones
set -eu
source /etc/lmn/vm.conf
PERSISTENT=0
while getopts ':p' OPTION; do
case "$OPTION" in
p)
PERSISTENT=1
VM_DIR="${VM_DIR_PERSISTENT}"
;;
esac
done
shift "$((OPTIND -1))"
# if less than two arguments supplied, display usage
if [[ $# -ne 2 ]]; then
echo "This script takes as input the name of the VM to clone" >&2
echo "Usage: $0 vm_name_orig vm_name_clone" >&2
exit 1
fi
VM_NAME=$1
VM_CLONE=$2
# 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
# change to image-directory
cd "${VM_DIR}"
if [[ ! -f "${VM_NAME}.qcow2" ]]; then
echo "qcow2 File does not exists." >&2
exit 1
fi
qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-${VM_CLONE}.qcow2"
chmod a-w "${VM_NAME}-${VM_CLONE}.qcow2"

View file

@ -0,0 +1,28 @@
#!/usr/bin/bash
# link VM in User-Dir in /tmp or /var/vm
set -eu
source /etc/lmn/vm.conf
# change to image-directory
cd "${VM_SYSDIR}"
while getopts ':p' OPTION; do
case "$OPTION" in
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
esac
done
shift "$((OPTIND -1))"
# link system-VM-Images to User VM Directory
for i in *.qcow2; do
[[ -f "${VM_DIR}/${i}" ]] || ln "${i}" "${VM_DIR}/${i}"
done
# allow lmnsynci to remove old vm images
chgrp lmnsynci "${VM_DIR}"
chmod g+w "${VM_DIR}"

78
roles/lmn_vm/files/vm-netboot Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/bash
#
# Start a netboot VM connected to macvtap device and fraction of mem/cpus
#
set -eu
## Imporant for all virsh libvirt calls:
export XDG_CONFIG_HOME="/var/tmp/vm/${UID}"
menu=(standard "CLI Standard Debian GNU/Linux NFS"
standard-ram "CLI Standard Debian GNU/Linux RAM"
kde-desktop "KDE Plasma Desktop Debian GNU/Linux NFS"
gnome-desktop "Gnome Desktop Debian GNU/Linux NFS")
img=$(dialog --clear --backtitle "Virtual Machine Chooser" \
--title "Choose the Virtual Machine to Start" \
--menu "Start VM:" 12 70 6 "${menu[@]}" 2>&1 >/dev/tty)
## If the menu is canceled, $0 stops here because of set -e
# FIXME: Use first device found for now:
mac="$(ip link | grep -A1 -m1 "macvtap-" | \
sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
tapdev="$(ip link | grep -A1 -m1 "macvtap-" | sed -nE "s%^[1-9]:\s(\S+)@.*%\1%p")"
if [[ $# -eq 0 ]] ; then
mem=$(sed -En "s/^MemAvailable:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
arg=("--memory=$((mem/2048))" "--vcpu=$((cpu/2))")
echo Set options: "${arg[@]}"
else
arg=("$@")
fi
kernel="http://livebox/d-i/n-live/${img%-ram}/live/vmlinuz"
initrd="http://livebox/d-i/n-live/${img%-ram}/live/initrd.img"
kargs=(boot=live components splash locales=de_DE.UTF-8 keyboard-layouts=de
swap=true live-config.timezone=Europe/Berlin)
case "$img" in
standard*)
arg+=(--autoconsole=text)
kargs+=(console=ttyS0)
;;&
*-ram)
kargs+=("fetch=http://10.190.1.2/d-i/n-live/${img%-ram}/live/filesystem.squashfs")
;;
*)
kargs+=(netboot=nfs "nfsroot=10.190.1.2:/srv/nfs/debian-live/${img%-ram}")
;;
esac
type="ethernet,mac=${mac},target.dev=${tapdev},xpath1.set=./target/@managed=no"
n=0
for vm in $(virsh --connect qemu:///session list --all --name) ; do
if virsh domiflist "$vm" | grep -q "$mac" ; then
type="user"
virt-manager &
fi
if [[ "${img}$n" = "$vm" ]] ; then
n=$((n+1))
fi
done
kargs+=(hostname="${img}$n")
## FIXME: use passt, needs more settings for correct DNS/gateway
# type=user,xpath1.create=./backend,xpath2.set=./backend/@type=passt,xpath3.create=./ip,xpath4.set=./ip/@family=ipv4,xpath5.set=./ip/@address=172.16.1.1,xpath6.set=./ip/@prefix=24,xpath7.create=./portForward,xpath8.set=./portForward/@proto=tcp,xpath9.set=./portForward/range/@start=2001,xpath10.set=./portForward/range/@end=2500,xpath11.set=./portForward/range/@to=1
http_proxy='' \
exec virt-install \
--name "${img}$n" \
--osinfo debiantesting \
--nodisks --import --noreboot --transient \
--controller type=scsi,model=virtio-scsi \
--install kernel="$kernel",initrd="$initrd",kernel_args="${kargs[*]}" \
--network "type=$type" "${arg[@]}"
# --filesystem "$HOME",share
# mount -t 9p share /mnt

80
roles/lmn_vm/files/vm-rebase Executable file
View file

@ -0,0 +1,80 @@
#!/usr/bin/bash
# Rebase one level down
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") [-n newname] vmname"
This script takes as input the name of the VM to rebase one level down
-n new name of the rebased image
EOF
}
source /etc/lmn/vm.conf
while getopts ':n:p' OPTION; do
case "$OPTION" in
n)
NEWNAME=$OPTARG
;;
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less or more than one arguments supplied, display usage
if [[ $# -ne 1 ]]; then
show_help
exit 1
fi
# change to Images directory
cd "${VM_DIR}"
VM_NAME="$1"
# check if VM-Diskimage exists
if [[ ! -f "${VM_NAME}.qcow2" ]]; then
echo "File not found ${VM_NAME}.qcow2" >&2
exit 1
fi
# check if new VM-Diskimage exists
if [[ -v NEWNAME ]] && [[ -f "${NEWNAME}.qcow2" ]]; then
echo "New Base already exists: ${NEWNAME}.qcow2" >&2
exit 1
fi
NUMBASES=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep -c image)
NEWBASE=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep image | head -n 3 | tail -n 1 | cut -d' ' -f2)
CURRENTBASE=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep image | head -n 2 | tail -n 1 | cut -d' ' -f2)
if [[ ! "${NUMBASES}" -ge 3 ]]; then
echo "Image must have at least 2 backing-files" >&2
exit 1
fi
# check if base Diskimage exists
if [[ ! -f "${NEWBASE}" ]] || [[ ! -f "${CURRENTBASE}" ]]; then
echo "Backingfiles not found ${CURRENTBASE}, ${NEWBASE}" >&2
exit 1
fi
# rebasing disk image
qemu-img rebase -f qcow2 -b "${NEWBASE}" -F qcow2 "${VM_NAME}.qcow2"
if [[ -v NEWNAME ]]; then
NEWNAME="${NEWNAME}.qcow2"
else
rm -f "${CURRENTBASE}"
NEWNAME="${CURRENTBASE}"
fi
mv "${VM_NAME}.qcow2" "${NEWNAME}"
chmod a-w "${NEWNAME}"

375
roles/lmn_vm/files/vm-run Executable file
View file

@ -0,0 +1,375 @@
#!/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}"
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"
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() {
if id | grep -q teachers; then
NETHOME=/srv/samba/schools/default-school/teachers/$USER
else
NETHOME=(/srv/samba/schools/default-school/students/*/"$USER")
fi
NETHOME="${NETHOME#/srv/samba/schools}"
cat << EOF > "/lmn/media/${USER}/.mounts.csv"
Drive;Remotepath
H;\\\\10.190.1.1${NETHOME//\//\\}
T;\\\\10.190.1.1\default-school\share
EOF
echo "${USER}" > "/lmn/media/${USER}/.user"
}
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
socket="/run/user/$(id -u $USER)/virtiofs-${VM_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 "/lmn/media/${USER}" --syslog
}
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
create_printerlist
create_mountlist
# start virtiofsd-service
[[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd
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 \
--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 \
--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

178
roles/lmn_vm/files/vm-sync Executable file
View file

@ -0,0 +1,178 @@
#!/usr/bin/bash
# Push/Pull VM-Disk-Image and Infos from server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") command [args]"
command:
push_file
get_file
get_image
delete_outdated_image
update_usage_information
EOF
}
get_torrent() {
if [[ ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2.torrent" ]]; then
echo "No torrent-File found"
exit 1
fi
cd "${VM_SYSDIR}"
echo Size needed: $(get_image_size "${VM_NAME}.qcow2.torrent") " Bytes (VM-Image) + 5GB (spare)"
echo Size available: $(df --block-size=1 --output=avail "${VM_SYSDIR}" | sed 1d)
while [[ $(( $(get_image_size "${VM_NAME}.qcow2.torrent") + 15000000000 )) -gt $(df --block-size=1 --output=avail "${VM_SYSDIR}" | sed 1d) ]]; do
echo "Not enough space to get ${VM_NAME}."
FILENAME="$(head -1 vm_usage_information.txt)"
if [[ -z $FILENAME ]]; then
echo "No more old VM-files to delete. Unable to get ${VM_NAME}. Please contact system administrator."
exit 1
fi
echo "Deleting $FILENAME"
sudo vm-aria2 stop "$(basename "${FILENAME}" .qcow2)"
rm -f "${FILENAME}"
[[ -f "${VM_DIR}/${FILENAME}" ]] && rm -f "${VM_DIR}/${FILENAME}"
sed -i -e 1d vm_usage_information.txt
done
lockfile="/tmp/sync-vm-${VM_NAME}.lock"
if ! flock -n "$lockfile" echo "try to acquire lock"; then
echo torrent seems to be in process.
echo waiting for completion ...
flock -w 3600 "$lockfile" echo "...completed"
sleep 5
else
(
if ! flock -n 200; then
echo "failed to acquire lock"
echo "Bitte noch einmal starten."
echo "Beliebige Taste zum Beenden."
read -n 1
exit 1
fi
# stop aria2-seeding if running
sudo vm-aria2 stop "${VM_NAME}"
cd "${VM_SYSDIR}"
# get image
aria2c --seed-time=0 --dht-file-path="$DHTDAT" \
--dht-entry-point="${SEEDBOX_HOST}:${SEEDBOX_PORT}" \
"${VM_SYSDIR}/${VM_NAME}.qcow2.torrent"
# and seed
sudo vm-aria2 start "${VM_NAME}"
if ! flock -u 200; then
echo failed to drop lock
exit 1
fi
) 200>"$lockfile"
fi
}
get_image_size() {
torrentfile=$1
length=$(aria2c -S "${torrentfile}" | grep "Total Length" | \
sed -E -e 's/.*\(([0-9,]*)\)/\1/' -e 's/,//g')
echo "$length"
}
delete_outdated_image() {
cd "${VM_SYSDIR}"
qcowsize=$(stat -c%s "${FILENAME}")
if [[ -f "${FILENAME}.torrent" ]] && [[ "${qcowsize}" != $(get_image_size "${FILENAME}.torrent") ]]; then
cat << EOF >&2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Es wurde eine neueres VM-Image auf dem Server gefunden.
Das Herunterladen des Images via WLAN benötigt viel Zeit.
EOF
read -rp "Soll das neue Image wirklich herunter geladen werden? j/n " answer
if [[ "${answer,,}" == "j" ]]; then
sudo vm-aria2 stop "${FILENAME%.qcow2}"
rm -f "${FILENAME}"
fi
fi
}
update_usage_information() {
cd "${VM_SYSDIR}"
[[ -f vm_usage_information.txt ]] || ls -tr *.qcow2 > vm_usage_information.txt || touch vm_usage_information.txt
FILENAME="$(basename $FILENAME)"
echo sed -i "/${FILENAME}/d" vm_usage_information.txt
sed -i "/${FILENAME}/d" vm_usage_information.txt
echo "${FILENAME}" >> vm_usage_information.txt
}
get_file() {
cd "${VM_SYSDIR}"
curl --fail --noproxy "${SEEDBOX_HOST}" -o "${FILENAME}" \
"http://${SEEDBOX_HOST}/aria2/${FILENAME}" || echo "File not found on seedbox"
}
push_file() {
cd "${VM_SYSDIR}"
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${FILENAME}"
}
########################
if [[ "$(id -nu)" != "lmnsynci" ]]; then
echo "$(basename "$0") must be run as lmnsynci user"
show_help
exit 1
fi
source /etc/lmn/vm.conf
while getopts ':' OPTION; do
case "$OPTION" in
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -lt 1 ]]; then
show_help
exit 1
fi
command=$1
shift
case "$command" in
push_file)
for FILENAME in "$@"; do
push_file
done
;;
get_file)
for FILENAME in "$@"; do
get_file
done
;;
get_image)
for VM_NAME in "$@"; do
get_torrent
done
;;
delete_outdated_image)
for FILENAME in "$@"; do
delete_outdated_image
done
;;
update_usage_information)
for FILENAME in "$@"; do
update_usage_information
done
;;
*)
show_help
exit 1
;;
esac

68
roles/lmn_vm/files/vm-upload Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/bash
# Push VM-Disk-Image on server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") vmname"
Create torrent and upload disk on server.
EOF
}
upload_image() {
# check if VM-Diskimage exists
if [[ ! (-f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2") ]]; then
echo "File not found ${VM_NAME}.qcow2" >&2
exit 1
fi
vm-aria2 stop "${VM_NAME}" || echo "VMImage-torrent not running"
# link private VM-Diskimage to system-Dir
if [[ -f "${VM_DIR}/${VM_NAME}.qcow2" \
&& ( -f "${VM_SYSDIR}/${VM_NAME}.qcow2" && ("${VM_DIR}/${VM_NAME}.qcow2" -nt "${VM_SYSDIR}/${VM_NAME}.qcow2") \
|| ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2") ]]; then
echo "copy private VM-Diskimage to system-dir"
chown lmnsynci:lmnsynci "${VM_DIR}/${VM_NAME}.qcow2"
ln -f "${VM_DIR}/${VM_NAME}.qcow2" "${VM_SYSDIR}/${VM_NAME}.qcow2"
if [[ -f "${VM_DIR}/${VM_NAME}.permall" ]]; then
cp "${VM_DIR}/${VM_NAME}.permall" "${VM_SYSDIR}/${VM_NAME}.permall"
chown lmnsynci:lmnsynci "${VM_SYSDIR}/${VM_NAME}.permall"
fi
fi
cd "${VM_SYSDIR}"
if [[ -f "/tmp/${VM_NAME}.qcow2.torrent" ]]; then
rm -f "/tmp/${VM_NAME}.qcow2.torrent"
fi
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${VM_NAME}.qcow2"
if [[ -f "${VM_SYSDIR}/${VM_NAME}.permall" ]]; then
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${VM_NAME}.permall"
fi
}
source /etc/lmn/vm.conf
while getopts ':p' OPTION; do
case "$OPTION" in
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]] ; then
show_help
exit 1
fi
VM_NAME=$1
upload_image

50
roles/lmn_vm/files/vm-virtiofsd Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/bash
set -eu
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]]; then
echo "This script takes as input the name of the VM " >&2
echo "Usage: $0 vm_name" >&2
exit 1
fi
VM_NAME="$1"
## Make sure VMs can read the base directory:
chgrp 1010 "/lmn/media/${SUDO_USER}"
chmod 0775 "/lmn/media/${SUDO_USER}"
socket="/run/user/$(id -u $SUDO_USER)/virtiofs-${VM_NAME}.sock"
# FIXME: This does not work. In windows, there is no virtiofs device.
# In GNU/Linux it's only readable.
#
#if ! systemctl -q is-active virtiofs-${VM_NAME}.socket ; then
# systemd-run --unit=virtiofs-${VM_NAME} \
# --slice=system-virtiofs \
# --collect \
# --socket-property=ListenStream="$socket" \
# --socket-property=Accept=no \
# --socket-property=SocketMode=0700 \
# --socket-property=SocketUser=${SUDO_USER} \
# --property=Type=exec \
# --property=StandardInput=socket \
# /usr/local/bin/virtiofsd --log-level debug --sandbox none \
# --syslog --fd=0 --shared-dir "/lmn/media/${SUDO_USER}"
#else
# systemctl restart virtiofs-${VM_NAME}.socket
#fi
if [[ ! -S "$socket" ]] ; then
systemd-run --unit=virtiofs-${VM_NAME} \
--slice=system-virtiofs \
--collect \
--property=Type=exec \
--property=SuccessExitStatus=1 \
--property="ExecStopPost=rm $socket" \
/usr/local/bin/virtiofsd --socket-path "$socket" \
--shared-dir "/lmn/media/${SUDO_USER}"
fi
sleep 1
chown "${SUDO_USER}" "$socket"

View file

@ -0,0 +1,9 @@
- name: Reload libvirtd
ansible.builtin.systemd:
name: libvirtd.service
listen: reload libvirtd
- name: Just force systemd to reread configs
ansible.builtin.systemd_service:
daemon_reload: true
listen: daemon reload

239
roles/lmn_vm/tasks/main.yml Normal file
View file

@ -0,0 +1,239 @@
---
# FIXME #691138, better: prepare interfaces ready to use, c.f. down below, macvtap.
# This task needs to be run before the last apt run to provide a ready-to-use installation.
- name: Allow users to attach to bridge
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/94qemu-bridge-suid
mode: '0644'
content: |
## Modify permissions after installation/upgrade
## to run qemu-bridge as root
DPkg::Post-Invoke {"/usr/bin/chmod 4755 /usr/lib/qemu/qemu-bridge-helper || true"; };
- name: Install libvirt packages
ansible.builtin.apt:
name:
- aria2
- mktorrent
- libvirt-daemon-system
- virt-manager
- virt-viewer
- dialog # for vm-netboot menu
# - name: allow all users to use VMs
# lineinfile:
# dest: /etc/libvirt/libvirtd.conf
# line: 'auth_unix_rw = "none"'
# insertafter: '#auth_unix_rw = "polkit"'
# notify: reload libvirtd
- name: Configure pam_mount for VM bind mounts
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (bind mounts for VMs) -->"
block: |
<!-- bind mounts for the VMs, setting gid here does not work -->
<volume
path="~"
mountpoint="/lmn/media/%(USER)/home"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
<volume
path="/srv/samba/schools/default-school/share"
mountpoint="/lmn/media/%(USER)/share"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
<volume
path="/srv/samba/schools/default-school"
mountpoint="/lmn/media/%(USER)/school"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
insertafter: "<!-- END ANSIBLE MANAGED BLOCK .* -->"
- name: Use umount script for proper cleanup
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (umount script needed for bind mounts ordering) -->"
block: |
<!-- Provide special umount script here to handle bind mounts and proper ordering -->
<umount>/usr/local/sbin/pam-umount.sh %(USER) %(USERUID) %(MNTPT)</umount>
insertafter: '^<mntoptions.*'
- name: Prepare umount script
ansible.builtin.copy:
src: pam-umount.sh
dest: /usr/local/sbin/pam-umount.sh
mode: '0755'
- name: Insert domain in default-network
ansible.builtin.lineinfile:
path: /etc/libvirt/qemu/networks/default.xml
line: ' <domain name="{{ ansible_domain }}" localOnly="no"/>'
insertafter: '</ip>'
- name: Autostart default network for VMs
ansible.builtin.file:
src: /etc/libvirt/qemu/networks/default.xml
dest: /etc/libvirt/qemu/networks/autostart/default.xml
state: link
- name: Create system-user syncing VM-files and others
ansible.builtin.user:
name: lmnsynci
comment: lmn sync user
system: true
create_home: false
- name: Create /etc/lmn directory
ansible.builtin.file:
path: /etc/lmn
state: directory
mode: '0755'
- name: Create /lmn directory
ansible.builtin.file:
path: /lmn
state: directory
mode: '0755'
- name: Create /lmn/media directory
ansible.builtin.file:
path: /lmn/media
state: directory
mode: '1777'
- name: Create /var/vm directory
ansible.builtin.file:
path: /var/vm
state: directory
mode: '1777'
- name: Create vm directory
ansible.builtin.file:
path: /lmn/vm
state: directory
owner: lmnsynci
group: lmnsynci
mode: '0755'
- name: Create cleanup-vm.conf
ansible.builtin.copy:
dest: /etc/tmpfiles.d/clean-vm.conf
mode: '0644'
content: |
D /var/tmp/vm 1777 root root -
- name: Deploy sudo configurations
ansible.builtin.copy:
src: lmn-vm
dest: /etc/sudoers.d/90-lmn-vm
owner: root
group: root
mode: '0700'
- name: Deploy vmimages scripts
ansible.builtin.copy:
src: "{{ item }}"
dest: /usr/local/bin/
owner: root
group: root
mode: '0755'
loop:
- vm-create
- vm-rebase
- vm-run
- vm-upload
- vm-sync
- vm-link-images
- vm-virtiofsd
- virtiofsd
- vm-aria2
- uploadseed
- desktop-sync
- name: Deploy vm configuration file vm.conf
ansible.builtin.template:
src: vm.conf.j2
dest: /etc/lmn/vm.conf
owner: root
group: root
mode: '0644'
- name: Deploy aria2 RPC password file
ansible.builtin.copy:
dest: /etc/lmn/uploadseed.conf
owner: root
group: lmnsynci
mode: '0640'
content: |
{{ vm_uploadseed_pwd }}
- name: Prepare directory for aria2 dht.dat
ansible.builtin.file:
path: /var/cache/aria2/
state: directory
owner: lmnsynci
group: lmnsynci
mode: '0755'
- name: Prepare directory for qemu bridge config
ansible.builtin.file:
path: /etc/qemu/
state: directory
mode: '0755'
- name: Deploy bridge.conf needed for qemu session mode
ansible.builtin.copy:
dest: /etc/qemu/bridge.conf
mode: '0644'
content: |
allow virbr0
allow virbr1
allow virbr2
- name: Configure macvtap interface
ansible.builtin.copy:
dest: "/etc/NetworkManager/system-connections/macvlan-vm-macvtap-{{ item }}.nmconnection"
mode: '0600'
content: |
[connection]
id=macvlan-macvtap-{{ item[3:9] }}
type=macvlan
interface-name=macvtap-{{ item[3:9] }}
[macvlan]
mode=2
parent={{ item }}
tap=true
[ipv4]
method=disabled
[ipv6]
method=disabled
[proxy]
loop: "{{ ansible_interfaces | select('search', '^en[pso].+') }}"
- name: Adjust interface permissions for user mode VMs
ansible.builtin.copy:
dest: /etc/udev/rules.d/80-macvlan.rules
mode: '0644'
content: |
{% for interface in (ansible_interfaces | select('search', '^en[pso].+')) %}
SUBSYSTEMS=="net", KERNELS=="macvtap-{{ interface[3:9] }}", MODE="0666"
{% endfor %}
- name: Start virt-manager in session mode by default
ansible.builtin.copy:
dest: /usr/local/bin/virt-manager
content: |
#!/usr/bin/sh
exec /usr/bin/virt-manager --connect qemu:///session $@
mode: '0755'
- name: Copy vm-netboot script
ansible.builtin.copy:
src: vm-netboot
dest: /usr/local/bin/
mode: '0755'

View file

@ -0,0 +1,19 @@
# variables for LMN VM submodule
SEEDBOX_HOST="{{ vm_torrent_serv }}"
SEEDBOX_PORT=6789
SEEDBOX_RPC_PORT=6800
SEEDBOX_PWFILE="/etc/lmn/uploadseed.conf"
DHTDAT="/var/cache/aria2/dht.dat"
DESKTOPSTARTERDIR="/srv/samba/schools/default-school/share/school/AdminIT/desktop/"
VM_SYSDIR="/lmn/vm"
if [[ -v SUDO_UID ]]; then
VM_DIR="/var/tmp/vm/${SUDO_UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${SUDO_UID}"
else
VM_DIR="/var/tmp/vm/${UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${UID}"
fi
VM_MEDIADIR="/lmn/media/${USER}"