| #!/usr/bin/env bash |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| |
| |
| # $Id: managesnapshot.sh 11601 2010-08-11 17:26:15Z kris $ $HeadURL: svn://svn.lab.vmops.com/repos/branches/2.1.refactor/java/scripts/storage/qcow2/managesnapshot.sh $ |
| # managesnapshot.sh -- manage snapshots for a single disk (create, destroy, rollback, backup) |
| |
| usage() { |
| printf "Usage: %s: -c <path to disk> -n <snapshot name>\n" $(basename $0) >&2 |
| printf "Usage: %s: -d <path to disk> -n <snapshot name>\n" $(basename $0) >&2 |
| printf "Usage: %s: -r <path to disk> -n <snapshot name>\n" $(basename $0) >&2 |
| printf "Usage: %s: -b <path to disk> -n <snapshot name> -p <dest dir> -t <dest file>\n" $(basename $0) >&2 |
| exit 2 |
| } |
| |
| qemu_img="cloud-qemu-img" |
| which $qemu_img >& /dev/null |
| if [ $? -gt 0 ] |
| then |
| which qemu-img >& /dev/null |
| if [ $? -eq 0 ] |
| then |
| qemu_img="qemu-img" |
| fi |
| fi |
| |
| is_lv() { |
| # Must be a block device |
| if [ -b "${1}" ]; then |
| # But not a volume group or physical volume |
| lvm vgs "${1}" > /dev/null 2>&1 && return 1 |
| # And a logical volume |
| lvm lvs "${1}" > /dev/null 2>&1 && return 0 |
| fi |
| return 1 |
| } |
| |
| get_vg() { |
| lvm lvs --noheadings --unbuffered --separator=/ "${1}" | cut -d '/' -f 2 |
| } |
| |
| get_lv() { |
| lvm lvs --noheadings --unbuffered --separator=/ "${1}" | cut -d '/' -f 1 |
| } |
| |
| double_hyphens() { |
| echo ${1} | sed -e "s/-/--/g" |
| } |
| |
| create_snapshot() { |
| local disk=$1 |
| local snapshotname="$2" |
| local failed=0 |
| |
| if [ ${dmsnapshot} = "yes" ] && is_lv ${disk}; then |
| local lv=`get_lv ${disk}` |
| local vg=`get_vg ${disk}` |
| local lv_dm=`double_hyphens ${lv}` |
| local vg_dm=`double_hyphens ${vg}` |
| local lvdevice=/dev/mapper/${vg_dm}-${lv_dm} |
| local lv_bytes=`blockdev --getsize64 ${lvdevice}` |
| local lv_sectors=`blockdev --getsz ${lvdevice}` |
| |
| lvm lvcreate --size ${lv_bytes}b --name "${snapshotname}-cow" ${vg} >&2 || return 2 |
| dmsetup suspend ${vg_dm}-${lv_dm} >&2 |
| if dmsetup info -c --noheadings -o name ${vg_dm}-${lv_dm}-real > /dev/null 2>&1; then |
| echo "0 ${lv_sectors} snapshot ${lvdevice}-real /dev/mapper/${vg_dm}-${snapshotname}--cow p 64" | \ |
| dmsetup create "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| dmsetup resume "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| else |
| dmsetup table ${vg_dm}-${lv_dm} | dmsetup create ${vg_dm}-${lv_dm}-real >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| dmsetup resume ${vg_dm}-${lv_dm}-real >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| echo "0 ${lv_sectors} snapshot ${lvdevice}-real /dev/mapper/${vg_dm}-${snapshotname}--cow p 64" | \ |
| dmsetup create "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| echo "0 ${lv_sectors} snapshot-origin ${lvdevice}-real" | \ |
| dmsetup load ${vg_dm}-${lv_dm} >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| dmsetup resume "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) |
| fi |
| dmsetup resume "${vg_dm}-${lv_dm}" >&2 |
| elif [ -f "${disk}" ]; then |
| $qemu_img snapshot -c "$snapshotname" $disk |
| |
| |
| if [ $? -gt 0 ] |
| then |
| failed=2 |
| printf "***Failed to create snapshot $snapshotname for path $disk\n" >&2 |
| $qemu_img snapshot -d "$snapshotname" $disk |
| |
| if [ $? -gt 0 ] |
| then |
| printf "***Failed to delete snapshot $snapshotname for path $disk\n" >&2 |
| fi |
| fi |
| else |
| failed=3 |
| printf "***Failed to create snapshot $snapshotname, undefined type $disk\n" >&2 |
| fi |
| |
| return $failed |
| } |
| |
| destroy_snapshot() { |
| local disk=$1 |
| local snapshotname="$2" |
| local failed=0 |
| |
| if is_lv ${disk}; then |
| local lv=`get_lv ${disk}` |
| local vg=`get_vg ${disk}` |
| local lv_dm=`double_hyphens ${lv}` |
| local vg_dm=`double_hyphens ${vg}` |
| if [ -e /dev/mapper/${vg_dm}-${lv_dm}-real ]; then |
| local dm_refcount=`dmsetup info -c --noheadings -o open ${vg_dm}-${lv_dm}-real` |
| if [ ${dm_refcount} -le 2 ]; then |
| dmsetup suspend ${vg_dm}-${lv_dm} >&2 |
| dmsetup table ${vg_dm}-${lv_dm}-real | dmsetup load ${vg_dm}-${lv_dm} >&2 |
| dmsetup resume ${vg_dm}-${lv_dm} |
| dmsetup remove "${vg_dm}-${snapshotname}" |
| dmsetup remove ${vg_dm}-${lv_dm}-real |
| else |
| dmsetup remove "${vg_dm}-${snapshotname}" |
| fi |
| else |
| dmsetup remove "${vg_dm}-${snapshotname}" |
| fi |
| lvm lvremove -f "${vg}/${snapshotname}-cow" |
| elif [ -f $disk ]; then |
| $qemu_img snapshot -d "$snapshotname" $disk |
| if [ $? -gt 0 ] |
| then |
| failed=2 |
| printf "Failed to delete snapshot $snapshotname for path $disk\n" >&2 |
| fi |
| else |
| failed=3 |
| printf "***Failed to delete snapshot $snapshotname, undefined type $disk\n" >&2 |
| fi |
| return $failed |
| } |
| |
| rollback_snapshot() { |
| local disk=$1 |
| local snapshotname="$2" |
| local failed=0 |
| |
| $qemu_img snapshot -a $snapshotname $disk |
| |
| if [ $? -gt 0 ] |
| then |
| printf "***Failed to apply snapshot $snapshotname for path $disk\n" >&2 |
| failed=1 |
| fi |
| |
| return $failed |
| } |
| |
| backup_snapshot() { |
| local disk=$1 |
| local snapshotname="$2" |
| local destPath=$3 |
| local destName=$4 |
| |
| if [ ! -d $destPath ] |
| then |
| mkdir -p $destPath >& /dev/null |
| if [ $? -gt 0 ] |
| then |
| printf "Failed to create $destPath\n" >&2 |
| return 3 |
| fi |
| fi |
| |
| if [ ${dmsnapshot} = "yes" ] && is_lv ${disk}; then |
| local vg=`get_vg ${disk}` |
| local vg_dm=`double_hyphens ${vg}` |
| local scriptdir=`dirname ${0}` |
| |
| if ! dmsetup info -c --noheadings -o name ${vg_dm}-${snapshotname} > /dev/null 2>&1; then |
| printf "Disk ${disk} has no snapshot called ${snapshotname}.\n" >&2 |
| return 1 |
| fi |
| |
| ${qemu_img} convert -f raw -O qcow2 "/dev/mapper/${vg_dm}-${snapshotname}" "${destPath}/${destName}" || \ |
| ( printf "${qemu_img} failed to create backup of snapshot ${snapshotname} for disk ${disk} to ${destPath}.\n" >&2; return 2 ) |
| |
| elif [ -f ${disk} ]; then |
| # Does the snapshot exist? |
| $qemu_img snapshot -l $disk|grep -w "$snapshotname" >& /dev/null |
| if [ $? -gt 0 ] |
| then |
| printf "there is no $snapshotname on disk $disk\n" >&2 |
| return 1 |
| fi |
| |
| $qemu_img convert -f qcow2 -O qcow2 -s $snapshotname $disk $destPath/$destName >& /dev/null |
| if [ $? -gt 0 ] |
| then |
| printf "Failed to backup $snapshotname for disk $disk to $destPath\n" >&2 |
| return 2 |
| fi |
| else |
| printf "***Failed to backup snapshot $snapshotname, undefined type $disk\n" >&2 |
| return 3 |
| fi |
| return 0 |
| } |
| #set -x |
| |
| cflag= |
| dflag= |
| rflag= |
| bflag= |
| nflag= |
| pathval= |
| snapshot= |
| tmplName= |
| deleteDir= |
| dmsnapshot=no |
| dmrollback=no |
| |
| while getopts 'c:d:r:n:b:p:t:f' OPTION |
| do |
| case $OPTION in |
| c) cflag=1 |
| pathval="$OPTARG" |
| ;; |
| d) dflag=1 |
| pathval="$OPTARG" |
| ;; |
| r) rflag=1 |
| pathval="$OPTARG" |
| ;; |
| b) bflag=1 |
| pathval="$OPTARG" |
| ;; |
| n) nflag=1 |
| snapshot="$OPTARG" |
| ;; |
| p) destPath="$OPTARG" |
| ;; |
| t) tmplName="$OPTARG" |
| ;; |
| f) deleteDir=1 |
| ;; |
| ?) usage |
| ;; |
| esac |
| done |
| |
| if modprobe dm-snapshot; then |
| dmsnapshot=yes |
| dmsetup targets | grep -q "^snapshot-merge" && dmrollback=yes |
| fi |
| |
| [ -z "${snapshot}" ] && usage |
| |
| [ -b "$pathval" ] && snapshot=`echo "${snapshot}" | md5sum -t | awk '{ print $1 }'` |
| |
| if [ "$cflag" == "1" ] |
| then |
| create_snapshot $pathval "$snapshot" |
| exit $? |
| elif [ "$dflag" == "1" ] |
| then |
| destroy_snapshot $pathval "$snapshot" $deleteDir |
| exit $? |
| elif [ "$bflag" == "1" ] |
| then |
| [ -z "${destPath}" -o -z "${tmplName}" ] && usage |
| backup_snapshot $pathval $snapshot $destPath $tmplName |
| exit $? |
| elif [ "$rflag" == "1" ] |
| then |
| rollback_snapshot $pathval "$snapshot" $destPath |
| exit $? |
| fi |
| |
| |
| exit 0 |