From 18928f281886cfe21022891c55716ebd7cc9059d Mon Sep 17 00:00:00 2001 From: "Andreas B. Mundt" Date: Tue, 31 Jan 2023 21:18:24 +0100 Subject: [PATCH] Implement VM configuration and deploy the images. --- lmn-desktop.yml | 4 +- roles/lmn_mount/tasks/main.yml | 2 +- roles/lmn_vm/files/create-clone.sh | 37 ++++++++ roles/lmn_vm/files/create-vm.sh | 34 +++++++ roles/lmn_vm/files/images.list | 5 + roles/lmn_vm/files/lmn-create-clone-sudo | 3 + roles/lmn_vm/files/lmn-mounthome-sudo | 3 + roles/lmn_vm/files/lmn-proxy.sh | 3 + roles/lmn_vm/files/mounthome.sh | 15 +++ roles/lmn_vm/files/pulseaudio-override.conf | 2 + roles/lmn_vm/files/rebase-vm.sh | 44 +++++++++ roles/lmn_vm/files/run-vm.sh | 67 +++++++++++++ roles/lmn_vm/tasks/main.yml | 94 +++++++++++++++++-- roles/lmn_vm/templates/squid-usermode.conf.j2 | 11 +++ 14 files changed, 315 insertions(+), 9 deletions(-) create mode 100755 roles/lmn_vm/files/create-clone.sh create mode 100755 roles/lmn_vm/files/create-vm.sh create mode 100644 roles/lmn_vm/files/images.list create mode 100644 roles/lmn_vm/files/lmn-create-clone-sudo create mode 100644 roles/lmn_vm/files/lmn-mounthome-sudo create mode 100755 roles/lmn_vm/files/lmn-proxy.sh create mode 100755 roles/lmn_vm/files/mounthome.sh create mode 100644 roles/lmn_vm/files/pulseaudio-override.conf create mode 100755 roles/lmn_vm/files/rebase-vm.sh create mode 100755 roles/lmn_vm/files/run-vm.sh create mode 100644 roles/lmn_vm/templates/squid-usermode.conf.j2 diff --git a/lmn-desktop.yml b/lmn-desktop.yml index cbad29a..9533177 100644 --- a/lmn-desktop.yml +++ b/lmn-desktop.yml @@ -14,7 +14,7 @@ when: "ansible_cmdline.adpw is not defined" vars: - domain: "pn.steinbeis.schule" + domain: "{{ ansible_domain }}" extra_pkgs: - vim - mc @@ -22,7 +22,7 @@ - console-setup - krb5-user - unattended-upgrades - extra_pkgs_bpo: [ linux-image-amd64 ] # [ libreoffice ] + extra_pkgs_bpo: [ linux-image-amd64 ] ansible_python_interpreter: "/usr/bin/python3" roles: diff --git a/roles/lmn_mount/tasks/main.yml b/roles/lmn_mount/tasks/main.yml index e71e674..ece685d 100644 --- a/roles/lmn_mount/tasks/main.yml +++ b/roles/lmn_mount/tasks/main.yml @@ -17,7 +17,7 @@ server="{{ smb_server }}" path="{{ smb_share }}" mountpoint="/media/%(DOMAIN_USER)/share" - options="sec=krb5i,cruid=%(USERUID),user=%(USER),gid=1001,file_mode=0770,dir_mode=0770" + options="sec=krb5i,cruid=%(USERUID),user=%(USER),gid=1010,file_mode=0770,dir_mode=0770" >rootansibleDebian-gdmsddmvirti insertafter: "" diff --git a/roles/lmn_vm/files/create-clone.sh b/roles/lmn_vm/files/create-clone.sh new file mode 100755 index 0000000..73ecfd2 --- /dev/null +++ b/roles/lmn_vm/files/create-clone.sh @@ -0,0 +1,37 @@ +#!/usr/bin/bash +# create VM clone + +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 to clone" + echo "Usage: $0 vm_name" + exit 1 +fi + +# change to image-directory +cd /var/lib/libvirt/images + +VM_NAME=$1 + +if [ ! -f "xml/${VM_NAME}.xml" -a -f "${VM_NAME}.gcow2" ]; then + echo "xml or qcow2 File does not exists." + exit 1 +fi + +qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-clone.qcow2" + +cp "xml/${VM_NAME}.xml" "xml/${VM_NAME}-clone.xml" + +# and actually rename the vm: (this also updates the storage path) +sed -i "s/${VM_NAME}/${VM_NAME}-clone/" "xml/${VM_NAME}-clone.xml" + +# set Username for mounting data from correct user +sed -i "s/USER/${SUDO_USER}/" "xml/${VM_NAME}-clone.xml" + +# delete the old vm +virsh --connect=qemu:///system undefine "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist" +# finally, create the new vm +virsh --connect=qemu:///system define "xml/${VM_NAME}-clone.xml" diff --git a/roles/lmn_vm/files/create-vm.sh b/roles/lmn_vm/files/create-vm.sh new file mode 100755 index 0000000..be187e2 --- /dev/null +++ b/roles/lmn_vm/files/create-vm.sh @@ -0,0 +1,34 @@ +#!/usr/bin/bash +# create 1st level-Clones + +set -eu + +# 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" + echo "Usage: $0 vm_name_orig vm_name_clone" + exit 1 +fi + +VM_NAME=$1 +VM_CLONE=$2 + +if [ ! -f "xml/$VM_NAME.xml" -a -f "$VM_NAME.gcow2" ]; then + echo "xml or qcow2 File does not exists." + 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 + +# virsh --connect=qemu:///system dumpxml $VM_NAME > xml/$VM_NAME_$VM_CLONE.xml +cp xml/$VM_NAME.xml xml/$VM_NAME-$VM_CLONE.xml + +# hardware addresses need to be removed, libvirt will assign +# new addresses automatically +sed -i /uuid/d xml/$VM_NAME-$VM_CLONE.xml +sed -i '/mac address/d' xml/$VM_NAME-$VM_CLONE.xml + +# and actually rename the vm: (this also updates the storage path) +sed -i s/$VM_NAME/$VM_NAME-$VM_CLONE/ xml/$VM_NAME-$VM_CLONE.xml diff --git a/roles/lmn_vm/files/images.list b/roles/lmn_vm/files/images.list new file mode 100644 index 0000000..1129e07 --- /dev/null +++ b/roles/lmn_vm/files/images.list @@ -0,0 +1,5 @@ +win10.qcow2 +win10-SolidWorks.qcow2 +win10-Elektro.qcow2 +deb11.qcow2 +deb11-virtualbox.qcow2 diff --git a/roles/lmn_vm/files/lmn-create-clone-sudo b/roles/lmn_vm/files/lmn-create-clone-sudo new file mode 100644 index 0000000..7c79a2b --- /dev/null +++ b/roles/lmn_vm/files/lmn-create-clone-sudo @@ -0,0 +1,3 @@ +%examusers ALL=(root) NOPASSWD: /usr/local/bin/create-clone.sh +%role-student ALL=(root) NOPASSWD: /usr/local/bin/create-clone.sh +%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/create-clone.sh diff --git a/roles/lmn_vm/files/lmn-mounthome-sudo b/roles/lmn_vm/files/lmn-mounthome-sudo new file mode 100644 index 0000000..f60cc5b --- /dev/null +++ b/roles/lmn_vm/files/lmn-mounthome-sudo @@ -0,0 +1,3 @@ +%examusers ALL=(root) NOPASSWD: /usr/local/bin/mounthome.sh +%role-student ALL=(root) NOPASSWD: /usr/local/bin/mounthome.sh +%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/mounthome.sh diff --git a/roles/lmn_vm/files/lmn-proxy.sh b/roles/lmn_vm/files/lmn-proxy.sh new file mode 100755 index 0000000..122a10d --- /dev/null +++ b/roles/lmn_vm/files/lmn-proxy.sh @@ -0,0 +1,3 @@ +export http_proxy='http://firewall:3128' +export https_proxy=$http_proxy +export ftp_proxy=$http_proxy diff --git a/roles/lmn_vm/files/mounthome.sh b/roles/lmn_vm/files/mounthome.sh new file mode 100755 index 0000000..ea3c2d8 --- /dev/null +++ b/roles/lmn_vm/files/mounthome.sh @@ -0,0 +1,15 @@ +#!/usr/bin/bash +set -eu + +share="$(getent passwd "$SUDO_UID" | cut -d : -f 6 | sed 's/.*default-school//')" + +if [ "$#" -gt 0 ] && [ "$1" = '-u' ]; then + umount "/media/${SUDO_USER}/home" && rmdir "/media/${SUDO_USER}/home" +else + chgrp 1010 "/media/${SUDO_USER}" + chmod 0770 "/media/${SUDO_USER}" + mkdir -p "/media/${SUDO_USER}/home" + mount -t cifs -o "sec=krb5i,cruid=${SUDO_UID},username=${SUDO_USER},uid=${SUDO_UID},\ +gid=1010,file_mode=0770,dir_mode=0770,forceuid,forcegid,user=${SUDO_USER}" \ + "//server/default-school/${share}" "/media/${SUDO_USER}/home" +fi diff --git a/roles/lmn_vm/files/pulseaudio-override.conf b/roles/lmn_vm/files/pulseaudio-override.conf new file mode 100644 index 0000000..5d7783e --- /dev/null +++ b/roles/lmn_vm/files/pulseaudio-override.conf @@ -0,0 +1,2 @@ +[Service] +Environment=HOME=/tmp/pulse.%u diff --git a/roles/lmn_vm/files/rebase-vm.sh b/roles/lmn_vm/files/rebase-vm.sh new file mode 100755 index 0000000..184f615 --- /dev/null +++ b/roles/lmn_vm/files/rebase-vm.sh @@ -0,0 +1,44 @@ +#!/usr/bin/bash + +# Rebase one level down + +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 to rebase one level down" + echo "Usage: $0 vm_name" + exit 1 +fi + +VM_NAME=$1 + +if [ ! -f $VM_NAME.qcow2 ] +then + echo "File not found $VM_NAME.qcow2" + exit 1 +fi + +shopt -s lastpipe +qemu-img info --backing-chain $VM_NAME.qcow2 | grep image | wc -l | read NUMBASES +qemu-img info --backing-chain $VM_NAME.qcow2 | grep image | head -n 3 | tail -n 1 | cut -d' ' -f2 | read NEWBASE +qemu-img info --backing-chain $VM_NAME.qcow2 | grep image | head -n 2 | tail -n 1 | cut -d' ' -f2 | read CURRENTBASE + +if [ ! $NUMBASES -ge 3 ] +then + echo "Image must have at least 2 backing-files" + exit 1 +fi + +if [ ! -f $NEWBASE -a -f $CURRENTBASE ] +then + echo "Backingfiles not found $CURRENTBASE, $NEWBASE" + exit 1 +fi + +qemu-img rebase -f qcow2 -b $NEWBASE -F qcow2 $VM_NAME.qcow2 +rm $CURRENTBASE +mv $VM_NAME.qcow2 $CURRENTBASE +chmod a-w $CURRENTBASE +qemu-img create -f qcow2 -F qcow2 -b $CURRENTBASE $VM_NAME.qcow2 diff --git a/roles/lmn_vm/files/run-vm.sh b/roles/lmn_vm/files/run-vm.sh new file mode 100755 index 0000000..cba0de4 --- /dev/null +++ b/roles/lmn_vm/files/run-vm.sh @@ -0,0 +1,67 @@ +#!/usr/bin/bash +# create and run clone + +set -eu + +NEWCLONE=0 + +show_help() { + cat << EOF +Usage: $(basename "$0") [-n] vmname" +Create a new clone, start the vm (if not yet running) and run virt-viewer. +Squid-Proxy will be started too. +User Home will be mounted on /media/USERNAME/home + -n new clone will be created, even if exists +EOF +} + +NEWCLONE=0 + +while getopts ':n' OPTION; do + case "$OPTION" in + n) + NEWCLONE=1 + ;; + ?) + 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 + +if [[ ! -f "/var/lib/libvirt/images/${VM_NAME}.qcow2" ]]; then + echo "no base VM disk '${VM_NAME}.qcow2' found" + exit 1 +fi + +# check, if we have to start squid +if [[ ! -f "/tmp/squid.pid" ]]; then + echo "starting squid." + /usr/sbin/squid -f /etc/squid/squid-usermode.conf +fi + +# check, if we have to mount home +if [[ ! -d "/media/${USER}/home" ]]; then + echo "mounting home." + sudo mounthome.sh +fi + +if ! virsh --connect=qemu:///system list | grep "${VM_NAME}"; then + echo "VM not yet running. Try to clone and start." + if [[ "${NEWCLONE}" = 1 ]] || [[ ! -f "/var/lib/libvirt/images/${VM_NAME}-clone.qcow2" ]]; then + sudo create-clone.sh "${VM_NAME}" + fi + virsh --connect=qemu:///system start "${VM_NAME}-clone" +fi +echo "starting viewer" +virt-viewer --connect=qemu:///system --full-screen "${VM_NAME}-clone" diff --git a/roles/lmn_vm/tasks/main.yml b/roles/lmn_vm/tasks/main.yml index 5b1df6e..6224e6b 100644 --- a/roles/lmn_vm/tasks/main.yml +++ b/roles/lmn_vm/tasks/main.yml @@ -18,6 +18,19 @@ autoremove: true when: ansible_distribution_release == 'bookworm' +- 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: autostart default network for VMs + file: + src: /etc/libvirt/qemu/networks/default.xml + dest: /etc/libvirt/qemu/networks/autostart/default.xml + state: link + - name: install squid apt: name: @@ -25,9 +38,78 @@ state: latest autoremove: true -- 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: disable squid + systemd: + name: squid + enabled: false + state: stopped + +- name: deploy squid user mode configuration + template: + src: squid-usermode.conf.j2 + dest: /etc/squid/squid-usermode.conf + mode: '0644' + +- name: deploy sudo configuration + copy: + src: lmn-mounthome-sudo + dest: /etc/sudoers.d/90-lmn-mounthome + owner: root + group: root + mode: '0700' + +- name: deploy sudo configuration + copy: + src: lmn-create-clone-sudo + dest: /etc/sudoers.d/90-lmn-create-clone + owner: root + group: root + mode: '0700' + +- name: deploy mount home script + copy: + src: "{{ item }}" + dest: /usr/local/bin/ + owner: root + group: root + mode: '0755' + loop: + - mounthome.sh + - create-vm.sh + - rebase-vm.sh + - create-clone.sh + - run-vm.sh + +- name: deploy http proxy config + copy: + src: lmn-proxy.sh + dest: /etc/profile.d/ + mode: '0644' + +- name: deploy pulseaudio fix + file: + path: /etc/systemd/user/pulseaudio.service.d/ + state: directory + +- name: deploy pulseaudio fix + copy: + src: pulseaudio-override.conf + dest: /etc/systemd/user/pulseaudio.service.d/override.conf + +#### VMs +- name: deploy initial image list + copy: + src: images.list + dest: /var/lib/libvirt/images/images.list + force: false + +- name: rsync VM image definitions + command: rsync -a --itemize-changes rsync://server:/vmimages-download/xml /var/lib/libvirt/images/ + register: result + changed_when: result.stdout | length > 0 + +- name: rsync VM images + command: rsync -a -i --files-from=/var/lib/libvirt/images/images.list rsync://server:/vmimages-download/ /var/lib/libvirt/images/ + register: result + changed_when: result.stdout | length > 0 + when: (ansible_mounts | selectattr("mount", "equalto", "/") | list)[0].size_available > 80000000000 diff --git a/roles/lmn_vm/templates/squid-usermode.conf.j2 b/roles/lmn_vm/templates/squid-usermode.conf.j2 new file mode 100644 index 0000000..1cf931a --- /dev/null +++ b/roles/lmn_vm/templates/squid-usermode.conf.j2 @@ -0,0 +1,11 @@ +acl local-servers dstdomain .{{ domain }} +cache_peer firewall parent 3128 0 no-query default login=NEGOTIATE auth-no-keytab +never_direct deny local-servers +never_direct allow all +#access_log stdio:/tmp/access.log squid +access_log none +cache_log /dev/null +logfile_rotate 0 +pid_filename /tmp/squid.pid +http_port 192.168.122.1:3128 +http_access allow all