git.lirion.de

Of git, get, and gud

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. P. <harald.p.@xmart.de> 2018-07-10 10:30:24 +0200
committerH. P. <harald.p.@xmart.de> 2018-07-10 10:30:24 +0200
commit819374205af3ae702a22d1dc6d0542d25ace05bd (patch)
treebd88dc1d01c9c03a51d52b4d802d18cbd6cf28d9
downloadkvm-helper-819374205af3ae702a22d1dc6d0542d25ace05bd.tar.bz2
Initial commit, ported from systemd-units.git
-rw-r--r--README.md52
-rw-r--r--etc/systemd/system/cluster-muromachi.target6
-rw-r--r--etc/systemd/system/kvm-clustervm@.service25
-rw-r--r--etc/systemd/system/kvm-infravm@.service22
-rw-r--r--etc/systemd/system/kvm-network@.service15
-rwxr-xr-xusr/local/bin/kvmhelper215
6 files changed, 335 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e931295
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+## Scope
+
+This contains a "framework" I have written around virsh for controlling KVM
+machines and setups more easily. More easily here focuses on two objectives:
+
+1. Become able to start and stop everything at boot time. While a shutdown is
+ rather uninteresting in terms of dependencies and whatnot and libvirt-guests
+ can take this over, a boot out of the blue and in proper order is something
+ I wanted to take into my own hands
+2. Integration into systemd. More dependencies, groups of units, ... and isn't
+ it nice to see every more important KVM yoke report itself at boot time?
+ (Yes, I have a more verbose boot, a black screen and then a prompt is for
+ the Windows/Apple generation.)
+3. Finally, with 1. and 2.: you need to do things on your own from here. I
+ heavily use virsh, but I wrap around that, because if I shutdown a machine
+ that is already shutdown for whatever reason, I would not consider the
+ systemd unit's shutdown to be failed, virsh does.
+4. Automation. See what you automate, but don't care anymore :) This point is
+ actually a summary of all of the above.
+
+## To-Do
+
+* [ ] Create a configuration file /etc/kvmheklper.conf and an update script
+ that will place the respective systemd units inside /etc/systemd for
+ you
+* [ ] Make the systemd service and target units mroe abstract, i.e. it should
+ *really* not have any names anymore and reflect the 4 layer principle
+ only
+
+
+## Content
+
+The directories *etc/systemd/system*, *etc/tmpfiles.d*, and *usr/local/[s]bin* should be
+self-explanatory. Therein, you'll find the following:
+
+### Systemd units
+
+* **kvm-SOMETHING@.service:** Designed to control machines or networks. I view
+ my setups basically as a compound of four layers: Network, network VMs
+ like routing/firewall/manages switching, infrastructural machines such
+ as an iscsi provider, and then the slave-only machines. This more or
+ less is reflected in what is pushed in this repository and will improve
+ in the near future (more abstraction, more generalisms, better layer
+ steering).
+* **cluster-SOMETHING.target:** Basically just an example. I have three slave
+ only machines, so I put them in there to have them boot parallelly.
+
+* **kvmhelper:** The actual script administering the environment through virsh.
+ It can be entirely quiet (but isn't by default, it also tells you if it's
+ successful)), and it will also terminate successfully if you try to start
+ something that is already up or try to kill something that is already
+ down.
diff --git a/etc/systemd/system/cluster-muromachi.target b/etc/systemd/system/cluster-muromachi.target
new file mode 100644
index 0000000..ec63edc
--- /dev/null
+++ b/etc/systemd/system/cluster-muromachi.target
@@ -0,0 +1,6 @@
+[Unit]
+Description=Cluster "muromachi_cl"
+BindsTo=kvm-clustervm@centoscl0.service kvm-clustervm@centoscl1.service kvm-clustervm@centoscl2.service
+
+[Install]
+WantedBy=multi-user.target
diff --git a/etc/systemd/system/kvm-clustervm@.service b/etc/systemd/system/kvm-clustervm@.service
new file mode 100644
index 0000000..9300f38
--- /dev/null
+++ b/etc/systemd/system/kvm-clustervm@.service
@@ -0,0 +1,25 @@
+[Unit]
+Description=VM %i (with cluster inside)
+Requires=kvm-infravm@iscsi.service
+After=kvm-infravm@iscsi.service
+Requires=libvirtd.service
+Requires=kvm-firewall.service
+Requires=kvm-network@sosaria05.service
+Requires=kvm-network@san-cluster.service
+After=kvm-firewall.service
+After=libvirtd.service
+After=lvm2-monitor.service
+After=kvm-network@sosaria05.service
+After=kvm-network@san-cluster.service
+PartOf=cluster-muromachi.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/kvmhelper -q vm-start %i
+ExecStop=/usr/local/bin/kvmhelper -q vm-stop %i
+RemainAfterExit=yes
+TimeoutStartSec=10s
+TimeoutStopSec=60s
+
+[Install]
+WantedBy=cluster-muromachi.target
diff --git a/etc/systemd/system/kvm-infravm@.service b/etc/systemd/system/kvm-infravm@.service
new file mode 100644
index 0000000..d07150c
--- /dev/null
+++ b/etc/systemd/system/kvm-infravm@.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=Infrastructural VM %i
+Requires=libvirtd.service
+Requires=kvm-firewall.service
+Requires=kvm-network@sosaria05.service
+Requires=kvm-network@san-cluster.service
+After=kvm-firewall.service
+After=libvirtd.service
+After=lvm2-monitor.service
+After=kvm-network@sosaria05.service
+After=kvm-network@san-cluster.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/kvmhelper -q vm-start %i
+ExecStop=/usr/local/bin/kvmhelper -q vm-stop %i
+RemainAfterExit=yes
+TimeoutStartSec=10s
+TimeoutStopSec=60s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/etc/systemd/system/kvm-network@.service b/etc/systemd/system/kvm-network@.service
new file mode 100644
index 0000000..aca4e4b
--- /dev/null
+++ b/etc/systemd/system/kvm-network@.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=KVM network %i
+Requires=libvirtd.service
+After=libvirtd.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/kvmhelper -q net-start %i
+ExecStop=/usr/local/bin/kvmhelper -q net-stop %i
+RemainAfterExit=yes
+TimeoutStartSec=10s
+TimeoutStopSec=10s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/usr/local/bin/kvmhelper b/usr/local/bin/kvmhelper
new file mode 100755
index 0000000..e24e6f5
--- /dev/null
+++ b/usr/local/bin/kvmhelper
@@ -0,0 +1,215 @@
+#!/bin/env bash
+
+# Script starts or stops KVM machines or networks in your environment through virsh.
+# I.e. your environment must be set up already so you
+# - either are able to administer the system's KVM environment (qemu:///system), or
+# - have your environment variables set up so you aim at the right KVM environment
+#
+# Future versions may explicitly ask for the QEMU URI variable and default to qemu:///system, but for
+# now we take this for granted that virsh works right for you without specific connection parameters.
+#
+# The maximum time in seconds to wait for the net-* commands to be successful:
+KHNTWAIT=10
+# The maximum time in seconds to wait for the machine commands to be successful:
+KHVMWAIT=60
+# The interval in seconds a network should be polled for status change when executing a start/shutdown -
+# while it is called *NT*, this will also be used for starting a VM:
+KHNTIVAL="5"
+# The interval in seconds a VM should be polled for status change when executing a shutdown:
+KHVMIVAL="10"
+
+##### SCRIPT FROM HERE #####
+
+
+export SHUDDUP=0
+prsopt=$(getopt -n "$0" -o q -- "$@")
+eval "set -- $prsopt"
+while [ "$#" -gt 0 ];do
+ case "$1" in
+ (-q) SHUDDUP=1;shift;;
+ (--) shift;break;;
+ (*) exit 1;;
+ esac
+done
+
+RETVAL=0
+KHCMD="$1"
+shift
+KHTARGS=( "$@" )
+declare -x INFOCMD STARTCMD STOPCMD ACTGREP ACTYES ACTNO KILLCMD QWAIT QSTATE
+export QLISTNPARMS=( "--name" "--all" )
+
+command -V virsh >/dev/null||exit 255
+command -V tput >/dev/null||exit 255
+
+kh_help() {
+ [ "$SHUDDUP" -eq 1 ]&&return
+ echo -n "USAGE: $(basename "$0") vm-start|vm-stop|net-start|net-stop"
+ echo " VMNAME|NETNAME [VMNAME|NETNAME ...]"
+}
+
+case "$KHCMD" in
+ "net-start"|"net-stop"|"vm-start"|"vm-stop")
+ [ "${#KHTARGS[@]}" -eq 0 ]&&echo "Please specify a target."&&kh_help&&exit 127
+ ;;
+ "")
+ echo "Please specify a command."
+ kh_help
+ exit 127
+ ;;
+ *)
+ echo "$KHCMD is not a valid command." >&2
+ kh_help
+ exit 127
+ ;;
+esac
+
+kh_status() {
+ # Function should be called with exit status of stop/start command or
+ # "start name"/"stop name" for the initial message.
+ case "$1" in
+ "start")
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[....] Starting $2...\\033[s\\033[K"||:
+ ;;
+ "stop")
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[....] Shutting down $2...\\033[s\\033[K"||:
+ ;;
+ 255)
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[$(tput setaf 3)WARN$(tput sgr0)]"
+ [ "$SHUDDUP" -eq 0 ]&&echo -e "\\033[u\\033[K successfully force-killed. ($(tput setaf 3)warn$(tput sgr0))"||:
+ ;;
+ 0)
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[$(tput setaf 2) OK $(tput sgr0)]"
+ [ "$SHUDDUP" -eq 0 ]&&echo -e "\\033[u\\033[K done."||:
+ ;;
+ 10)
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[$(tput setaf 7)DONE$(tput sgr0)]"
+ [ "$SHUDDUP" -eq 0 ]&&echo -e "\\033[u\\033[K already done."||:
+ ;;
+ *)
+ [ "$SHUDDUP" -eq 0 ]&&echo -ne "$(tput cub 666)[$(tput setaf 1)FAIL$(tput sgr0)]"
+ [ "$SHUDDUP" -eq 0 ]&&echo -e "\\033[u\\033[K $(tput setaf 1)failed$(tput sgr0)."||:
+ ;;
+ esac
+}
+kh_qstate() {
+ case "$ACTGREP" in
+ "") QSTATE="$(virsh "$INFOCMD" "$2" 2>/dev/null)";;
+ *) QSTATE="$(virsh "$INFOCMD" "$2" 2>&1|grep "$ACTGREP"|awk '{print $NF}')";;
+ esac
+ case "$1" in
+ "start")
+ [ "$QSTATE" == "$ACTYES" ]&&return 0||return 1
+ ;;
+ "stop")
+ [ "$QSTATE" == "$ACTNO" ]&&return 0||return 1
+ ;;
+ esac
+}
+kh_exec() {
+ # Function expects exactly two arguments: command and KVM net name to be started/stopped.
+ # RC:
+ # 1 if no argument passed or arguments invalid
+ # 2 if start is unsuccessful or times out.
+ FN="kh_exec"
+ [ -z "$1" ]&&echo "Critical exception in $FN(): no argument passed!" >&2&&exit 1
+ [ -z "$2" ]&&echo "Critical exception in $FN(): no network name passed!" >&2&&exit 1
+ if ! virsh "$LISTCMD" "${QLISTNPARMS[@]}"|grep "$2" >/dev/null;then
+ echo "Critical exception in $FN(): Network/VM $2 unknown!" >&2&&exit 2
+ fi
+ case "$1" in
+ "start") kh_status start "$2";;
+ "stop") kh_status stop "$2";;
+ esac
+ case "$1" in
+ "start")
+ kh_qstate start "$2"&&kh_status 10 "$2"&&return 0
+ ;;
+ "stop")
+ kh_qstate stop "$2"&&kh_status 10 "$2"&&return 0
+ ;;
+ esac
+ TOELAPSE="0"
+ while [ "$TOELAPSE" -lt "$QWAIT" ];do
+ case "$1" in
+ "start") virsh "$STARTCMD" "$2" >/dev/null 2>&1;;
+ "stop") virsh "$STOPCMD" "$2" >/dev/null 2>&1;;
+ esac
+ TOELAPSE=$((TOELAPSE+QIVAL))
+ sleep "$QIVAL"
+ [ "$SHUDDUP" -eq 0 ]&&echo -n "."
+ case "$1" in
+ "start")kh_qstate start "$2"&&break;;
+ "stop")kh_qstate stop "$2"&&break;;
+ esac
+ done
+ case "$1" in
+ "start")
+ if kh_qstate start "$2";then
+ kh_status 0
+ else
+ kh_status 1;RETVAL=20
+ fi
+ ;;
+ "stop")
+ if kh_qstate stop "$2";then
+ kh_status 0
+ else
+ virsh "$KILLCMD" "$2" >/dev/null 2>&1
+ if kh_qstate stop "$2";then
+ kh_status 255;RETVAL=255
+ else
+ kh_status 1;RETVAL=20
+ fi
+ fi
+ ;;
+ esac
+}
+kh_filter() {
+ # Function expects exactly two arguments: command and KVM net name to be started/stopped.
+ # This works as a pre-filter for knstart and knstop
+ # RC:
+ # 1 if no argument, no network name passed, or wrong syntax
+ # 2 if network/VM does not exist
+ # 3 if network/VM cannot be started
+ # 4 if network/VM state is not determinable
+ FN="kh_filter"
+ [ -z "$1" ]&&echo "Critical exception in $FN(): no argument passed!" >&2&&exit 1
+ [ -z "$2" ]&&echo "Critical exception in $FN(): no action specified!" >&2&&exit 1
+ [ -z "$3" ]&&echo "Critical exception in $FN(): no network/VM name passed!" >&2&&exit 1
+ case "$1" in
+ "net")
+ INFOCMD="net-info"; STARTCMD="net-start"
+ STOPCMD="net-destroy"; ACTGREP="^Active:"
+ ACTYES="yes"; ACTNO="no"
+ KILLCMD="$STOPCMD"; LISTCMD="net-list"
+ QWAIT="$KHNTWAIT"; QIVAL="$KHNTIVAL"
+ ;;
+ "vm")
+ INFOCMD="domstate"; STARTCMD="start"
+ STOPCMD="shutdown"; #ACTGREP="^State:"
+ ACTYES="running"; ACTNO="shut off"
+ KILLCMD="destroy"; LISTCMD="list"
+ QWAIT="$KHVMWAIT" QIVAL="$KHVMIVAL"
+ [ "$2" == "start" ]&&QIVAL="$KHNTIVAL"
+ ;;
+ esac
+ case "$2" in
+ "start") kh_exec start "$3";;
+ "stop") kh_exec stop "$3";;
+ *) echo "Invalid action $2 to $FN()!" >&2&&exit 1;;
+ esac
+}
+
+[ ${#KHTARGS} -eq 0 ]&&echo "No VM/network specified, aborting." >&2&&exit 127
+for i in "${KHTARGS[@]}";do
+ case "$KHCMD" in
+ "net-stop") kh_filter net stop "$i"||RETVAL="$((RETVAL+1))";;
+ "net-start") kh_filter net start "$i"||RETVAL="$((RETVAL+1))";;
+ "vm-stop") kh_filter vm stop "$i"||RETVAL="$((RETVAL+1))";;
+ "vm-start") kh_filter vm start "$i"||RETVAL="$((RETVAL+1))";;
+ esac
+
+done
+
+exit "$RETVAL"