From 3d715bae35887f97383daad57c4a3c71fbe04342 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Thu, 10 Jun 2021 11:09:44 +0200 Subject: [PATCH] kvm-host: replace the "kvm-tools" package with scripts deployed by Ansible --- CHANGELOG.md | 1 + kvm-host/defaults/main.yml | 1 + kvm-host/files/add-vm.sh | 221 ++++++++++++++++++++++++++++++++++++ kvm-host/files/kvmstats.sh | 94 +++++++++++++++ kvm-host/tasks/munin.yml | 35 +++++- kvm-host/tasks/packages.yml | 2 +- kvm-host/tasks/ssh.yml | 2 +- kvm-host/tasks/tools.yml | 49 +++++++- 8 files changed, 394 insertions(+), 11 deletions(-) create mode 100755 kvm-host/files/add-vm.sh create mode 100755 kvm-host/files/kvmstats.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 23f3ed03..710a4093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The **patch** part changes incrementally at each release. * apt: store keys in /etc/apt/trusted.gpg.d in ascii format * certbot: sync_remote.sh is configurable * evolinux-base: copy GPG key instead of using apt-key +* kvm-host: replace the "kvm-tools" package with scripts deployed by Ansible * nodejs: change GPG key name * ntpd: Add leapfile configuration setting to ntpd on debian 10+ * packweb-apache: install phpMyAdmin from buster-backports diff --git a/kvm-host/defaults/main.yml b/kvm-host/defaults/main.yml index bb97c0f9..bec20a12 100644 --- a/kvm-host/defaults/main.yml +++ b/kvm-host/defaults/main.yml @@ -1,3 +1,4 @@ --- kvm_custom_libvirt_images_path: '' kvm_install_drbd: True +kvm_scripts_dir: /usr/local/sbin \ No newline at end of file diff --git a/kvm-host/files/add-vm.sh b/kvm-host/files/add-vm.sh new file mode 100755 index 00000000..3186db43 --- /dev/null +++ b/kvm-host/files/add-vm.sh @@ -0,0 +1,221 @@ +#!/bin/bash +# Add-VM script to add a VM on evoKVM. +# _ ____ ____ __ ____ __ +# / \ | _ \| _ \ \ \ / / \/ | +# / _ \ | | | | | | |____\ \ / /| |\/| | +# / ___ \| |_| | |_| |_____\ V / | | | | +# /_/ \_\____/|____/ \_/ |_| |_| +# +# Need packages: dialog +# Bash strict mode +set -euo pipefail + +dryRun() { + + if ($doDryRun); then + echo -e "\e[34mDoing:" $* "\e[39m" + else + echo -e "\e[34mDoing:" $* "\e[39m" + $* + fi +} + +critical() { + echo -ne "\e[31m${1}\e[39m\n" && exit 1 +} + +warn() { + + echo -ne "\e[33m${1}\e[39m\n" +} + +[ -f "/etc/evolinux/add-vm.cnf" ] && . /etc/evolinux/add-vm.cnf +masterKVMIP="${masterKVMIP:-127.0.0.1}" +slaveKVMIP="${slaveKVMIP:-}" +disks="${disks:-}" +[ -n "${disks}" ] || disks=("ssd" "hdd") +bridgeName="${bridgeName:-br0}" +doDryRun=${doDryRun:-false} +isoImagePath="${isoImagePath:-}" + +export DIALOGOUT=$(mktemp --tmpdir=/tmp addvm.XXX) +# TODO: How to replace _ with a space?? +export DIALOG="$(which dialog) --backtitle Add-VM_Press_F1_for_help" +export DIALOGRC=.dialogrc +export HELPFILE=$(mktemp --tmpdir=/tmp addvm.XXX) +tmpResFile=$(mktemp --tmpdir=/tmp addvm.XXX) +xmlVM=$(mktemp --tmpdir=/tmp addvm.XXX) +masterKVM="$(hostname -s)" +slaveKVM="$(ssh $slaveKVMIP hostname -s)" + +# Exit & Cleanup function. +clean() { + + echo -e "\nBye! Cleaning..." + [ -f $DIALOGOUT ] && rm $DIALOGOUT + [ -f $HELPFILE ] && rm $HELPFILE +# [ -f $tmpResFile ] && rm $tmpResFile +# [ -f $xmlVM ] && rm $xmlVM + exit +} +trap clean EXIT SIGINT + +$DIALOG --hfile $HELPFILE --title "KVM Config" --form "Set the right config. "\ +"If you do not want a type of disk, type none." 0 0 0 \ + "vCPU" 1 1 "2" 1 10 20 0 \ + "memory" 2 1 "4G" 2 10 20 0 \ + "volroot" 3 1 "${disks[0]}-20G" 3 10 20 0 \ + "volhome" 4 1 "${disks[1]}-40G" 4 10 20 0 \ + "vmName" 5 1 "" 5 10 20 0 \ + 2>$DIALOGOUT +vCPU=$(sed 1'q;d' $DIALOGOUT) +memory=$(sed 2'q;d' $DIALOGOUT|tr -d 'G') +memory=$(($memory * 1024)) +volroot=$(sed 3'q;d' $DIALOGOUT) +volhome=$(sed 4'q;d' $DIALOGOUT) +vmName=$(sed 5'q;d' $DIALOGOUT) + +[ -z "$vmName" ] && critical "You need a VM Name!!" + +$DIALOG --title "Continue?" --clear "$@" \ + --yesno "Will create a VM named $vmName on $masterKVM with $vCPU vCPU, "\ +"$memory memory, $volroot for / (and /usr, ...) and $volhome for /home." 10 80 +if [[ $? -ne 0 ]]; then + exit 1 +fi + +if ! [[ "$volroot" =~ ([^-]+)-([0-9]+G) ]]; then + critical "No volume for root device (/dev/vda)?!!" +else + volrootDisk="${BASH_REMATCH[1]}" + volrootSize="${BASH_REMATCH[2]}" + [[ " ${disks[*]} " == *"$volrootDisk"* ]] || critical "Unknow disk $volrootDisk !" + dryRun lvcreate -L$volrootSize -n${vmName}_root $volrootDisk + dryRun ssh $slaveKVMIP lvcreate -L$volrootSize -n${vmName}_root $volrootDisk +fi + +if ! [[ "$volhome" =~ ([^-]+)-([0-9]+G) ]]; then + warn "No volume for home device (/dev/vdb)... Okay, not doing it!" + volhomeDisk="none" +else + volhomeDisk="${BASH_REMATCH[1]}" + volhomeSize="${BASH_REMATCH[2]}" + [[ " ${disks[*]} " == *"$volhomeDisk"* ]] || critical "Unknow disk $volhomeDisk !" + dryRun lvcreate -L$volhomeSize -n${vmName}_home $volhomeDisk + dryRun ssh $slaveKVMIP lvcreate -L$volhomeSize -n${vmName}_home $volhomeDisk +fi + +if [[ -f "/etc/drbd.d/${vmName}.res" ]]; then + warn "The DRBD resource file ${vmName}.res is already present! Continue? [y/N]" + read + if ! [[ "$REPLY" =~ (Y|y) ]]; then + exit 1 + fi +fi + +# Generates drbd resource file. + +if [ $(ls /etc/drbd.d/|wc -l) -gt 1 ]; then + lastdrbdPort=$(grep -hEo ':[0-9]{4}' /etc/drbd.d/*.res | sort | uniq | tail -1 | sed 's/://') + drbdPort=$((lastdrbdPort+1)) + lastMinor=$(grep -hEo 'minor [0-9]{1,}' /etc/drbd.d/*.res | sed 's/minor //' | sort -n | tail -1) + minorvol0=$((lastMinor+1)) + minorvol1=$((lastMinor+2)) +else + drbdPort=7900 + minorvol0=0 + minorvol1=1 +fi + +cat << EOT > $tmpResFile +resource "${vmName}" { + net { + cram-hmac-alg "sha1"; + shared-secret "$(apg -n 1 -m 16 -M lcN)"; + # Si pas de lien dediƩ 10G, passer en protocol A + # Et desactiver allow-two-primaries; + protocol C; + allow-two-primaries; + # Tuning perf. + max-buffers 8000; + max-epoch-size 8000; + sndbuf-size 0; + } + # A utiliser si RAID HW avec cache + batterie + disk { + disk-barrier no; + disk-flushes no; + } + volume 0 { + device minor ${minorvol0}; + disk /dev/${volrootDisk}/${vmName}_root; + meta-disk internal; + } +EOT +if [[ "$volhomeDisk" != "none" ]]; then + cat << EOT >> $tmpResFile + volume 1 { + device minor ${minorvol1}; + disk /dev/${volhomeDisk}/${vmName}_home; + meta-disk internal; + } +EOT +fi +cat << EOT >> $tmpResFile + on $masterKVM { + address ${masterKVMIP}:${drbdPort}; + } + on $slaveKVM { + address ${slaveKVMIP}:${drbdPort}; + } +} +EOT + +# Create/Activate the new drbd resources. +drbdadm="$(command -v drbdadm)" +($doDryRun) && drbdadm="${drbdadm} --dry-run" + +($doDryRun) && trap "rm /etc/drbd.d/${vmName}.res && ssh ${slaveKVMIP} rm /etc/drbd.d/${vmName}.res" 0 +install -m 600 $tmpResFile /etc/drbd.d/${vmName}.res +scp /etc/drbd.d/${vmName}.res ${slaveKVMIP}:/etc/drbd.d/ +${drbdadm} create-md "$vmName" +ssh $slaveKVMIP ${drbdadm} create-md "$vmName" +${drbdadm} adjust "$vmName" +ssh $slaveKVMIP ${drbdadm} adjust "$vmName" +${drbdadm} -- --overwrite-data-of-peer primary "$vmName" + +if !($doDryRun); then + sleep 5 && drbd-overview | tail -4 + + drbdDiskPath="/dev/drbd/by-res/${vmName}/0" + if ! [[ -b "$drbdDiskPath" ]]; then + warn "$drbdDiskPath not found! Continue? [y/N]" + read + if ! [[ "$REPLY" =~ (Y|y) ]]; then + exit 1 + fi + fi +fi + +virtHome="" +[ "$volhomeDisk" != "none" ] && virtHome="--disk path=/dev/drbd/by-disk/${volhomeDisk}/${vmName}_home,bus=virtio,io=threads,cache=none,format=raw" +bootMode="--pxe" +[ -f "$isoImagePath" ] && bootMode="--cdrom=$isoImagePath" + +dryRun virt-install --connect=qemu:///system \ + --name=${vmName} \ + --cpu mode=host-passthrough --vcpus=${vCPU} \ + --memory=${memory} \ + --disk path=/dev/drbd/by-disk/${volrootDisk}/${vmName}_root,bus=virtio,io=threads,cache=none,format=raw \ + $virtHome \ + $bootMode \ + --network=bridge:${bridgeName},model=virtio \ + --noautoconsole --graphics vnc,listen=127.0.0.1,keymap=fr \ + --rng /dev/random \ + --os-variant=none + +if [ -x /usr/share/scripts/evomaintenance.sh ]; then + ($doDryRun) || echo "Install VM ${vmName} (add-vm.sh)" | /usr/share/scripts/evomaintenance.sh +fi + +echo -e "\e[32mDone! Now you can install your VM with virt-manager.\e[39m" diff --git a/kvm-host/files/kvmstats.sh b/kvm-host/files/kvmstats.sh new file mode 100755 index 00000000..588ef863 --- /dev/null +++ b/kvm-host/files/kvmstats.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +# NOTE: kvmstats relies on the hxselect(1) command to parse virsh' xml +# files. On Debian, this command is provided by the 'html-xml-utils' +# package. + +set -e -u + +usage () { + echo 'usage: kvmstats.sh [-a] [-u K|M|G]' + exit 1 +} + +for DEP in hxselect lvs tempfile bc +do + if [ -z "$(which $DEP)" ] + then + echo "kvmstats.sh: $DEP not found in \$PATH" 1>&2 + exit 1 + fi +done + +POW=$(echo 1024 ^ 3 | bc) +while [ $# -ne 0 ] && echo "$1" | grep -q '^-[[:alnum:]]' +do + case $1 in + '-u') + case $2 in + 'K') + POW=$(echo 1024 ^ 1 | bc) + ;; + 'M') + POW=$(echo 1024 ^ 2 | bc) + ;; + 'G') + POW=$(echo 1024 ^ 3 | bc) + ;; + *) + usage + esac + ;; + '-a') + SHOW_AVAIL=y + ;; + *) + usage + esac + shift +done + +# since libvirt seems to store memoy in KiB, POW must be lowered by 1 +POW=$((POW / 1024)) + +TMPFILE=$(tempfile -s kvmstats) +LVSOUT=$(tempfile -s kvmstats) + +lvs --units b --nosuffix >"$LVSOUT" + +for VM in $(virsh list --all --name) +do + VCPU=$(hxselect -c 'domain vcpu' "$TMPFILE" + +( + echo vm vcpu ram disk running + cat "$TMPFILE" + awk '/yes$/ { vcpu += $2; ram += $3; disk += $4; running++ } END { print "TOTAL(running)", vcpu, ram, disk, running }' <"$TMPFILE" + if [ $SHOW_AVAIL ] + then + AV_CPU=$(awk '/^processor/ { cpu++ } END { print cpu }' /proc/cpuinfo) + AV_MEM=$(awk '/^MemTotal:/ { print int($2 / 1024 ^ 2) }' /proc/meminfo) + echo AVAILABLE "$AV_CPU" "$AV_MEM" + fi +) | column -t + +rm "$TMPFILE" "$LVSOUT" diff --git a/kvm-host/tasks/munin.yml b/kvm-host/tasks/munin.yml index dc130238..400dcd49 100644 --- a/kvm-host/tasks/munin.yml +++ b/kvm-host/tasks/munin.yml @@ -1,14 +1,41 @@ --- +- include_role: + name: remount-usr + +- name: Create local munin directory + file: + name: /usr/local/share/munin/ + state: directory + mode: "0755" + +- name: Create plugin directory + file: + name: /usr/local/share/munin/plugins/ + state: directory + mode: "0755" + - name: Get Munin plugins get_url: url: "https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/libvirt/{{ item }}" - dest: "/etc/munin/plugins/" + dest: "/usr/local/share/munin/plugins/" mode: "0755" + force: no loop: - - kvm_cpu - - kvm_io - - kvm_mem + - kvm_cpu + - kvm_io + - kvm_mem + notify: restart munin-node + +- name: Enable redis munin plugin + file: + src: "/usr/local/share/munin/plugins/{{item}}" + dest: "/etc/munin/plugins/{{item}}" + state: link + loop: + - kvm_cpu + - kvm_io + - kvm_mem notify: restart munin-node - name: Copy Munin plugins conf diff --git a/kvm-host/tasks/packages.yml b/kvm-host/tasks/packages.yml index 99790e84..d2a540e5 100644 --- a/kvm-host/tasks/packages.yml +++ b/kvm-host/tasks/packages.yml @@ -9,5 +9,5 @@ - virtinst - libvirt-daemon-system - libvirt-clients - - kvm-tools - vlan + state: present diff --git a/kvm-host/tasks/ssh.yml b/kvm-host/tasks/ssh.yml index 1b0f7915..fe71c287 100644 --- a/kvm-host/tasks/ssh.yml +++ b/kvm-host/tasks/ssh.yml @@ -44,7 +44,7 @@ state: present special_time: "daily" user: root - job: "virsh list | ssh {{ hostvars[item]['ansible_hostname'] }} 'cat >/root/libvirt-{{ inventory_hostname }}/virsh-list.txt'" + job: "virsh list --all | ssh {{ hostvars[item]['ansible_hostname'] }} 'cat >/root/libvirt-{{ inventory_hostname }}/virsh-list.txt'" loop: - "{{ groups['hypervisors'] }}" when: item != inventory_hostname diff --git a/kvm-host/tasks/tools.yml b/kvm-host/tasks/tools.yml index b3a4be0f..56caa6ea 100644 --- a/kvm-host/tasks/tools.yml +++ b/kvm-host/tasks/tools.yml @@ -1,13 +1,52 @@ --- +- name: remove old package + apt: + name: kvm-tools + purge: yes + state: absent + - include_role: name: remount-usr + when: kvm_scripts_dir is search ("/usr") -- name: migrate-vm scripts is present +- name: add-vm script is present copy: - src: migrate-vm.sh - dest: /usr/share/scripts/migrate-vm - mode: "0755" + src: add-vm.sh + dest: "{{ kvm_scripts_dir }}/add-vm" + mode: "0700" owner: root group: root - force: yes \ No newline at end of file + force: yes + +- name: migrate-vm script is present + copy: + src: migrate-vm.sh + dest: "{{ kvm_scripts_dir }}/migrate-vm" + mode: "0700" + owner: root + group: root + force: yes + +- name: kvmstats script is present + copy: + src: kvmstats.sh + dest: "{{ kvm_scripts_dir }}/kvmstats" + mode: "0700" + owner: root + group: root + force: yes + +# backward compatibility + +- name: remove old migrate-vm script + file: + path: /usr/share/scripts/migrate-vm + state: absent + when: "'/usr/share/scripts' not in kvm_scripts_dir" + +- name: remove old kvmstats script + file: + path: /usr/share/scripts/kvmstats + state: absent + when: "'/usr/share/scripts' not in kvm_scripts_dir" \ No newline at end of file