#!/bin/bash

show_help() {
  c="  `basename $0`"
  echo "Usage:"
  echo "$c help                         # this help"
  echo "$c restart|reboot|cad name      # restart or reboot(ctrl-alt-delete)"
  echo "$c shutdown|stop [name|--all]   # shutdown and sync"
  echo "$c start [name|--all]           # sync and start"
  echo "$c rename name newname          # rename guest"
  echo "$c migrate name host [--live]   # live migrate to host"
  echo "$c move name host [newname]     # offline move guest"
  echo "$c save guest                   # save guest"
  echo "$c list|l|la [--all]            # list guests"
  echo "$c info|i|ii|ia [--inactive] [name] # get information about guest"
  echo "$c dump|d                       # dump XML information"
  echo "$c vol-list|vol [--save] storage # list volumes for storage"
  echo "$c edit|e name [-r RAM|-c CPU|-b device] # edit guest"
  echo "$c define file.xml|-            # define a new guest"
  echo "$c set [mem|cpu|boot] guest PARAM # set guest parameters"
  echo "$c attach-disk guest src dst    # attach disk"
  echo "$c detach-disk guest dst        # detach disk"
  echo "$c resize-disk guest dst [size]	# resize disk"
  echo "$c console|con|c [name|--all]   # console for guest"
  echo "$c diff name1 name2             # diff virtual machine configs"
  echo "$c destroy name                 # destroy guest"
  echo "$c reset name                   # destroy and start again"
  echo "$c kill name                    # kill qemu guest process"
  echo "$c install name GiB_ram cpus [-n vlan|-N network] -d disk [-o rhel9.0] # install guest"
  echo "$c add guest                    # add hardware"
  echo "$c magic guest keys...          # send magic keys"
  echo "$c fix guest [pci]              # fix guest config (centos<->fedora)"
  echo "$c addlease guest lockspace resource # add lease for guest"
  echo "$c backup guest -d -u -c -a [p1 p2] # backup guest"
  echo "$c restore dir/filename.gz > /dev/...  # restore from backup"
  echo "$c pool-list|pool-start|pool-destroy # pool commands"
  echo "$c dd src_device dst_device     # copy data"
  exit 0
}

export VOL_INFO_DIR=/tmp/vs_vols

drop_caches() {
  sync
  if [ -w /proc/sys/vm/drop_caches ]; then
    echo 3 > /proc/sys/vm/drop_caches
  fi
  sync
  if [ "$1" ]; then
    sleep $1
  fi
}

list_guests() {
  virsh list --name "$@"
}

console_guest() {
  if [ "$1" = "--all" ]; then
    for guest in `list_guests`; do
      virsh console "$guest"
    done
  else
    virsh console "$@"
  fi
}

shutdown_guests() {
  if [ "$1" = "--all" ]; then
    for guest in `list_guests`; do
      virsh send-key "$guest" --holdtime 200 KEY_LEFTSHIFT
      sleep 1
      virsh shutdown "$guest"
    done
    for guest in `list_guests`; do
      echo -e "\n\n\n---------- WAITING FOR $guest ----------\n\n\n"
      virsh console "$guest"
      virsh list
    done
    drop_caches 1s
  else
    for guest in "$@"; do
      if [ "`virsh domstate $guest`" = "running" ]; then
        echo "virsh shutdown $guest"
        virsh send-key "$guest" --holdtime 200 KEY_LEFTSHIFT
        sleep 0.1
        virsh shutdown "$guest"
        virsh console "$guest"
      else
        echo "Guest is not running: $guest"
      fi
    done
    drop_caches 1s
    echo "Guest shut off: $guest"
  fi
}

restart_guest() {
  shutdown_guests "$1"
  virsh start "$1" --console
  exit 0
}

cad_guest() {
  virsh send-key "$1" KEY_LEFTCTRL KEY_LEFTALT KEY_DELETE
  virsh console "$1"
  exit 0
}

start_guest() {
  drop_caches 1s
  if [ "$1" = "--all" ]; then
    shift
    for guest in `list_guests --all`; do
      if [ "${guest%_s}" = "$guest" ]; then
        # do not start guests with "_s" suffix
        virsh start "$guest" "$@"
      fi
    done
  else
    virsh start $1 --console
  fi
}

kill_guest() {
  pid=""
  for procpid in /proc/[0-9]*; do
    if tr '\0' '|' < $procpid/cmdline | awk '
      BEGIN { rc=1 }
      /qemu.*\|-name\|'$1'/ { rc=0 }
      END { exit rc }
      '
    then
      if [ -z "$pid" ]; then
        pid=`basename $procpid`
      else
        pid="duplicate"
      fi
    fi
  done
  if [ -f "/proc/$pid/cmdline" ]; then
    tr '\0' ' ' < /proc/$pid/cmdline
    echo ""
    echo -n "Really kill pid $pid [y/n]? "
    read answer
    if [ "$answer" = "y" -o "$answer" = "yes" ]; then
      kill -9 $pid
    fi
  fi
}

rename_guest() {
  if [ "`virsh domstate $1`" = "shut off" ]; then
    #TMP=`mktemp`
    #virsh dumpxml $1 --inactive --security-info > $TMP
    #sed -i "s|<name>$1</name>|<name>$2</name>|" $TMP
    #virsh undefine $1
    #virsh define $TMP
    #rm -f $TMP
    virsh domrename "$1" "$2"
  else
    echo "ERROR: Unable to rename a running guest!"
  fi
}

move_guest() {
  virsh shutdown $1
  virsh console $1
  drop_caches 1s
  if [ "$3" ]; then
    M="$3"
  else
    M="$1"
  fi
  ssh root@$2 virsh start $M --console
}

virsh_list() {
  DOM_IDS=`list_guests $@`
  TOTAL_KB=`grep ^MemTotal: /proc/meminfo | awk '{ print $2 }'`
  export TOTAL_KB
  for domain in $DOM_IDS; do
    virsh dumpxml $domain
  done | awk -F"[<>]" '
    BEGIN {
      sum_memory = 0
      sum_current = 0
      total_kb = ENVIRON["TOTAL_KB"]
      cnt = 0
      autostart_cnt = 0
      printf("%-20s  %16s  %16s  %7s  %9s\n",
             "Name", "Memory", "Current Memory", "Running", "Autostart")
    }
    /^<domain / { running = "no" }
    /^<domain .* id=/ { running = "YES" }
    /^ *<name>/ {
      name = $3
      fn = "/etc/libvirt/qemu/autostart/" name ".xml"
      astart = ""
      getline astart <fn
      if (astart!="") {
        autostart = "YES"
      } else {
        autostart = "no"
      }
      # update running/paused state
      if (running=="YES") {
        "virsh domstate "name | getline state
        if (state!="running") running = state
      }
    }
    /^ *<memory/ { memory = $3; sum_memory += $3 }
    /^ *<currentMemory/ {
      current = $3
      sum_current += $3
      if (running == "YES") cnt += 1
      if (autostart == "YES") autostart_cnt += 1
      printf("%-20s %14.2f MB %14.2f MB  %7s  %9s\n",
             name, memory/1024, current/1024, running, autostart)
    }
    END {
      printf("----------------------------------------------------------------------------\n")
      printf("%-20s %14.2f MB %14.2f MB  %7d  %9d\n",
             "TOTAL:", sum_memory/1024, sum_current/1024,
             cnt, autostart_cnt)
      printf("%-20s %14.2f MB %14.2f MB\n",
             "FREE:", (total_kb-sum_memory)/1024, (total_kb-sum_current)/1024)
    }'
}

virsh_info() {
  DOM_IDS=""
  DUMP_OPTS=""
  LIST_OPTS=""
  export LIST_BRIEF=""
  for domain in $@; do
    if [ "${domain::2}" = "--" ]; then
      if [ "$domain" = "--all" ]; then
        LIST_OPTS="$domain"
      elif [ "$domain" = "--brief" ]; then
        export LIST_BRIEF="1"
      else
        DUMP_OPTS="$DUMP_OPTS ${domain}"
      fi
    else
      DOM_IDS="$DOM_IDS $domain"
    fi
  done
  if [ "$DOM_IDS" = "" ]; then
    DOM_IDS=`virsh list $LIST_OPTS | awk '/^ *[0-9-]/ { print $2 }'`
  fi
  for domain in $DOM_IDS; do
    virsh dumpxml $DUMP_OPTS $domain
  done | awk -F"[<>]" '
    function get0(i) {
      split($0, a, "'\''")
      return a[i]
    }
    function geta(name) {
      split($0, a, "(<[a-z]+ |/?>)")
      split(a[2], b, " ")
      for (item in b) {
        gsub("'\''", "", b[item])
        split(b[item], c, "=")
        key = c[1]
        value = c[2]
        if (key==name) return value
      }
      return ""
    }
    function file_exist(fn) {
      rc = system("test -e " fn)
      return rc==0
    }
    function disk_size(fn) {
      "blockdev --getsz "fn" 2>/dev/null #"name | getline size
      if (size==0) return "found"
      size /= 2048
      total_disk_size += size
      if (size>=1024) return sprintf("%5.3f GiB", size/1024)
      return sprintf("%5.3f MB", size)
    }
    function get_alias(fn) {
      alias=""
      "cat $VOL_INFO_DIR/`basename "fn"` 2>/dev/null #"name | getline alias
      return alias
    }
    function rpm_version(fn) {
      "rpm -q --qf %{NAME}-%{VERSION} -f "fn" #"name | getline rpmver
      return rpmver
    }
    function cpu_gen(cpu_model) {
      if (cpu_model~"(Westmere|Nehalem)") return " [GenPrev]"
      if (cpu_model~"SandyBridge") return " [Gen2/3]"
      if (cpu_model~"Haswell") return " [Gen4]"
      if (cpu_model~"Broadwell") return " [Gen5]"
      if (cpu_model~"Skylake") return " [Gen6-10]"
      if (cpu_model~"Icelake") return " [Gen10]"
      return ""
    }
    function with_missing(format, value, missing) {
      ret = sprintf(format, value)
      for (i=0; i<missing; i++)
        ret = ret "!"
      return ret
    }
    function print_verbose(s) {
      if (ENVIRON["LIST_BRIEF"]=="")
        printf(s)
    }
    BEGIN {
      section = ""
      if (ENVIRON["LIST_BRIEF"]=="1") {
        printf("%-20s CPU   Memory GiB     HDD GiB     Networks\n", "Name")
      }
    }
    /^ *<name>/ {
      print_verbose($3 "\n")
      name = $3
      bootdev = ""
    }
    /^ *<cpu / {
      section = "cpu"
      cpu_mode = geta("mode")
    }
    /^ *<\/cpu>/ { section="" }
    /^ *<model / && section=="cpu" { cpu_model=$3 }
    /^ *<vcpu/ { cpu_cores=$3 }
    /^ *<devices>/ {
      if (!cpu_model) cpu_model="hypervisor-default"
      if (cpu_mode=="host-model") cpu_model="host-model"
      cpu_model = cpu_model cpu_gen(cpu_model)
      print_verbose(sprintf( \
        "\tCPU:\t%s core(s), model: %s\n", cpu_cores, cpu_model \
      ))
    }
    /^ *<memory/ { memory = $3 }
    /^ *<emulator>/ {
      if (file_exist($3)) {
        emulator_status = "[" rpm_version($3) "]"
      } else {
        emulator_status = "[MISSING]"
      }
      print_verbose(sprintf("\tEmulator: %s %s\n", $3, emulator_status))
    }
    /^ *<currentMemory/ {
      current = $3
      if (current==memory)
        print_verbose(sprintf("\tRAM:\t%1.3f GiB\n", memory/1024/1024))
      else
        print_verbose(sprintf("\tRAM:\t%1.3f / %1.3f GiB\n", current/1024/1024, memory/1024/1024))
    }
    /^ *<disk / {
      disk_status = ""
      disk_alias = ""
      cache = ""
      discard = ""
      format = ""
      vd_io = ""
      boot_order = ""
    }
    /^ *<disk .* device=.?cdrom/ {
      disk_path = "cdrom"
    }
    /^ *<driver / {
      cache = geta("cache")
      discard = geta("discard")
      format = geta("type")
      vd_io = geta("io")
    }
    /^ *<source (file|dev)=/ {
      disk_path = get0(2)
      if (file_exist(disk_path)) {
        disk_status = disk_size(disk_path)
        disk_alias = get_alias(disk_path)
      } else {
        disk_status = "MISSING"
        total_disks_missing += 1
      }
    }
    /^ +<target dev=/ {
      target = geta("dev")
      bus = geta("bus")
    }
    /^ +<boot order=/ {
      boot_order = geta("order")
    }
    /<\/disk>/ {
      print_verbose(sprintf("\tDisk:\t%-10s %s [%s]\n", target, disk_path, disk_status))
      if (disk_alias!="")
        print_verbose(sprintf("\t\tlun:       %s\n", disk_alias))
      print_verbose(sprintf("\t\tformat=%s, bus=%s, boot_order=%s\n",
             format, bus, boot_order))
      print_verbose(sprintf("\t\tcache=%s, discard=%s, io=%s\n", \
             cache, discard, vd_io))
    }
    /^ *<interface type=/ {
      section = "interface"
      interface_type = geta("type")
    }
    /^ *<source .*=/ {
      source = get0(2)
    }
    /^ *<mac address=/ {
      mac_address = geta("address")
    }
    /^ *<model / && section=="interface" { interface_model=geta("type") }
    /<\/interface>/ {
      section = ""
      if (interface_type=="bridge") {
        if (file_exist("/sys/class/net/" source)) {
          bridge_status = "[found]"
        } else {
          bridge_status = "[MISSING]"
          total_networks_missing += 1
        }
        print_verbose(sprintf("\tBridge:\t%-10s %s %s\n", source, mac_address, bridge_status))
      } else if (interface_type=="network") {
        print_verbose(sprintf("\tNet:\t%-10s %s\n", source, mac_address))
      } else {
        print_verbose(sprintf("\tUNKNOWN:\t%s", mac_address))
      }
      print_verbose(sprintf("\t\tmodel=%s, boot_order=%s\n", interface_model, boot_order))
      total_networks += 1
    }
    /^ *<boot dev=/ {
      if (bootdev) {
        bootdev=bootdev", "get0(2)
      } else {
        bootdev=get0(2)
      }
    }
    /^ *<emulator>/ {
      emulator = $3
    }
    /^<domain/ {
      total_disk_size = 0
      total_disks_missing = 0
      total_networks = 0
      total_networks_missing = 0
    }
    /^<\/domain>/ {
      if (boot_order=="")
        print_verbose(sprintf("\tBoot:\t%s\n", bootdev))
      if (ENVIRON["LIST_BRIEF"]=="1") {
        printf("%-20s %3d  %-14s  %-10s  %2s\n", \
          name, cpu_cores, \
          sprintf("%6.1f/%3.1f", current/1048576, memory/1048576), \
          with_missing("%7.1f", total_disk_size/1024, total_disks_missing), \
          with_missing("%d", total_networks, total_networks_missing) \
        )
      }
    }
  '
}

diff_guests() {
  g1=`mktemp /tmp/$1,XXXXXXXXXX`
  g2=`mktemp /tmp/$2,XXXXXXXXXX`
  virsh dumpxml $1 >> $g1
  virsh dumpxml $2 >> $g2
  diff -u $g1 $g2
  rm -f $g1 $g2
}

edit_guest() {
  guest="$1"
  if [ "$2" = "-r" ]; then
    g1=`mktemp /tmp/$guest,1,XXXXXXXXXX`
    if [ -x /usr/bin/dc -o /bin/dc ]; then
      R=`echo $3 1048576 '*' p | dc | sed 's/\..*$//'`
    else
      R=$(($3*1048576))
    fi
    virsh dumpxml $guest --inactive --security-info \
      | sed 's|<memory[^>]*>[0-9]*</memory>|<memory>'$R'</memory>|' \
      | sed 's|<currentMemory[^>]*>[0-9]*</currentMemory>|<currentMemory>'$R'</currentMemory>|' \
      > $g1
    head $g1
    # set currentMemory for running guest
    virsh setmem $guest $R || true
    EDITOR="/bin/cp $g1" virsh edit $guest
    rm -f $g1
  elif [ "$2" = "-c" ]; then
    g1=`mktemp /tmp/$guest,1,XXXXXXXXXX`
    if [ "$3" = "host" ]; then
      virsh dumpxml $guest --inactive --security-info \
        | awk '
          /<cpu .*>/ {
            skip=1
            ap = "'\''"
            print "<cpu mode="ap"host-model"ap" check="ap"partial"ap">"
            print "  <model fallback="ap"allow"ap"/>"
          }
          /<\/cpu>/ { skip=0 }
          skip==1 { next }
          { print }
        ' > $g1
    else
      virsh dumpxml $guest --inactive --security-info \
        | sed 's|<vcpu\( [^>]*\)\?>[0-9]*</vcpu>|<vcpu\1>'$3'</vcpu>|' \
        > $g1
    fi
    EDITOR="/bin/cp $g1" virsh edit $guest
    rm -f $g1
  elif [ "$2" = "-b" ]; then
    g1=`mktemp /tmp/$guest,1,XXXXXXXXXX`
    shift 2
    bootdevs=""
    for i in "$@"; do
      if [ "$i" = "ipxe" ]; then
        bootdevs="$bootdevs  <kernel>/var/lib/libvirt/images/ipxe.lkrn</kernel>\n"
      else
        bootdevs="$bootdevs  <boot dev='$i'/>\n"
      fi
    done
    virsh dumpxml $guest --inactive --security-info \
      | grep -v '^ *<boot dev=' \
      | sed "s|</os>|$bootdevs  </os>|" \
      > $g1
    #grep -e 'os>' -e '<boot' $g1
    EDITOR="/bin/cp $g1" virsh edit $guest
    rm -f $g1
  else
    virsh edit $guest
  fi
  virsh_info --inactive $guest
}

install_guest() {
  # guest installation
  # name ram cpus ...
  cr=`rpm -q --qf='%{version}' centos-release` # centos7
  if [ $? = 0 ]; then
    osrel="rhel$cr"
  else
    cr=`rpm -q --qf='%{version}' centos-linux-release` # centos8
    if [ $? = 0 ]; then
      osrel="rhel$cr"
    else
      cr=`rpm -q --qf='%{version}' almalinux-release` # almalinux8
      if [ $? = 0 ]; then
        osrel="rhel$cr"
      else
        fr=`rpm -q --qf='%{version}' fedora-release` # fedora
        osrel="fedora$fr"
      fi
    fi
  fi
  NAME="$1"
  ARGS="--name=$1 --ram=$(($2*1024)) --vcpus=$3"
  shift 3
  while getopts "n:b:N:d:o:" opt; do
    case $opt in
      [nb])
        #ARGS="$ARGS --bridge $OPTARG"
        ARGS="$ARGS --network bridge=$OPTARG"
      	;;
      N)
        ARGS="$ARGS --network network=$OPTARG"
        ;;
      d)
        ARGS="$ARGS --disk $OPTARG,cache=none,format=raw"
        ;;
      o)
        osrel="$OPTARG"
        ;;
      \?)
        ARGS="$ARGS $OPTARG"
        ;;
    esac
  done
  virt-install \
    --os-type=linux --os-variant=$osrel \
    --accelerate --vnc --pxe \
    $ARGS
  virsh suspend "$NAME"
  virsh_info "$NAME"
  echo "Press ENTER to continue ..."
  read enter_key
  virsh resume "$NAME"
  virsh console "$NAME"
}

add_to_guest() {
  if [ "$1" = "agent" ]; then
    TMP=`mktemp`
    virsh dumpxml --inactive --security-info $2 > $TMP
    sed -i -f - $TMP << EOF
/<devices>/{
a<channel type='unix'>
a<source mode='bind' path='/var/lib/libvirt/qemu/$2.agent'/>
a<target type='virtio' name='org.qemu.guest_agent.0'/>
a</channel>
}
EOF
    virsh define $TMP
    rm -f $TMP
  else
    echo "Unknown hardware. Currently only 'agent' is supported."
  fi
}

send_magic_keys() {
  guest=$1
  shift
  if [ -z "$1" ]; then
    echo "Missing key. Use one of:"
    echo "    Actions:"
    echo "        s - sync"
    echo "        u - umount filesystems read-only"
    echo "        o - shut off the system"
    echo "        b - reboot"
    echo "    Kill processes:"
    echo "        f - OOM kill"
    echo "        e - send SIGTERM except init"
    echo "        i - send SIGKILL except init"
    echo "    Display information:"
    echo "        d - display locks"
    echo "        l - show backtrace"
    echo "        m - output memory information"
    echo "        t - list all tasks"
    echo "        w - display blocked tasks"
    echo "        z - dump trace buffers"
    echo "See https://en.wikipedia.org/wiki/Magic_SysRq_key for more info."
    exit 1
  fi
  for key in $@; do
    echo -n "Sending key '$key' ..."
    virsh send-key $guest KEY_LEFTALT KEY_SYSRQ KEY_`echo $key | tr a-z A-Z`
    sleep 1
  done
}

gen_fix_editor() {
  if [ -f /usr/bin/qemu-system-x86_64 -a "$1" = "epel" ]; then
    # CentOS >= 7
    emulator="/usr/bin/qemu-system-x86_64"
  elif [ -f /usr/libexec/qemu-kvm ]; then
    # CentOS <= 6 & qemu-ev for CentOS 7
    emulator="/usr/libexec/qemu-kvm"
    # remove PCI controller
    echo "s|<controller type='pci' [^/>]*/>||"
  elif [ -f /usr/bin/qemu-kvm ]; then
    # Fedora
    emulator="/usr/bin/qemu-kvm"
  else
    echo "Unknown OS!"
    return 1
  fi
  # reset machine type to system default
  if [ "$1" = "pc" ]; then
    echo "s|\(<type .* machine=\)'[^']*'|\1'pc'|"
  else
    echo "s|\(<type .* machine=\)'[^']*'|\1'q35'|"
  fi
  # reset emulator
  if [ "$emulator" ]; then
    echo "s|<emulator>.*</emulator>|<emulator>$emulator</emulator>|"
  fi
  # cirrus card is obsolete, qxl too, use virtio
  echo "s|<model type='cirrus' .*/>|<model type='virtio' heads='1' primary='yes'/>|"
  echo "s|<model type='qxl' .*/>|<model type='virtio' heads='1' primary='yes'/>|"
  # remove spice graphics/audio
  echo "s|<graphics type='spice'|<graphics type='vnc'|"
  echo "s|<audio .* type='spice'/>||"
  echo "/<channel type='spicevmc'>/,/<\/channel>/d"
  echo "/<redirdev bus='usb' type='spicevmc'>/,/<\/redirdev>/d"
  # reset PCI device addresses
  if [ "$1" = "pci" -o "$1" = "reset" ]; then
    echo "/<address type='pci' /d"
    # remove PCI controller
    echo "s|<controller type='pci' [^/>]*/>||"
  fi
}

fix_config() {
  if gen_fix_editor $2; then
    if [ -x /usr/bin/xmllint ]; then
      virsh dumpxml $1 | sed -f <(gen_fix_editor $2) \
        | tee /tmp/vs-fix-$1.xml | xmllint - || exit $?
    fi
    gen_fix_editor $2 \
      | EDITOR="/bin/sed -i -f -" \
        virsh edit $1
  fi
}

define_guest() {
  gen_fix_editor  # debug output only
  for SOURCE in "$@"; do
    virsh define <(sed -f <(gen_fix_editor $2) "$SOURCE")
  done
}

scan_vol() {
  mkdir -p $VOL_INFO_DIR
  for vol in "$@"; do
    virsh vol-list $vol | grep ^unit: | sed 's/^unit//' | \
      while read unit fn; do
        echo "$vol:$unit" > $VOL_INFO_DIR/`basename $fn`
      done
  done
}

get_domain_volumes() {
  virsh dumpxml --inactive "$1" | awk '
    /<source (file|dev)=/ {
      split($0, a, "'\''")
      print a[2]
    }'
}

vol_list() {
  # check for used volume
  declare -A volumes
  for domain in `list_guests`; do
    for volume in `get_domain_volumes "$domain"`; do
      volumes["$volume"]+="$domain "
    done
  done
  virsh vol-list "$@" | while read name path; do
    if [ "${name::1}" = "-" ]; then
      printf "%s\n" "$name"
    elif [ "$path" ]; then
      printf " %-20s %s  %s\n" "$name" "$path" "${volumes[$path]}"
    fi
  done
}

add_lease() {
  EDITOR="/bin/sed -i -e \"s|<devices>|<devices><lease><lockspace>$2</lockspace><key>$1</key><target path='$3'/></lease>|\"" virsh edit "$1"
}

if [ "${1::2}" = "--" ]; then
  CMD="${1:2}"
else
  CMD="$1"
fi

if [ "$CMD" = "list" -o "$CMD" = "l" ]; then
  virsh_list "$2"
elif [ "$CMD" = "la" ]; then
  virsh_list --all
elif [ "$CMD" = "info" -o "$CMD" = "i" ]; then
  shift
  virsh_info "$@"
elif [ "$CMD" = "ii" ]; then
  shift
  virsh_info --inactive "$@"
elif [ "$CMD" = "ia" ]; then
  shift
  virsh_info --all "$@"
elif [ "$CMD" = "dump" -o "$CMD" = "d" -o "$CMD" = "dumpxml" ]; then
  shift
  virsh dumpxml "$@"
elif [ "$CMD" = "vol-list" -o "$CMD" = "vol" ]; then
  if [ "$2" == "--save" ]; then
    shift 2
    for pool in "$@"; do
      virsh vol-list "$pool"
      scan_vol "$pool"
    done
  elif [ -z "$2" ]; then
    virsh pool-list --all
  else
    shift
    for pool in "$@"; do
      vol_list "$pool"
    done
  fi
elif [ "$CMD" = "magic" ]; then
  shift
  send_magic_keys "$@"
elif [ -z "$2" ]; then
  if [ "$1" ]; then
    show_help | grep -e ^Usage: -e "$1"
  else
    show_help
  fi
elif [ "$CMD" = "restart" ]; then
  restart_guest "$2"
elif [ "$CMD" = "cad" -o "$CMD" = "reboot" ]; then
  cad_guest "$2"
elif [ "$CMD" = "shutdown" -o "$CMD" = "stop" ]; then
  shift 1
  shutdown_guests "$@"
elif [ "$CMD" = "start" ]; then
  start_guest $2
elif [ "$CMD" = "rename" ]; then
  shift
  rename_guest "$@"
elif [ "$CMD" = "move" ]; then
  move_guest "$@"  
elif [ "$CMD" = "migrate" ]; then
  guest=$2
  target=$3
  shift 3
  virsh migrate "$@" $guest qemu+ssh://root@$target/system
elif [ "$CMD" = "save" ]; then
  shift 1
  virsh managedsave "$@" --verbose --bypass-cache
elif [ "$CMD" = "edit" -o "$CMD" = "e" ]; then
  shift
  edit_guest "$@"
elif [ "$CMD" = "define" ]; then
  shift
  define_guest "$@"
elif [ "$CMD" = "set" ]; then
  GUEST="$3"
  SUBCMD="$2"
  shift 3
  case $SUBCMD in
    ram|memory|mem)
      edit_guest $GUEST -r "$1"
      ;;
    cpu|core|cores)
      edit_guest $GUEST -c "$1"
      ;;
    boot)
      edit_guest $GUEST -b "$@"
      ;;
  esac
elif [ "$CMD" = "attach-disk" ]; then
  shift
  TMP=`mktemp`
  if [ "${2::5}" = "/dev/" ]; then
    FTYPE=block
    FSOURCE=dev
  else
    FTYPE=file
    FSOURCE=file
  fi
  echo "
    <disk type='$FTYPE' device='disk'>
      <driver name='qemu' type='raw' cache='none'/>
      <source $FSOURCE='$2'/>
      <target dev='$3' bus='virtio'/>
    </disk>
  " > $TMP
  #virsh attach-disk --cache none "$@"
  #virsh attach-disk --config --cache none "$@"
  cat $TMP
  virsh attach-device $1 $TMP
  virsh attach-device $1 $TMP --config
  rm -f $TMP
elif [ "$CMD" = "detach-disk" ]; then
  shift
  virsh detach-disk "$@"
  virsh detach-disk --config "$@"
elif [ "$CMD" = "resize-disk" ]; then
  shift
  if [ "$3" ]; then
    size="$3"
  else
    size=`blockdev --getsize64 "$2"`B
  fi
  virsh blockresize "$1" "$2" --size="$size"
elif [ "$CMD" = "console" -o "$CMD" = "con" -o "$CMD" = "c" ]; then
  shift
  console_guest "$@"
elif [ "$CMD" = "diff" ]; then
  diff_guests $2 $3
elif [ "$CMD" = "destroy" ]; then
  shift
  virsh destroy "$@"
elif [ "$CMD" = "reset" ]; then
  shift
  virsh destroy "$@"
  virsh start "$@" --console
elif [ "$CMD" = "kill" ]; then
  shift
  kill_guest "$@"
elif [ "$CMD" = "install" ]; then
  shift
  install_guest "$@"
elif [ "$CMD" = "add" ]; then
  shift
  add_to_guest "$@"
elif [ "$CMD" = "fix" ]; then
  shift
  fix_config "$@"
elif [ "$CMD" = "addlease" ]; then
  shift
  add_lease "$@"
elif [ "$CMD" = "backup" ]; then
  shift
  virt-backup "$@"
elif [ "$CMD" = "restore" ]; then
  shift
  virt-backup --restore "$@"
elif [ "$CMD" = "pool-list" -o "$CMD" = "pool-start" -o "$CMD" = "pool-destroy" ]; then
  virsh "$@"
elif [ "$CMD" = "dd" ]; then
  if [ -b "$2" ]; then
    SIZE=`blockdev --getsize64 "$2"`
  else
    SIZE=`stat -c'%s' "$2"`
  fi
  dd if="$2" bs=10M iflag=direct \
    | pv --size=$SIZE \
    | dd of="$3" bs=10M oflag=direct
else
  show_help
fi
