#!/bin/bash
#
# Copyright 2017 Red Hat, Inc.
#
# NAME
#     rht-vmctl - Red Hat Training virtual machine control utility
#
# SYNOPSIS
#     rht-vmctl -h|--help
#     rht-vmctl [-y|--yes] VMCMD VMNAME [DATETIME]
#     rht-vmctl [-i|--inquire] VMCMD VMNAME [DATETIME]
#
#     where VMCMD is one of the following:
#       view      - runs virt-viewer on VMNAME
#       start     - if stopped, start VMNAME (obtain and overlay image if
#                   not there)
#       stop      - if running, stop VMNAME
#       poweroff  - if running, poweroff VMNAME
#       reset     - poweroff, recreate/restore latest overlay, start
#       save      - stop, backup overlay, start (this is typically used as
#                   a checkpoint for sequential labs that build on each other)
#       restore   - stop, restore overlay (to DATETIME), start
#       listsaves - list the saves of VMNAME
#       status    - show status of VMNAME on this system
#       get       - download image, create overlay
#       remove    - remove VMNAME from system
#       fullreset - poweroff, delete images/all saves, redownload image, 
#                   recreate overlay, start
#                   (this subcommand is used when a bad save has been made)
#
# DESCRIPTION
#     This tool is used to manage the virtual machines on this
#     local hypervisor layer system.
#
#     File naming conventions:
#        $RHT_COURSE-$VMNAME.xml - libvirt VM XML definition
#        $RHT_COURSE-$VMNAME-vdZ.qcow2 - master image vdZ
#        $RHT_COURSE-$VMNAME-vdZ.ovl - overlay of vdZ
#        $RHT_COURSE-$VMNAME-vdZ.ovl-$SAVE - backup of overlay
#
# CHANGELOG
#   * Thu Sep  7 2017 Robert Locke <rlocke@redhat.com>
#   - add -y|--yes as replacement to -q|--quiet
#   * Mon Jun 26 2017 Robert Locke <rlocke@redhat.com>
#   - change failure behavior inside loops that are now inside functions
#   - add -i option to always ask confirmation
#   * Wed Jun 21 2017 Robert Locke <rlocke@redhat.com>
#   - virt-host-validate -q not working since 7.3 (possibly 7.2)
#   - fundamental logic change to pass "list" to each rht_*_vm function
#   * Wed Apr 19 2017 Robert Locke <rlocke@redhat.com>
#   - remove $VMNAME from .ssh/known_hosts on remove master disk
#   * Thu Jul 28 2016 Robert Locke <rlocke@redhat.com>
#   - add DEFINED to RUNNING, PULLING, MISSING in status
#   * Wed May 11 2016 Robert Locke <rlocke@redhat.com>
#   - add support for RHT_ENROLLMENT -ge 512
#   * Thu Mar  3 2016 Robert Locke <rlocke@redhat.com>
#   - change auto-add virtualport to tie to RHT_PRIVUSEOVS
#   * Wed Feb  3 2016 Robert Locke <rlocke@redhat.com>
#   - auto-add virtualport to network definitions if missing
#   * Thu Oct  2 2015 Robert Locke <rlocke@redhat.com>
#   - add a locking mechanism to get to prevent multiple executions
#   * Mon Jul  6 2015 Robert Locke <rlocke@redhat.com>
#   - rht-vmctl - shift virsh define to get
#   * Thu Jun 25 2015 Robert Locke <rlocke@redhat.com>
#   - shift save and restore to use rsync
#   - remove always does undefine
#   - add optional argument to save
#   * Mon Jun  8 2015 Robert Locke <rlocke@redhat.com>
#   - change "all" to be same on f0, introduce "everything"
#   * Mon Jun  1 2015 Robert Locke <rlocke@redhat.com>
#   - add --keep option for remove
#   * Fri May  1 2015 Robert Locke <rlocke@redhat.com>
#   - add verb listsaves and enhance restore
#   * Mon Jan 12 2015 Robert Locke <rlocke@redhat.com>
#   - add use of virt-host-validate to generate warning
#   * Thu Jan  8 2015 Robert Locke <rlocke@redhat.com>
#   - add support for .iso disks to be downloaded
#   * Wed Oct  9 2014 Robert Locke <rlocke@redhat.com>
#   - stop mixing layers - change retrieval to use f0, not classroom VM
#   * Tue Aug 19 2014 Robert Locke <rlocke@redhat.com>
#   - add curl failure generating error message
#   - add support for --gui option for later visualization improvements
#   * Thu Jul 10 2014 Robert Locke <rlocke@redhat.com>
#   - shift f0 infrastructure VMs from RHT_VMS to RHT_VM0
#   * Wed Jul  2 2014 Robert Locke <rlocke@redhat.com>
#   - add support for save/restore/fullreset of classroom
#   - add remove for classroom
#   - make remove prompt for confirmation (in case anyone finds it)
#   * Tue Jun 24 2014 Robert Locke <rlocke@redhat.com>
#   - add hidden "remove" verb
#   * Thu Jun 19 2014 Robert Locke <rlocke@redhat.com>
#   - add status verb
#   * Thu Jun 12 2014 Robert Locke <rlocke@redhat.com>
#   - virsh start generate error messages about missing virt/kvm
#   * Wed Jun 11 2014 Robert Locke <rlocke@redhat.com>
#   - add hidden "viewstart" verb (used by desktop icons)
#   * Tue May 20 2014 George Hacker <ghacker@redhat.com>
#   - cleaned up output for user experience
#   * Sun Apr 20 2014 Rob Locke <rlocke@redhat.com>
#   - original code

PATH=/usr/bin:/bin:/usr/sbin:/sbin

# Some variables
CONTENTSERVER=http://foundation0.ilt.example.com
VMBLKPATH=/var/lib/libvirt/images

# Source some variables that we need - should provide:
# RHT_VENUE=ilt
# RHT_ENROLLMENT=X
# RHT_COURSE=rh124
# RHT_ROLE=foundation
# RHT_VMTREE=rhel7.0/x86_64
# RHT_VMS="desktop server"
# RHT_VM0="classroom"
source /etc/rht
# Validate
if [[ -z "${RHT_ENROLLMENT}" || -z "${RHT_COURSE}" || -z "${RHT_VMS}" || -z "${RHT_VMTREE}" ]]; then
  echo "Error: missing a needed variable from /etc/rht"
  echo "RHT_ENROLLMENT = '${RHT_ENROLLMENT}'"
  echo "RHT_COURSE     = '${RHT_COURSE}'"
  echo "RHT_VMTREE     = '${RHT_VMTREE}'"
  echo "RHT_VMS        = '${RHT_VMS}'"
  exit 2
fi

# Set/adjust enrollment for RHT_ENROLLMENT -ge 512
enrollment=$((RHT_ENROLLMENT%512))

# Source library of functions
source /usr/local/lib/rhttool.shlib
# trap on_exit EXIT

# Additional functions for this shell script
function print_usage {
cat << EOF

This utility manages the Red Hat Training supplied VMs on the local
hypervisor.

Usage: rht-vmctl [-y|--yes] VMCMD VMNAME [DATETIME]
       rht-vmctl [-i|--inquire] VMCMD VMNAME [DATETIME]
       rht-vmctl -h|--help

  where VMCMD is one of:
    view       - launches console viewer of VMNAME
    start      - obtain and start up VMNAME
    stop       - stop a running VMNAME
    poweroff   - if running, force stop VMNAME
    reset      - poweroff, return to saved or original state, start VMNAME
    save       - stop, save image, start VMNAME (to DATETIME)
    restore    - poweroff, restore to save (to DATETIME), start VMNAME
    listsaves  - list the saves of VMNAME
    status     - display libvirt status of VMNAME
    get        - if not here, obtain VMNAME from server
    remove     - remove VMNAME from system
    fullreset  - poweroff, reobtain from server, start VMNAME (bad save/image)

  -i|--inquire - confirm each VMNAME first
  -y|--yes     - confirm nothing, just do it

  VMNAME of "all" processes all VMs available in the course
EOF
}

function get_latest {
  latest=$( ls -v $* 2>/dev/null | tail -n1 )
  echo ${latest}
}

function rht_drop_blk {
  local vmname=$1
  local vmblk=$2
  # Loop through QCOW2 disk definitions
  for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
    vdisk=${vdisk0%.ovl}
    debug "Dropping ${vdisk}-${vmblk}."
    if [ "${vmblk}" = "saves" ] ; then
      rm -f ${VMBLKPATH}/${vdisk}.ovl-*
    fi
    if [ "${vmblk}" = "overlay" ] ; then
      rm -f ${VMBLKPATH}/${vdisk}.ovl
    fi
    if [ "${vmblk}" = "master" ] ; then
      rm -f ${VMBLKPATH}/${vdisk}.ovl-*
      rm -f ${VMBLKPATH}/${vdisk}.ovl
      if [[ "${RHT_VM0}" != *${vmname}* ]] ; then
        rm -f ${VMBLKPATH}/${vdisk}.qcow2
      fi
    fi
  done
  # Loop through ISO disk definitions
  for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*iso ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
    vdisk=${vdisk0%.iso}
    debug "Dropping ${vdisk}-${vmblk}."
    if [ "${vmblk}" = "master" ] ; then
      if [[ "${RHT_VM0}" != *${vmname}* ]] ; then
        rm -f ${VMBLKPATH}/${vdisk0}
      fi
    fi
  done
  # If removing master, also undefine and remove xml file
  if [ "${vmblk}" = "master" ] ; then
    debug "Undefining ${vmname}."
    virsh undefine ${vmname} &> /dev/null
    if [[ "${RHT_VM0}" != *${vmname}* ]] ; then
      rm -f ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml
    fi
    sed -i -e "/^${vmname}/d" /root/.ssh/known_hosts 2>/dev/null
    sed -i -e "/^${vmname}/d" /home/kiosk/.ssh/known_hosts 2>/dev/null
  fi
}

function rht_get_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    lockfile=/tmp/.lock-rht-vmctl-${vmname}
    if is_running ${vmname} ; then
      debug "Not getting. ${vmname} is already running."
      continue
    fi
    # Punt if lockfile exists
    if [[ -f ${lockfile} ]]; then
      debug "Lockfile exists. Another ${vmname} get is in progress."
      echo "Lockfile exists. Another ${vmname} get is in progress."
      continue
    fi
    # Create lockfile
    touch ${lockfile}
    # If needed, get (and fix) libvirt XML definition file
    if [[ ! -f ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml ]]; then
      if [[ "${RHT_VM0}" == *${vmname}* ]] ; then
        echo "Error: missing libvirt XML definition - ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml"
        rm -f ${lockfile} &>/dev/null
        exit 12
      fi
      echo "Downloading virtual machine definition file for ${vmname}."
      if ! curl -# -f -o ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml \
           ${CONTENTSERVER}/${RHT_VMTREE}/vms/${RHT_COURSE}-${vmname}.xml ; then
        echo "Error: Unable to download XML definition - ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml"
        rm -f ${lockfile} &>/dev/null
        exit 12
      fi
    fi
    # Need to convert MAC address to match RHT_ENROLLMENT
    # Shift disk(s) to use overlay file(s)
    RHT_ENROLLMENT_HEX=$(printf %02X ${enrollment})
    sed -i -e "s/RHT_ENROLLMENT_HEX/${RHT_ENROLLMENT_HEX}/" \
           -e "s/\.qcow2/.ovl/" ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml
    # Auto-add virtualport to private networks
    if [[ "${RHT_PRIVUSEOVS}" == "yes" ]]; then
      if ! grep openvswitch ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml &>/dev/null ; then
        sed -i -e '/privbr0/a \\t\t\t\<virtualport type="openvswitch"/\>' \
               -e '/virbr1/a \\t\t\t\<virtualport type="openvswitch"/\>' \
            ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml
      fi
    fi
    # Loop through QCOW2 disk definitions
    for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
      vdisk=${vdisk0%.ovl}
      # If needed, retrieve qcow2 file
      if [[ ! -f ${VMBLKPATH}/${vdisk}.qcow2 ]]; then
        if [[ "${RHT_VM0}" == *${vmname}* ]] ; then
          echo "Error: missing master QCOW2 image - ${VMBLKPATH}/${vdisk}.qcow2"
          rm -f ${lockfile} &>/dev/null
          exit 12
        fi
        # Since we are retrieving, make sure the overlays are also gone
        rm -f ${VMBLKPATH}/${vdisk}.ovl*
        echo "Downloading virtual machine disk image ${vdisk}.qcow2"
        if ! curl -# -f -o ${VMBLKPATH}/${vdisk}.qcow2 \
             ${CONTENTSERVER}/${RHT_VMTREE}/vms/${vdisk}.qcow2 ; then
          echo "Error: Unable to download image - ${VMBLKPATH}/${vdisk}.qcow2"
          rm -f ${lockfile} &>/dev/null
          exit 12
        fi
      fi
      # If needed, re-create or restore overlay file
      if [[ ! -f ${VMBLKPATH}/${vdisk}.ovl ]]; then
        ovlrestore=$(get_latest ${VMBLKPATH}/${vdisk}.ovl-*)
        # If there is no save, create overlay from master
        if [[ -z "${ovlrestore}" ]]; then
          echo "Creating virtual machine disk overlay for ${vdisk}.qcow2"
          qemu-img create -q -f qcow2 -b ${VMBLKPATH}/${vdisk}.qcow2 ${VMBLKPATH}/${vdisk}.ovl
        # else restore latest save
        else
          echo "Restoring from ${ovlrestore}"
          cp ${ovlrestore} ${VMBLKPATH}/${vdisk}.ovl
        fi
      fi
    done
    # Loop through ISO disk definitions
    for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*iso ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
      # If needed, retrieve iso file
      if [[ ! -f ${VMBLKPATH}/${vdisk0} ]]; then
        if [[ "${RHT_VM0}" == *${vmname}* ]] ; then
          echo "Error: missing ISO image - ${VMBLKPATH}/${vdisk0}"
          rm -f ${lockfile} &>/dev/null
          exit 12
        fi
        echo "Downloading virtual machine disk image ${vdisk0}"
        if ! curl -# -f -o ${VMBLKPATH}/${vdisk0} \
             ${CONTENTSERVER}/${RHT_VMTREE}/vms/${vdisk0} ; then
          echo "Error: Unable to download ISO image - ${VMBLKPATH}/${vdisk0}"
          rm -f ${lockfile} &>/dev/null
          exit 12
        fi
      fi
    done
    # if not libvirt defined ; then define it
    if ! is_defined ${vmname} ; then
      debug "Defining ${vmname} from its xml file."
      virsh define ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml &> /dev/null
    fi
    # Remove lockfile since get is done
    rm -f ${lockfile} &>/dev/null
  done
}

function rht_start_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    if is_running ${vmname} ; then
      echo  "Error: ${vmname} not started (is already running)"
      continue
    fi
    rht_get_vm ${vmname}
    # if not libvirt defined ; then define it
    if ! is_defined ${vmname} ; then
      debug "Defining ${vmname} from its xml file."
      virsh define ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml &> /dev/null
    fi
    if [[ -z "${NOSTART}" ]]; then
      # Punt if this VM should not be started (unless explicit)
      if [[ -n "${VMMULTI}" && "${RHT_NOSTARTVMS}" == *${vmname}* ]]; then
        echo "Not starting ${vmname}."
      else
        echo "Starting ${vmname}."
        virsh -q start ${vmname} > /dev/null
        # If we are on foundation0, set autostart if infrastructure VMs
        if [[ "${RHT_VM0}" == *${vmname}* ]]; then
          debug "Configuring ${vmname} to autostart."
          virsh autostart ${vmname} &> /dev/null
        fi
      fi
    fi
  done
}

function rht_stop_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    echo -n "Stopping ${vmname}."
    # Cap how long we are going to wait
    i=${RHT_STOPTIMEVMS:-20}
    while is_running ${vmname} && [[ ${i} -gt 0 ]]; do
      virsh shutdown ${vmname} &> /dev/null
      sleep 5
      let i=i-1
      echo -n '.'
    done
    echo
    if is_running ${vmname} ; then
      QUIET=yes rht_poweroff_vm ${vmname}
    fi
    if is_running ${vmname} ; then
      echo "Error: unable to stop ${vmname}."
      echo 'Try manually shutting it down then try again.'
      continue
    fi
  done
}

function rht_poweroff_vm {
  local vmlist="$*"
  if confirm_yn "Are you sure you want to poweroff ${vmlist}?"; then
    for vmname in ${vmlist} ; do
      echo -n "Powering off ${vmname}."
      # Cap how long we are going to wait
      i=5
      while is_running ${vmname} && [[ ${i} -gt 0 ]]; do
        virsh destroy ${vmname} &> /dev/null
        sleep 5
        let i=i-1
        echo -n '.'
      done
      echo
      if is_running ${vmname} ; then
        echo "Error: unable to poweroff ${vmname}."
        echo 'Try manually stopping it then try again.'
        continue
      fi
    done
  fi
}

function rht_reset_vm {
  local vmlist="$*"
  if confirm_yn "Are you sure you want to reset ${vmlist}?"; then
    for vmname in ${vmlist} ; do
      QUIET=yes rht_poweroff_vm ${vmname}
    done
    for vmname in ${vmlist} ; do
      echo "Resetting ${vmname}."
      rht_drop_blk ${vmname} overlay
      lockfile=/tmp/.lock-rht-vmctl-${vmname}
      rm -f ${lockfile} &>/dev/null
      rht_start_vm ${vmname}
    done
  fi
}

function rht_fullreset_vm {
  local vmlist="$*"
  if confirm_yn "Are you sure you want to full reset ${vmlist}?"; then
    for vmname in ${vmlist} ; do
      QUIET=yes rht_poweroff_vm ${vmname}
    done
    for vmname in ${vmlist} ; do
      echo "Full resetting ${vmname}."
      rht_drop_blk ${vmname} master
      lockfile=/tmp/.lock-rht-vmctl-${vmname}
      rm -f ${lockfile} &>/dev/null
      rht_start_vm ${vmname}
    done
  fi
}

function rht_remove_vm {
  local vmlist="$*"
  if confirm_yn "Are you sure you want to remove ${vmlist}?"; then
    for vmname in ${vmlist} ; do
      if [[ -z "${NODELETE}" ]]; then
        QUIET=yes rht_poweroff_vm ${vmname}
        echo "Removing ${vmname}."
        rht_drop_blk ${vmname} master
      else
        rht_stop_vm ${vmname}
        debug "Undefining ${vmname}."
        virsh undefine ${vmname} &> /dev/null
        sed -i -e "/^${vmname}/d" /root/.ssh/known_hosts 2>/dev/null
        sed -i -e "/^${vmname}/d" /home/kiosk/.ssh/known_hosts 2>/dev/null
      fi
      lockfile=/tmp/.lock-rht-vmctl-${vmname}
      rm -f ${lockfile} &>/dev/null
    done
  fi
}

function rht_save_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    if ! is_defined ${vmname} ; then
      echo "Error: ${vmname} is not defined - nothing to save."
    else
      rht_stop_vm ${vmname}
      echo "Saving ${vmname} disk image."
      # Loop through disk definitions
      if [[ -z "${VMOVL}" ]]; then
        VMOVL=$(date +%Y%m%d%H%M)
      fi
      for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
        rsync ${VMBLKPATH}/${vdisk0} ${VMBLKPATH}/${vdisk0}-${VMOVL}
      done
      rht_start_vm ${vmname}
    fi
  done
}

function rht_listsaves_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    if ! is_defined ${vmname} ; then
      echo "Error: ${vmname} is not defined - nothing to list."
    else
      for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
        vdisk=${vdisk0%.ovl}
        echo "Saves for ${vdisk}:"
        ovllist=$(ls -v ${VMBLKPATH}/${vdisk}.ovl-* 2>/dev/null)
        if [[ -z "${ovllist}" ]]; then
          echo "   Error: No saves."
        else
          for save in ${ovllist} ; do
            echo "   "${save#*.ovl-}
          done
        fi
        return
      done
    fi
  done
}

function rht_restore_vm {
  local vmlist="$*"
  if confirm_yn "Are you sure you want to restore ${vmlist}?"; then
    for vmname in ${vmlist} ; do
      if ! is_defined ${vmname} ; then
        echo "Error: ${vmname} is not defined - nothing to restore."
      else
        # If not specified, calculate most recent save
        if [[ -z "${VMOVL}" ]]; then
          for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
            vdisk=$(get_latest ${VMBLKPATH}/${vdisk0}-*)
            VMOVL=${vdisk#*.ovl-}
            break
          done
        fi
        # Punt if no saves specified or found
        if [[ -z "${VMOVL}" ]]; then
          echo "Error: ${vmname} does not have a save - nothing to restore"
          return
        fi
        # Validate VMOVL: Confirm there is a save for each disk and abort if not
        for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
          if [[ ! -f "${VMBLKPATH}/${vdisk0}-${VMOVL}" ]]; then
            echo "Error: ${vmname} missing save ${vdisk0}-${VMOVL}."
            return
          fi
        done
        # Perform restore
        QUIET=yes rht_poweroff_vm ${vmname}
        echo "Restoring disk image for ${vmname} to ${VMOVL}."
        # Loop through disk definitions and restore
        for vdisk0 in $( grep -o ${RHT_COURSE}-${vmname}-.*ovl ${VMBLKPATH}/${RHT_COURSE}-${vmname}.xml 2>/dev/null ) ; do
          rsync ${VMBLKPATH}/${vdisk0}-${VMOVL} ${VMBLKPATH}/${vdisk0}
        done
        rht_start_vm ${vmname}
      fi
    done
  fi
}

function rht_view_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    if is_running ${vmname} ; then
      virt-viewer ${vmname} &> /dev/null &
    else
      echo "Error: unable to view ${vmname} - not currently running."
    fi
  done
}

function rht_viewstart_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    if ! is_running ${vmname} ; then
      rht_start_vm ${vmname}
    fi
    rht_view_vm ${vmname}
  done
}

function rht_status_vm {
  local vmlist="$*"
  for vmname in ${vmlist} ; do
    lockfile=/tmp/.lock-rht-vmctl-${vmname}
    if is_running ${vmname} ; then
      echo -e "${vmname} \\033[1;32mRUNNING\\033[0;39m"
    elif is_defined ${vmname} ; then
      echo -e "${vmname} \\033[1;33mDEFINED\\033[0;39m"
    elif [[ -f ${lockfile} ]]; then
      echo -e "${vmname} \\033[1;35mPULLING\\033[0;39m"
    else
      echo -e "${vmname} \\033[1;31mMISSING\\033[0;39m"
    fi
  done
}

# Main area
# If I need root privileges to run, then use sudo to call myself
if [[ "${EUID}" -gt "0" ]] ; then
  debug 'Using sudo to become root.'
  sudo $0 "$@"
  exit
fi
# Parse options
ARGS=$(getopt -o hqyqidnkg -l "help,quiet,yes,quiet,inquire,debug,nostart,keep,gui" -n "rht-vmctl" -- "$@")
if [[ $? -ne 0 ]]; then
  print_usage
  exit 1
fi
eval set -- "${ARGS}"
while true ; do
  case $1 in
    -h|--help)
      print_usage ; exit 0 ;;
    -y|--yes|-q|--quiet)
      # confirm_yn() checks for this variable
      QUIET=yes ; shift ;;
    -i|--inquire)
      # force individual inquire before doing
      INQUIRE=yes ; shift ;;
    -d|--debug)
      # debug() checks for this variable
      DEBUG=true ; shift ;;
    -n|--nostart)
      # do not start VM (just do whatever else was requested)
      NOSTART=yes ; shift ;;
    -k|--keep)
      # do not delete files (just do whatever else was requested)
      NODELETE=yes ; shift ;;
    -g|--gui)
      # identify whether we are launched from GUI
      GUI=yes ; shift ;;
    --)
      shift ; break ;;
    *)
      echo "Error: bad option, $1" ; print_usage ; exit 1 ;;
  esac
done
# Process arguments
VMCMD=""
VMNAME=""
VMMULTI=""
VMOVL=""
if [[ -n "$1" ]]; then
  case $1 in
    start|stop|poweroff|reset|fullreset|listsaves|get|view|viewstart|status|remove)
      VMCMD=$1
      VMNAME=$2
      ;;
    save|restore)
      VMCMD=$1
      VMNAME=$2
      VMOVL=$3
      ;;
    *)
      print_usage
      exit 1
      ;;
  esac
fi
# Validation
if [[ -z "${VMCMD}" || -z "${VMNAME}" ]]; then
  echo "Error: missing subcommand or VMNAME."
  print_usage
  exit 1
fi
# Validate VMNAME
match=0
my_vms=${VMNAME}
if [[ "${VMNAME}" == "everything" ]]; then
  [[ -n "${RHT_VM0}" ]] && my_vms="${RHT_VM0} ${RHT_VMS}" || my_vms="${RHT_VMS}"
  VMMULTI=${VMNAME}
  match=1
elif [[ "${VMNAME}" == "all" ]]; then
  my_vms="${RHT_VMS}"
  VMMULTI=${VMNAME}
  match=1
else
  for i in ${RHT_VMS} ${RHT_VM0} ; do
    [[ "${i}" == "${VMNAME}" ]] && match=1
  done
fi
if [[ ${match} -eq 0 ]]; then
  echo "Error: unrecognized VMNAME specified, ${VMNAME}."
  print_usage
  exit 1
fi

# Abort if virtualization does not validate
virttest=$(virt-host-validate 2>&1 | grep FAIL)
if [[ -n "${virttest}" ]]; then
  echo "Error: Failure in validating virtualization capabilities"
  echo "${virttest}"
  exit 101
fi

#for i in ${my_vms} ; do
#  # Branch based on request
#  debug "We will ${VMCMD} ${i}."
#  rht_${VMCMD}_vm ${i}
#done

# If INQUIRE is mandated, confirm/edit list, removing those that are no
if [[ -n "${INQUIRE}" ]]; then
  new_vms=
  QUIET=
  for vmname in ${my_vms}; do
    if confirm_yn "Are you sure you want to ${VMCMD} ${vmname}?"; then
      new_vms="${new_vms} ${vmname}"
    fi
  done
  my_vms="${new_vms}"
  QUIET=yes
fi

# Call the verb operating on the "set" of VMs
if [[ -n "${my_vms}" ]]; then
  rht_${VMCMD}_vm ${my_vms}
fi
