summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Ball <nullspoon@iohq.net>2016-02-19 19:54:51 -0700
committerAaron Ball <nullspoon@iohq.net>2016-02-19 20:00:54 -0700
commit755aeab02ce534eb07b1e30ac2d91e6f09b27544 (patch)
treebabcf817a897cd35c5c415dec61d4e4c3b7a630c
downloadvmgr-755aeab02ce534eb07b1e30ac2d91e6f09b27544.tar.gz
vmgr-755aeab02ce534eb07b1e30ac2d91e6f09b27544.tar.xz
Initial commit
Currently builds out a vm. Assigns it a predictable mac address and hostname. Also inserts a predictable ip reservation into dnsmasq for the mac address. Part of the insertion process also adds dns entry, so hostnames will resolve between systems. Supports listing vms with ls action (vmgr ls). Output includes hostname hostname, ip, and mac address. Supports rm action to delete a virtual machine. The rm command will stop the vm, delete it, remove its storage, refresh the storage pool, and remove the vms ip address reservation.
-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