# vim syntax=sh if type zfs > /dev/null 2>&1; then SNAP_METHOD="zfs" STATE_PATH="/vms/state" else SNAP_METHOD="lvm" STATE_PATH="/var/lib/libvirt/qemu/save" fi lvm_snapshot() { local volume=$1 snapname=$2 sudo lvcreate -sn $volume-$snapname -l 100%ORIGIN vms/$volume } lvm_revert() { local volume=$1 snapname=$2 sudo lvconvert --merge vms/$volume-$snapname -i 5 sudo lvcreate -sn $volume-$snapname -l 100%ORIGIN vms/$volume # keep snapshot around } lvm_discard() { local volume=$1 snapname=$2 sudo lvremove -f vms/$volume-$snapname } zfs_snapshot() { local volume=$1 snapname=$2 sudo zfs snapshot vms/$volume@$snapname sudo zfs clone vms/$volume@$snapname vms/$volume-$snapname sudo zfs promote vms/$volume-$snapname } zfs_revert() { local volume=$1 snapname=$2 sudo zfs destroy vms/$volume sudo zfs clone vms/$volume-$snapname@$snapname vms/$volume } zfs_discard() { local volume=$1 snapname=$2 clones="$(sudo zfs get -H clones vms/$volume-$snapname@$snapname | cut -f3)" case $clones in "" ) sudo zfs destroy vms/$volume-$snapname@$snapname sudo zfs destroy vms/$volume-$snapname ;; vms/$volume ) sudo zfs promote vms/$volume sudo zfs destroy vms/$volume-$snapname sudo zfs destroy vms/$volume@$snapname ;; * ) echo "Can't remove snapshot vms/$volume-$snapname@$snapname since it has clones: $clones" esac } virsh_all() { local action=$1 shift echo "$@" | xargs -P0 -n1 virsh $action | sed -n '/./p' } snapshot_vms() { local snapname=$1 domain snap_arg shift sudo mkdir -p "$STATE_PATH" sudo chown libvirt-qemu:kvm "$STATE_PATH" virsh_all suspend "$@" for domain; do ${SNAP_METHOD}_snapshot $domain $snapname snap_arg="" case $domain in fuel ) snap_arg="--diskspec hdb,snapshot=no" ;; compute-* ) snap_arg="--diskspec hdb,snapshot=no" ${SNAP_METHOD}_snapshot $domain-ceph $snapname ;; esac virsh snapshot-create-as $domain $domain-$snapname --atomic --memspec "$STATE_PATH/$domain-$snapname" --diskspec hda,snapshot=no $snap_arg done virsh_all resume "$@" } revert_vms() { local snapname=$1 domain shift virsh_all destroy "$@" for domain; do ${SNAP_METHOD}_revert $domain $snapname case $domain in compute-* ) ${SNAP_METHOD}_revert $domain-ceph $snapname ;; esac virsh restore "$STATE_PATH/$domain-$snapname" --paused done virsh_all resume "$@" } discard_snapshots() { local snapname=$1 domain shift for domain; do ${SNAP_METHOD}_discard $domain $snapname case $domain in compute-* ) ${SNAP_METHOD}_discard $domain-ceph $snapname ;; esac virsh snapshot-delete $domain $domain-$snapname --metadata sudo rm -f "$STATE_PATH/$domain-$snapname" done } zfs_transfer() { local volume=$1 snapname=$2 target=$3 local dataset="vms/$volume-$snapname@$snapname" sudo zfs send "$dataset" | pv -cN $domain -s "$(sudo zfs list -Hp "$dataset" | cut -f4)" | ssh $target sudo zfs recv -vd vms } transfer_snapshots() { local snapname=$1 target=$2 domain shift; shift if [ "$SNAP_METHOD" != "zfs" ]; then echo "Can transfer only ZFS snapshots" return 1 fi if ! type pv > /dev/null 2>&1; then sudo apt-get install -y pv fi if ! ssh -o KbdInteractiveAuthentication=no $target true; then echo "Please set up passwordless SSH to node $target" return 1 fi if ! ssh $target sudo -nv 2> /dev/null; then echo "Please set up passwordless sudo on node $target" return 1 fi ssh $target sudo mkdir -p "$STATE_PATH" for domain; do ${SNAP_METHOD}_transfer $domain $snapname $target case $domain in compute-* ) ${SNAP_METHOD}_transfer $domain-ceph $snapname $target ;; esac local state_file="$STATE_PATH/$domain-$snapname" sudo pv "$state_file" | ssh $target sudo "sh -c \"cat > \\\"$state_file\\\"\"" done }