#!/bin/bash
#
# Scan Qcow2 image file for existing snapshots and (re-)create /var/lib/libvirt/qemu/snapshot/$VM/$SNAP.xml files.
# Author: Philipp Hahn <hahn@univention.de>
#
set -e
shopt -s extglob

prog=${##*}
force=
noact=
parent=
validate=
parent=
last=

debug () { : ; }
usage () {
	local rv=$1
	local msg=${2:-""}
	echo "usage: ${0##*/} [-dfhnpv] image.qcow2 [vm]"
	if [ -n "$msg" ]
	then
		echo "$msg"
	else
		echo " -d, --debug     Show parsed data"
		echo " -f, --force     Overwrite old files"
		echo " -h, --help      Print this help message"
		echo " -n, --no-act    Don't write file"
		echo " -p, --parent    Generate linearized parent references"
		echo " -v, --validate  Validate generated xml"
	fi
	exit $rv
}

temp=$(getopt -o dfhnpv --long debug,force,help,no-act,parent,validate -n "$proc" -- "$@")
if [ $? -ne 0 ]
then
	usage 2 >&2
fi
eval set -- "$temp"
while true
do
	case "$1" in
	-d|--debug) debug () { echo "D: $@" ; } ; shift ;;
	-f|--force) force=true ; shift ;;
	-h|--help) usage 0 ;;
	-n|--no-act) noact=true ; shift ;;
	-p|--parent) parent=true ; shift ;;
	-v|--validate) validate=true ; shift ;;
	--) shift ; break ;;
	*) usage 3 "Internal error $1" >&2 ;;
	esac
done

image="$1"
[ -n "$image" ] || usage 2 "Now qcow2 image given" >&2
[ -r "$image" ] || usage 1 "Image not readable: $image" >&2

vm_name="$2"
if [ -z "$vm_name" ]
then
	vm_name=${image##*/}
	vm_name=${vm_name%.qcow2}
fi
vm_xml="/etc/libvirt/qemu/$vm_name.xml"
[ -r "$vm_xml" ] || usage 1 "VM not readable: $vm_xml" >&2

snap_dir="/var/lib/libvirt/qemu/snapshot/$vm_name"
[ -d "$snap_dir" ] || usage 1 "Snapshot directory not found: $snap_dir" >&2

# sed -e '1,2d' -e 's/^\([0-9]\+\)\s\+\(\S.*\S\)\s\+\([0-9]\+[KMG]\?\)\s\(....-..-.. ..:..:..\)   \(..:..:..\....\)$/\1\n\2\n\3\n\4\n\5/')
exec < <(LC_ALL=C qemu-img snapshot -l "$image")
read line
test "$line" = "Snapshot list:" || usage 3 "Incompatible change in qemu-img: $line" >&2
read line
test "$line" = "ID        TAG                 VM SIZE                DATE       VM CLOCK" || usage 3 "Incompatible change in qemu-img: $line" >&2
while read line
do
	debug "line=$line"
	id=${line%% *}
	vmclock=${line:${#line}-12:12} # 00:00:00.000
	date=${line:${#line}-34:19} # 0000-00-00 00:00:00
	line=${line:10:${#line}-45}
	vmsize=${line##* }
	tag=${line%%+( )${vmsize}}
	debug "id='$id'"
	debug "tag='$tag'"
	debug "vmsize='$vmsize'"
	debug "date='$date'"
	debug "clock='$vmclock'"

	# convert $tag to valid xml
	tag=${tag//\&/\&amp\;}
	tag=${tag//\</\&lt\;}
	tag=${tag//\>/\&gt\;}
	tag=${tag//\"/\&quot\;}

	snap_xml="$snap_dir/$tag.xml"
	if [ -e "$snap_xml" -a -z "$force" ]
	then
		echo "Snapshot already exists: $snap_xml"
		continue
	fi

	debug "$snap_xml"
	if [ -z "$noact" ]
	then
		exec 3>"$snap_xml"
	else
		exec 3>&1
	fi
	echo "<domainsnapshot>" >&3
	echo "	<name>$tag</name>" >&3
	if [ -n "parent" -a -n "$last" ]
	then
		echo "	<parent>" >&3
		echo "		<name>$last</name>" >&3
		echo "	</parent>" >&3
	fi
	if [ 0 = "$vmsize" -a "00:00:00.000" = "$vmclock" ]
	then
		echo "	<state>shutoff</state>" >&3
	else
		echo "	<state>running</state>" >&3
	fi
	echo "	<creationTime>$(date -d "${date}Z" +%s)</creationTime>" >&3
	cat "$vm_xml" >&3
	echo "</domainsnapshot>" >&3
	exec 3<&-
	if [ -n "$validate" ]
	then
		virt-xml-validate "$snap_xml"
	fi
	last="$tag"
done

exit 0
