#!/bin/bash ####################################### ## ## ## function definitions ## ## kvm-tools ## ## ## ####################################### # **** usage message **** # this will print the usage message usage() { echo "usage: kvm-tools options OPTIONS: -c create guest -e edit guest -a add virtual disk to guest -b backup guest(s) -h show this message -v show version information -i generate new id's -f run environment check/fix -u update kvm-tools (pull from git) " } # **** version message **** # this will print the version message version() { echo echo "kvm-tools" echo echo "vesion: $version" echo "author: $author" echo log debug "version - version information printed" } # **** get image path(s) **** # with parse the image path(s) from xml getimgpath() { local raw=$(virsh dumpxml $1 | grep "file=" | grep -v "/dev/" | tr -d "<'>") for item in $raw; do if [ $item = "source" ]; then : else echo ${item:5:$(($(echo `expr length "$item"`)-6))} fi done } # **** get xml path **** # will search for matching uuid in $configpath and defines global variable $xmlpath getxmlpath() { local list=$(ls $configpath | grep xml | grep -v "~") local uuid=$(virsh domuuid $1) local i=0 for var in $list; do if [ -w $configpath/$var ]; then local raw=$(cat $configpath/$var | grep uuid) local uuidfromfile=${raw:8:36} if [ $uuidfromfile = $uuid ]; then xmlpath="$configpath/$var" let i++ fi else log error 'getxmlpath - some of the xml configuration files are not writeable, run "kvm-tools -f" to fix this' exit 1 fi done if [ $i = "1" ]; then : return 0 elif [ $i = "0" ]; then log error "getxmlpath - xml file for $1 could not be determined in $configpath" exit 1 elif [ $i -gt "1" ]; then log error "getxmlpath - there is more than one file with matching uuid in $configpath" exit 1 fi } # **** dialog window creation **** # creates the dialog windows graph() { local bgtitle="kvm-tools $version | $1" if [ $2 = "--inputbox" ]; then local size="7 80" elif [ $2 = "--yesno" ]; then local size="20 80" elif [ $2 = "--fselect" ]; then local size="12 80" else local size="20 80 14" fi dialog --backtitle "$bgtitle" --no-cancel "$2" "$3" $size $4 2> /tmp/dialog local returncode=$? clear dialogresult=$(cat /tmp/dialog) rm /tmp/dialog return $returncode } # **** backup **** # the virtual disk image and the xml config file # will be saved to the backuppath backup() { # am i root? amiroot # creat local variable with given machines (comma seperated list) local list=$1 # creat local variables with all running/all shutoff machines (whitespace seperated) local running=$(virsh list --all | grep "running" | awk '{print $2}') local shutoff=$(virsh list --all | grep "shut off" | awk '{print $2}') # check for given option "all" if [ $list = "all" ]; then # if "all" was given fill the list with all machines list="$running $shutoff" else # otherwise just change the commas to whitespaces in the list local list=${list//,/ } fi # getting a short timestamp like this 20110423 #local timestamp=$(gettimestamp short) # $var will be the machine name for var in $list; do log info "backup - starting backup of $var" # BACKUP XML file to backuppath virsh dumpxml $var > $backuppath/$var.xml log debug "backup - backing up xml file for $var" # SUSPEND machine if running local run=0 if [ $(echo $shutoff | grep -c $var) = "0" ]; then log debug "backup - suspending $var" virsh suspend $var &> /dev/null local run=$? fi # check if suspend was successfull (if not.. skip) if [ $run = 0 ]; then # check for more than one image file for item in $(getimgpath $var); do # CEATE HASH for image diff log debug "backup - creating sha1 hash of $item" local sum=$(sha1sum $item | awk '{print $1}') # get new filename local newfile=${item##/*/} #local ending=${item:$(echo `expr index "$item" .`)} # BACKUP IMAGE file to backuppath log debug "backup - backing up $item" cp $item $backuppath/$newfile # RESUME machine if paused if [ $(echo $shutoff | grep -c $var) = "0" ]; then log debug "backup - resuming $var" virsh resume $var &> /dev/null fi # CHECK if HASH matches log debug "backup - creating sha1 hash of $backuppath/$newfile" if [ $sum = $(sha1sum $backuppath/$newfile | awk '{print $1}') ]; then log debug "backup - hash for $newfile was successfully checked" log info "backup - $var successfully backuped" else log error "backup - hash for $newfile does not match" fi done else log error "backup - could not suspend $var, skipping backup of $var" fi done } # **** edit vm config (xml) **** # opens vim with xml file for given guest edit() { # i shouldn't be root amiroot not # get the xml filepath for $1 getxmlpath $1 # create sum for diff sha1sum $xmlpath > /tmp/checksum vi $xmlpath sha1sum -c --status /tmp/checksum if [ "$?" = "0" ]; then log debug "edit - $USER did not change xml configuration for $1" else virsh define $xmlpath &> /dev/null log info "edit - $USER changed xml configuration for $1" fi rm /tmp/checksum } # **** fix stuff :) **** # environment check, fixes stuff :) fix() { # am i root? amiroot # install missing dependencies log debug "fix - installing missing dependencies" apt-get update apt-get install -y dialog pwgen libvirt-bin python-vm-builder libxml-xpath-perl # reseting file permissions in $configpath chgrp -R libvirtd $configpath chmod -R 775 $configpath log debug "fix - reseted permissions in $configpath" # fixing perms in $imagepath chmod -R 775 $imagepath chgrp -R libvirtd $imagepath log debug "fix - reseted permission in $imagepath" if [ -r $logfile ]; then chmod 775 $logfile chgrp libvirtd $logfile log debug "fix - reseted permission for $logfile" else touch $logfile chmod 775 $logfile chgrp libvirtd $logfile log debug "fix - created $logfile and set permissions" fi # adding kvm-tools to /usr/bin (symlink) if [ -r /usr/bin/kvm-tools ]; then : else ln -s $repopath/kvm-tools.sh /usr/bin/kvm-tools log debug "fix - created a symlink for kvm-tools in /usr/bin" fi log info "fix - environment fix for kvm-tools successfully ran" return 0 } #browseqcow() #{ # # read imagename # if [ -z $1 ]; then # echo 'usage: "browseqcow2 "' # exit 1 # fi # # imagename=$1 # # # # im i root # amiroot # # # run unmount # if [[ $1 = "--unmount" || $1 = "-u" || $1 = "--umount" ]]; then # umount /mnt/ # qemu-nbd --disconnect /dev/nbd0 # echo "image unmounted" # echo # exit 0 # fi # # # load kernel module # modprobe nbd max_part=8 # # # gen blockdevice # qemu-nbd --connect=/dev/nbd0 $imagename # # # make the actual mount # sleep 2 # mount /dev/nbd0p1 /mnt/ # # echo "$imagename is mounted in /mnt" # echo 'run "browseqcow2.sh --unmount" when ready' # echo #} # **** add image **** # attaches new created virtual disk to given guest addimage() { # no need for root privileges amiroot not # virtual machine name (passed) local hostname=$1 # genqcow2 genqcow $hostname # xmlpath for $hostname getxmlpath $hostname local targets="vdb vdc vdd vde vdf vdg vdh vdi vdj" for var in $targets; do virsh dumpxml $hostname | grep $var if [ $? = 1 ]; then local where=$(sed -n '/\/disk/=' $xmlpath) for item in $where; do local nr=$item done sed ''$nr' a ' $xmlpath > $xmlpath"_new" rm $xmlpath && mv $xmlpath"_new" $xmlpath virsh define $xmlpath log info "addimage - $var was added to $hostname" chmod 775 $xmlpath chgrp libvirtd $xmlpath exit 0 else log error "addimage - $var already exists for $hostname" fi done } # **** does image exist? **** # checks if images exists and gives back errorcode(0 if its there, 1 of not) imgexisting() { # imgexisting needs global vars: $imagepath # usage: imgexisting hostname imagename # passed args local hostname=$1 local imagename=$2 virsh dumpxml $hostname | grep $imagename local inxml=$? ls $imagepath | grep $imagename local inpath=$? if [ $inpath = 0 ]; then log info "imgexisting - $imagename exists in $imagepath" return 0 elif [ $inxml = 0 ]; then log info "imgexisting - $imagename exists in the xml of $hostname" return 0 else log info "imgexisting - $imagename is not existing" return 1 fi } # **** generate qcow2 image **** # creates a qcow2 image in the $imagepath genqcow() { local bgmessage="virtual disk creation" local hostname=$1 # imagename local exists=0 while [ $exists = 0 ]; do graph "$bgmessage" --inputbox "imagename?" $hostname"_storage.qcow2" imagename="$dialogresult" #global var imgexisting $hostname $imagename local exists=$? done # imagesize graph "$bgmessage" --menu "imagesize?" "4 GB 8 GB 16 GB 32 GB 64 GB 128 GB" local imagesize="$dialogresult" # generating the actual image qemu-img create -f qcow2 $imagepath/$imagename $imagesize"G" log info "genqcow - image with $imagesize"GB" created: $imagepath/$imagename" chgrp libvirtd $imagepath/$imagename chmod 775 $imagepath/$imagename } # **** generates new id's **** # prints new ids to the logengine newids() { # you shouldn't be root amiroot not # generating new ids for a vm local newuuid=$(uuidgen) local newmac=$(MACADDR="52:54:$(dd if=/dev/urandom count=1 2>/dev/null | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4/')"; echo $MACADDR) # printing new ids log info "newids - $USER generated new id's" echo echo "UUID: $newuuid" echo "MAC: $newmac" echo } # **** create virtual ubuntu server **** # this will start the wizard for creating a new virtual machine mkjeos() { # config section local bgmessage="server deployment" local hostname=$1 # am i root? amiroot # configuration wizard proceed=1 while [ $proceed != "0" ]; do # hosname graph "$bgmessage" --inputbox "hostname?" $hostname local hostname="$dialogresult" # domain graph "$bgmessage" --inputbox "domain?" $defaultdomain local domain="$dialogresult" # username graph "$bgmessage" --inputbox "username?" local username="$dialogresult" # architecture graph "$bgmessage" --menu "architecture?" "amd64 64bit_kernel i386 32bit_kernel" local arch="$dialogresult" # memory graph "$bgmessage" --menu "memory?" "256 MB 512 MB 1024 MB 2048 MB 4096 MB 8192 MB" local mem="$dialogresult" # rootsize graph "$bgmessage" --menu "rootsize?" "4 GB 8 GB 16 GB 32 GB 64 GB 128 GB" local rootsize="$dialogresult" # swapsize graph "$bgmessage" --menu "swapsize?" "512 MB 1024 MB 2048 MB 4096 MB 8192 MB" local swapsize="$dialogresult" # ubuntu release graph "$bgmessage" --menu "ubuntu release?" "lucid 10.04_long_term hardy 8.04_long_term maverick 10.10" local release="$dialogresult" # network configuration graph "$bgmessage" --inputbox "ip address?" local ip="$dialogresult" graph "$bgmessage" --inputbox "netmask?" local netmask="$dialogresult" graph "$bgmessage" --inputbox "gateway?" local gateway="$dialogresult" graph "$bgmessage" --inputbox "dns server(s)?" local dns="$dialogresult" # mirrors local mirrorlist="$defaultmirror default_mirror http://at.archive.ubuntu.com/ubuntu official_austria_mirror http://ftp.halifax.rwth-aachen.de/ubuntu uni_aachen" graph "$bgmessage" --menu "mirror?" "$mirrorlist" local mirror="$dialogresult" # cpus graph "$bgmessage" --menu "cpus?" "1 singlecore 2 dualcore 4 quadcore 8 insane" local cpus="$dialogresult" # local network bridges local bridgelist=$(ifconfig | grep HWaddr | awk '{print $1}' | grep br) local localbridges="" for var in $bridgelist; do local localbridges="$localbridges $var local_network_bridge" done graph "$bgmessage" --menu "network?" "$localbridges" local bridge="$dialogresult" # timezone #local timezonelist="Europe/Vienna GMT+1" #graph "$bgmessage" --menu "timezone?" "$timezonelist" #local timezone="$dialogresult" local timezone="Europe/Vienna" # additional software for var in $recpackages; do local packagegraph="$packagegraph $var recommended on" done for var in $optpackages; do local packagegraph="$packagegraph $var optional off" done graph "$bgmessage" --checklist "additional software?" "$packagegraph" local packagelist="${dialogresult//\"/ }" for var in $packagelist; do local addpkg="$addpkg --addpkg $var" done # select firstboot script #graph "$bgmessage" --fselect ~/ graph "$bgmessage" --fselect $repopath/firstboot.sh local firstboot="$dialogresult" #calculate rootsize local rootsize=$((rootsize*1024)) #gen password local password=$(pwgen -snc 10 1) local text=$(echo "hostname: $hostname domain: $domain user: $username cpus: $cpus memory: $mem ip: $ip release: $release architecture: $arch rootsize: $rootsize swapsize: $swapsize network: $bridge mirror: $mirror firstboot: $firstboot continue? (CTRL+C will kill the creation process) ") graph "$bgmessage" --yesno "$text" local proceed=$? done clear log debug "mkjeos - creation of $hostname started. this can take a while, so please be patient." vmbuilder kvm ubuntu --verbose --mem="$mem" --cpus="$cpus" --rootsize="$rootsize" --swapsize="$swapsize" --domain="$domain" --user="$username" --pass="$password" --suite="$release" --flavour=virtual --mirror="$mirror" --security-mirror="$mirror" --timezone="$timezone" --arch="$arch" --hostname="$hostname" --libvirt="qemu:///system" --bridge="$bridge" --ip="$ip" --mask="$netmask" --gw="$gateway" --dns="$dns" $addpkg --firstboot=$firstboot --destdir="$imagepath/tmpvmbuilder" &> /tmp/builderlog if [ $? = "0" ]; then log info "mkjeos - $hostname successfully created" # removing temporary builderlog rm /tmp/builderlog # moving imagefile, redefining xml (changing image path) mv $imagepath/tmpvmbuilder/tmp* $imagepath/$hostname"_"system.qcow2 rm -r $imagepath/tmpvmbuilder local image=$(getimgpath $hostname) getxmlpath $hostname sed -i 's/'${image//\//\\\/}'/'${imagepath//\//\\\/}'\/'$hostname'_system.qcow2/' $xmlpath virsh define $xmlpath # run env fix fix # start the machine? graph "$bgmessage" --yesno "everything is done, do you want to start $hostname now?" local startvm=$? if [ $startvm = 0 ]; then virsh start $hostname fi # log log info "mkjeos - hostname=$hostname.$domain, user=$username, password=$password release=$release, arch=$arch, size=$rootsize, swap=$swapsize, network=$bridge, ip=$ip, mirror=$mirror" # machine details output clear echo echo "machine details" echo echo "hostname=$hostname.$domain" echo "user=$username" echo "password=$password" echo "release=$release" echo "arch=$arch" echo "size=$rootsize" echo "swap=$swapsize" echo "network=$bridge" echo "ip=$ip" echo "mirror=$mirror" echo else log error "mkjeos - there was an error creating virtual machine $hostname" exit 1 fi } # end of functions