From 8682d695ed430956752a000de79674261b63f19f Mon Sep 17 00:00:00 2001 From: Harald Pfeiffer Date: Mon, 9 Jul 2018 18:13:03 +0200 Subject: Initial commit of kvm core script --- usr/local/bin/kvmhelper | 215 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100755 usr/local/bin/kvmhelper 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" -- cgit v1.2.3