summaryrefslogtreecommitdiff
path: root/vmgr
diff options
context:
space:
mode:
Diffstat (limited to 'vmgr')
-rwxr-xr-xvmgr328
1 files changed, 328 insertions, 0 deletions
diff --git a/vmgr b/vmgr
new file mode 100755
index 0000000..b45b8e3
--- /dev/null
+++ b/vmgr
@@ -0,0 +1,328 @@
+#!/usr/bin/env bash
+#
+# Jei! Ég get að skrifa kóðann!!
+#
+
+network='default'
+prefixmac='52:54:00:7e:70:'
+prefixip='192.168.122.'
+
+
+binvirsh='/usr/bin/virsh'
+binvirt_install='/usr/bin/virt-install'
+binqemu_img='/usr/bin/qemu-img'
+bincp='/bin/cp'
+
+
+#
+# Standard logging function. Prints standardized output. Timestamps all errors
+# and infos.
+#
+# @param level Log level to print
+# @param msg All remaining arguments to be printed as message
+#
+function log {
+ level=${1}
+ shift
+ msg=${@}
+
+ d=$(date '+%F %T')
+
+ oldifs=${IFS}
+ IFS=" "
+
+ for i in ${msg}; do
+ if [[ ${level} == 'error' ]]; then
+ echo -n "${d} ERROR "
+ elif [[ ${level} == 'info' ]]; then
+ echo -n "${d} INFO "
+ elif [[ ${level} == 'debug' ]]; then
+ echo -n "${d} DEBUG "
+ elif [[ ${level} == 'fatal' ]]; then
+ echo -n "${d} FATAL "
+ fi
+
+ echo -e ${i}
+ done
+
+ IFS=${oldifs}
+ # Exit 1 if message was fatal
+ [[ ${1} == 'fatal' ]] && exit 1
+}
+
+
+#
+# Executes virt-install with the specified arguments.
+#
+function install {
+ local name=${1}
+ local disk=${2}
+ local mac=${3}
+ local mem=4096
+
+ out=$(${binvirt_install} \
+ --name ${name} \
+ --ram=${mem} \
+ --virt-type=kvm \
+ --disk "${disk}" \
+ -w network=default,mac=${mac} \
+ --graphics vnc,password=${name} \
+ --noautoconsole \
+ --import 2>&1)
+
+ # Log the output of virt install
+ if [[ $? -gt 0 ]]; then
+ log error "${out}"
+ else
+ log info "${out}"
+ fi
+}
+
+
+
+#
+# Clones a disk. Supports two speed options, fast or small.
+# Fast just does a simple copy of the disk from source to destination.
+# Small uses qemu-img to recompress the disk from source to destination to save
+# space.
+#
+# @param src
+# @param dest
+# @param speed
+#
+function clone_disk {
+ local src=${1}
+ local dest=${2}
+ local speed=${3}
+
+ [[ -z ${1} ]] && echo "ERROR: Template path required (arg 1)." && exit 1
+ [[ -z ${2} ]] && echo "ERROR: Destination disk path required (arg 2)." && exit 1
+ [[ -z ${3} ]] && echo "ERROR: Clone speed required (arg 3)." && exit 1
+
+ if [[ ${speed} == 'fast' ]]; then
+ ${bincp} ${src} ${dest}
+ elif [[ ${speed} == 'small' ]]; then
+ ${binqemu_img} convert -O qcow2 -p -c ${src} ${dest}
+ else
+ log error "Unknown speed: ${speed}"
+ exit 1
+ fi
+}
+
+
+#
+# Reserves an ip on the specified network for mac address ${mac}. Also inserts
+# dns entry for the specified name.
+#
+function net_reserve_ip {
+ local network=${1}
+ local name=${2}
+ local mac=${3}
+ local ip=${4}
+
+ xml="<host mac='${mac}' name='${name}' ip='${ip}'/>"
+ # Perform reservation command
+ out=$(${binvirsh} net-update ${network} add ip-dhcp-host "${xml}" 2>&1)
+
+ # Log the output of ip reservation operation
+ if [[ $? -gt 0 ]]; then
+ log error "A problem occured reserving ip address ${ip} for mac ${mac}."
+ log error "${out}"
+ exit 1
+ else
+ log info "${out}"
+ fi
+}
+
+#
+# TODO
+#
+function net_rm_reservation {
+ local network=${1}
+ local name=${2}
+ local mac=${3}
+ local ip=${4}
+
+ xml="<host mac='${mac}' name='${name}' ip='${ip}'/>"
+ # Perform reservation command
+ ${binvirsh} net-update ${network} delete ip-dhcp-host "${xml}"
+
+ if [[ $? -gt 0 ]]; then
+ log error "A problem occured deleting ip reservation (${ip}) for mac ${mac}."
+ exit 1
+ fi
+}
+
+
+
+#
+# Determines the next available vm index based on network mac address usage.
+#
+function get_next_index {
+ local network=${1}
+ local prefixmac=${2}
+
+ # Start at 10, to allow for 0-9 as network-resources that aren't transient.
+ index=10
+
+ # Get list of hosts
+ hostxml="$(${binvirsh} net-dumpxml ${network} | grep '<host' )"
+
+ # For each index, check if the mac address exists
+ while [ $(echo ${hostxml} | grep "mac='${prefixmac}${index}'" -c ) -ne 0 ]; do
+ #echo "Host exists at ${macprefix}${index}"
+ # Use this fancy printf statement to ensure we're always zero padded.
+ index=$(printf "%0*d" 2 $((${index} + 1)))
+ done
+
+ # Unused index found. Echo.
+ echo "${index}"
+}
+
+
+#
+# TODO
+#
+function get_index_from_name {
+ local name=${1}
+
+ # Reset name so we grab whatever is at the front of an fqdn. If name isn't an
+ # fqdn, this will return the same.
+ name=$(echo ${name} | cut -d '.' -f 1)
+
+ # Get length of the hostname
+ local len=$((${#name} - 2))
+
+ # Strip index off end of hostname
+ # Note that we assume a 2 digit
+ local index=${name:$len:2}
+
+ # Return index
+ echo ${index}
+}
+
+
+
+function vm_new {
+ [[ -z ${1} ]] && log fatal "A prefix name for the new vm is required."
+ [[ -z ${2} ]] && log fatal "A template disk path is required."
+
+ local prefix=${1}
+ local template=${2}
+ local domain=${3}
+
+ # Get next mac/index/ip
+ local next=$(get_next_index ${network} ${prefixmac})
+ local mac="${prefixmac}${next}"
+ local ip="${prefixip}${next}"
+ local name="${prefix}${next}${domain}"
+ local disk="${name}.sda.qcow2"
+
+ # echo -e "VM Variables:\n-------------"
+ # echo -e " \e[1mmac\e[0m: ${mac}"
+ # echo -e " \e[1mip\e[0m: ${ip}"
+ # echo -e " \e[1mname\e[0m: ${name}"
+ # echo -e " \e[1mdisk\e[0m: ${disk}"
+ # echo -e " \e[1mtemplate\e[0m: ${template}"
+ # echo
+
+ # Clone the disk
+ log info "Cloning disk from ${template} to ${disk}"
+ clone_disk ${template} ${disk} 'fast'
+
+ log info "Reserving ip address ${ip} for mac ${mac}"
+ net_reserve_ip ${network} ${name} ${mac} ${ip}
+
+ log info "Installing VM into libvirtd."
+ install ${name} ${disk} ${mac}
+}
+
+
+function vm_rm {
+ [[ -z ${1} ]] && echo "Please a vm name to be deleted." && exit 1
+
+ local name=${1}
+
+ # Get next mac/index/ip/disk
+ local index=$(get_index_from_name ${name})
+ local mac="${prefixmac}${index}"
+ local ip="${prefixip}${index}"
+ local disk="${name}.sda.qcow2"
+
+ # Get storage pool name (for refresh later)
+ local pool=$(basename $(pwd))
+
+ # Shut the VM down forcibly (it's about to be deleted, so we don't care about
+ # a friendly shutdown here)
+ ${binvirsh} destroy ${name}
+
+ # Refresh the pool after VM destruction. If we don't do this, virsh
+ # intermittently fails on storage removal, with a permission denied error.
+ # This is because it doesn't know that the disk is no longer in use, so a
+ # pool-refresh is required.
+ log info "Refreshing storage pool ${pool} to ensure disk removal success."
+ poolout=$(${binvirsh} pool-refresh ${pool} 2>&1)
+
+ if [[ $? -gt 0 ]]; then
+ log error "${poolout}"
+ else
+ log info "${poolout}"
+ fi
+
+ # Delete the VM and remove all of its storage
+ log info -n "Removing VM ${name} and all storage."
+ undefout=$(${binvirsh} undefine --remove-all-storage ${name})
+
+ if [[ $? -gt 0 ]]; then
+ log error "${undefout}"
+ else
+ log info "${undefout}"
+ fi
+
+ # Remove its network entry
+ log info "Deleting ip reservation for ${name}, mac ${mac}, ip ${ip}"
+ net_rm_reservation ${network} ${name} ${mac} ${ip}
+}
+
+
+
+#
+# Lists all VMs currently running, their expected ips, and mac addresses.
+#
+function vm_ls {
+ names=$(${binvirsh} list --name)
+
+ output='Host IP MAC\n---- -- ---\n'
+ for i in ${names}; do
+ index=$(get_index_from_name ${i})
+ output="${output}${i} ${prefixip}${index} ${prefixmac}${index}\n"
+ done
+ echo -e ${output} | column -c 80 -t
+}
+
+
+#
+# Everyone loves a main function
+#
+function main {
+ action=${1}
+ shift;
+
+ args=(${@})
+
+ if [[ ${action} == 'new' ]]; then
+ vm_new ${args[@]}
+ elif [[ ${action} == 'rm' ]]; then
+ vm_rm ${args[@]}
+ elif [[ ${action} == 'ls' ]]; then
+ vm_ls
+ elif [[ ${action} == '' ]]; then
+ log error "Please specify an action (new, rm, ls)"
+ exit 1
+ else
+ log error "Unknown action: ${action}"
+ exit 1
+ fi
+}
+
+main ${@}

Generated by cgit