#!/bin/bash # kvm-shell - a small interface to kvm for regular users # Copyright (C) 2009 Michael Kress # Copyright (C) 2011 Joerg Jaspert # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # INSTALL: # 1) create these entries for your users in /etc/sudoers (use visudo) # (in Debian its even better to use /etc/sudoers.d/kvm-shell, run # visudo -f /etc/sudoers.d/kvm-shell): # User_Alias KVMUSERS = vuser1,vuser2 # Cmnd_Alias KVM = /usr/bin/virsh # KVMUSERS ALL = NOPASSWD: KVM # 2) Create /etc/kvm-shell/users (or whatever name you set USERSFILE # to below): # Format: # # Example: # vuser1 vm1 # vuser2 vm1 vm2 vm3 # Description: List here which users are allowed to control which vms set -e set -u set -E VERSION="2.0" PROGRAM="kvm-shell" # configuration USERSFILE=/etc/kvm-shell/users # What do we run for sudo? SUDO=/usr/bin/sudo # Where is virsh VIRSH=/usr/bin/virsh # After how many seconds without input do we exit the dialogbox? TIMEOUTSECS=120 # A tempdir. Whatever is in the environment before has precedence. TMPDIR=${TMPDIR:-"/tmp"} # Set to no if you do not want people to edit DNS information DO_RDNS="yes" # Where are the rdns files stored? # Files in that directory should be named after the VM they are for. # Inside kvm-shell they will just end up in an editor, whatever function # and format is required by the tool actually doing the rdns setup is up # to whatever, kvm-shell doesn't care # The files don't even need to be writable by the user, readable is enough. # (New version is edited in a tempfile, then given to RDNSSCRIPT) # Obviously RDNSSCRIPT should be able to replace them with the new version. RDNS=/etc/kvm-shell/rdns # A script to do whatever with the dns function after the editbox # is done. It gets two arguments: # 1 - the original file # 2 - the new edit from the user # 3 - the calling username (can be used for logging. Nice for sudo users) RDNSSCRIPT="${SUDO} /usr/local/bin/dodns" ######################################################################## # Helper functions # ######################################################################## # Simple function to cleanup whenever we exit. # This goes through all variable names in TEMPFILES and deletes the files # that those variables point to. function cleanup() { ERRVAL=$? trap - ERR EXIT TERM HUP INT QUIT for TEMPFILE in ${TEMPFILES}; do DELF=${!TEMPFILE:-""} if [ -n "${DELF}" ] && [ -f "${DELF}" ]; then rm -f "${DELF}" fi done exit $ERRVAL } # Define TEMPFILES so we are always sure it is there TEMPFILES="" ######################################################################## ######################################################################## ######################################################################## # Main functions come here # ######################################################################## # Function to handle reverse DNS setup. Basically uses dialogs editbox # to let user edit a file, then hands that over to RDNSSCRIPT. # Arguments: VM name function revdns() { local VM=${1:-""} if [ -z ${VM} ]; then # No VM, byebye return fi local dnsfile="${RDNS}/${VM}" if [ -f "${dnsfile}" ]; then # edit it, put output into a tempfile for processing dnsedit=$(mktemp -p "${TMPDIR}" kvmshell.XXXXXXXX) TEMPFILES="${TEMPFILES} dnsedit" set +e dialog --backtitle "KVM Shell ${VERSION}" \ --title "User ${USER} - Edit reverse DNS for VM ${VM}" \ --cancel-label "Cancel, don't save" --ok-label "Apply" \ --timeout ${TIMEOUTSECS} \ --editbox ${dnsfile} 22 78 2>${dnsedit} ret=$? set -e case ${ret} in 0) # Clean exit, means we have something to do echo "Applying changes, please be patient..." ${RDNSSCRIPT} "${dnsfile}" "${dnsedit}" "${USER}" ;; *) # Anything else means we send em to the shredder... echo "Break, not changing RDNS for ${VM}" ;; esac rm -f "${dnsedit}" fi } # Just split out a little helptext function helpbox() { set +e dialog --stdout --backtitle "KVM Shell ${VERSION}" \ --title "User ${USER} - Help" \ --msgbox " KVM Shell ${VERSION} This is the KVM Shell Version ${VERSION}, written by Joerg Jaspert, based on an initial version from Michael Kress. KVM Shell allows you to manage your domains daily operations such as using its serial console, starting or stopping it. You may also, if configured, change the reverse DNS for the IP addresses associated with your VM. Simply select the VM you want to manage and then follow the menu options given to you. [This text could be extended. Want to write it?] " 20 70 set -e return 0 } ######################################################################## # main_menu: Find the VMs for the current user and display a menu to # select from # No arguments function main_menu { local AVAILABLEVMS=$(egrep "^${USER} " "${USERSFILE}") # Only end up with the VMs, not the username too AVAILABLEVMS=${AVAILABLEVMS#* } local MENUITEMS="" local COUNTER=0 for vm in ${AVAILABLEVMS}; do set +e local STATE=$(${SUDO} ${VIRSH} domstate $vm 2>/dev/null) local ret=$? set -e # if virsh got us nothing we replace it with "unknown" STATE=${STATE:-"unknown"} # dialog is picky-picky-annoying, so we want to ensure there isnt a space anywhere in STATE STATE=${STATE// /-} if [ "${ret}" = 0 ]; then COUNTER=$(( COUNTER+1 )) MENUITEMS="${MENUITEMS} ${vm} ${STATE}" fi done dialogout=$(mktemp -p "${TMPDIR}" kvmshell.XXXXXXXX) TEMPFILES="${TEMPFILES} dialogout" set +e dialog --backtitle "KVM Shell ${VERSION}" \ --title "User ${USER} - Select VM to manage" \ --cancel-label "Exit" --ok-label "Select VM" \ --help-button --timeout ${TIMEOUTSECS} \ --menu 'Available VMs:' 15 55 ${COUNTER} ${MENUITEMS} 2>${dialogout} ret=$? set -e local selvm=$(cat "${dialogout}") rm -f "${dialogout}" case $ret in 0) # Clean exit, means selected a VM, lets call its action menu select_action ${selvm} ;; 2) # Hoowah, help? Go away. helpbox ;; *) # Anything else means we send em to the shredder... exit ;; esac } # Deal with a VM directly, do whatever (console, dominfo, ...) # Takes on argument, the VM name function select_action { local selvm=${1:-""} if [ -z "${selvm}" ]; then return 2 fi set +e STATE=$(${SUDO} ${VIRSH} domstate ${selvm} 2>/dev/null) ret=$? set -e if [ ${ret} -ne 0 ]; then dialog --sleep 5 --title "User ${USER} - unknown VM Selected" --infobox "\nHeyho, you selected an unknown VM" 5 40 return fi while :; do dialogout=$(mktemp -p "${TMPDIR}" kvmshell.XXXXXXXX) TEMPFILES="${TEMPFILES} dialogout" set +e dialog --backtitle "KVM Shell ${VERSION}" \ --title "User ${USER} - please select action for VM ${selvm} (${STATE})" \ --cancel-label "Main Menu" --ok-label "Select Action" \ --help-button --timeout ${TIMEOUTSECS} \ --menu "Available actions for VM ${selvm} ($STATE):" 15 65 10 \ "dominfo" "Information about your VM" \ "console" "Serial console access" \ "start" "Boot a stopped VM" \ "shutdown" "Stop a running VM" \ "reboot" "Reboot a running VM" \ "destroy" "Kill a VM (as if power cable pulled)" \ "rdns" "Edit reverse DNS of IPs for the VM" \ "mainmenu" "Back to main menu" 2>${dialogout} ret=$? set -e local selact=$(cat "${dialogout}") rm -f "${dialogout}" clear case ${ret} in 0) case ${selact} in console) echo "Remember, you can leave the console with Strg+]" echo "Also, you may need to hit Enter once to see anything" echo "" ${SUDO} ${VIRSH} ${selact} ${selvm} ;; dominfo|start|shutdown|reboot|destroy) set +e ${SUDO} ${VIRSH} ${selact} ${selvm} set -e ;; rdns) if [ "${DO_RDNS}" = "yes" ]; then revdns ${selvm} else echo "Sorry, admin disabled this function. Maybe you should poke them with a stick?" sleep 2 fi ;; mainmenu) return ;; esac read -p "[Hit Return]" x ;; 2) helpbox ;; *) return ;; esac done } trap cleanup EXIT TERM HUP INT QUIT while :; do main_menu done exit 0