#!/bin/bash

# Old hypervisor running Xen (to connect via SSH)
OLD="root@rouf.grenode.net"
# Name of the volume group on the new hypervisor (for Ganeti)
VG="vms"
# Name of a Ganeti node, on which the new VM will be created.
GANETI_NODE="tillac.grenode.net"

function failwith() {
  echo "$1"
  exit 1
}

usage() {
  echo "usage: $0 [--dry-run] <vm_name> <memory> <nb_CPU> <bridge1> [bridge2 [bridge3 ...]]"
  echo "Migrate a VM from a Xen host to the local Ganeti host."
  echo "The named VM must exist and be running on the old Xen host."
  echo "A list of bridges can be passed: for each bridge, one network interface will be created in the VM."
  echo
  echo "This script does the following:"
  echo "1/ Create a local LVM volume, with the same size as the old VM"
  echo "2/ Shutdown the VM on the old (Xen) hypervisor"
  echo "3/ Copy the VM disk through SSH (which can take time!)"
  echo "4/ Create a local Ganeti VM with the same name as the old one, and the specified memory & CPU"
  echo "5/ Start the new VM"
  echo
  echo "To see what would happen without performing the dangerous steps, use --dry-run."
  exit 1
}

[ "$1" = "--dry-run" ] && { DRYRUN="echo #"; shift; }

[ -z "$4" ] && usage
vm="$1"
memory="$2"
cpu="$3"
shift 3

(( cpu >= 1 && cpu <= 12 )) || failwith "Invalid CPU count: $cpu"

echo "Checking network bridges on new hypervisor..."
for bridge in "$@"
do
  ip link show dev "$bridge" || failwith "Bridge not found"
done

# Shell magic for bridges
bridges="$(echo "$@" | awk '{for (i = 1; i <= NF; i++) { printf "--net=%d:link=%s ", i-1, $i; }}')"



# Get VM disk
disk="$(ssh $OLD xm list -l "$vm" | grep uname | sed -e 's#.*uname phy:\(.*\))#\1#')"
[ -n "$disk" ] || failwith "No disk found, is the VM still running on old hypervisor?"
echo; echo "Checking existing LVM volume on old hypervisor..."
ssh $OLD lvdisplay "$disk" || failwith "LVM disk invalid"

# Size in mebibytes (2^20): this is what ganeti wants.
disk_size=$(ssh $OLD LANG=C lvs "$disk" -o LV_SIZE --noheadings --units m --nosuffix | sed -e 's#[^0-9]*\([0-9]*\).*#\1#')

echo; echo "Using disk $disk with size $disk_size MiB."

sleep 2

echo; echo "Creating LVM volume on new hypervisor..."
$DRYRUN lvcreate -n "$vm" -L "$disk_size"m "$VG" || failwith "LVM creation failed"

local_disk="/dev/$VG/$vm"
echo; echo "Checking that new LVM volume has been created..."
$DRYRUN lvdisplay "$local_disk" || failwith "Couldn't find new LVM volume on new hypervisor"

echo; echo "Shutting down VM on old hypervisor... (this may take some time)"
$DRYRUN ssh $OLD xm shutdown -w "$vm"
# Just to be sure
sleep 2

echo; echo "Copying disk from old hypervisor..."
if [ -z "$DRYRUN" ]
then
  ssh $OLD "dd if=$disk bs=1M" | pv | dd of="$local_disk" bs=4M
else
  $DRYRUN "ssh $OLD \"dd if=$disk bs=1M\" | pv | dd of=\"$local_disk\" bs=4M"
fi

echo; echo "Creating new Ganeti VM on new hypervisor..."
$DRYRUN gnt-instance add -n "$GANETI_NODE" -o noop -t plain --disk 0:adopt="$vm" -B memory="$memory",vcpus="$cpu" $bridges --no-name-check --no-ip-check --no-start "$vm" || failwith "Ganeti creation failed"

echo; echo "Starting VM..."
$DRYRUN gnt-instance start "$vm"
