home *** CD-ROM | disk | FTP | other *** search
- #!/bin/bash
-
- # Copyright (C) 2009 Chris Procter All rights reserved.
- # Copyright (C) 2009 Red Hat, Inc. All rights reserved.
- #
- # This file is part of LVM2.
- #
- # This copyrighted material is made available to anyone wishing to use,
- # modify, copy, or redistribute it subject to the terms and conditions
- # of the GNU General Public License v.2.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- #
- # vgimportclone: This script is used to rename the VG and change the associated
- # VG and PV UUIDs (primary application being HW snapshot restore)
-
- # following external commands are used throughout the script
- # echo and test are internal in bash at least
- RM=rm
- BASENAME=basename
- MKTEMP=mktemp
- AWK=awk
- CUT=cut
- TR=tr
- READLINK=readlink
- GREP=grep
- GETOPT=getopt
-
- # user may override lvm location by setting LVM_BINARY
- LVM="${LVM_BINARY:-lvm}"
-
- die() {
- code=$1; shift
- echo "Fatal: $@" 1>&2
- exit $code
- }
-
- "$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'"
-
-
- function getvgname {
- ### get a unique vg name
- ### $1 = list of exists VGs
- ### $2 = the name we want
- VGLIST=$1
- VG=$2
- NEWVG=$3
-
- BNAME="${NEWVG:-${VG}}"
- NAME="${BNAME}"
- I=0
-
- while [[ "${VGLIST}" =~ "${NAME}" ]]
- do
- I=$(($I+1))
- NAME="${BNAME}$I"
- done
- echo "${NAME}"
- }
-
-
- function checkvalue {
- ### check return value and error if non zero
- if [ $1 -ne 0 ]
- then
- die $1 "$2, error: $1"
- fi
- }
-
-
- function usage {
- ### display usage message
- echo "Usage: ${SCRIPTNAME} [options] PhysicalVolume [PhysicalVolume...]"
- echo " -n|--basevgname - Base name for the new volume group(s)"
- echo " -i|--import - Import any exported volume groups found"
- echo " -t|--test - Run in test mode"
- echo " --quiet - Suppress output"
- echo " -v|--verbose - Set verbose level"
- echo " -d|--debug - Set debug level"
- echo " --version - Display version information"
- echo " -h|--help - Display this help message"
- echo ""
- exit 1
- }
-
-
- function cleanup {
- #set to use old lvm.conf
- LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
-
- if [ $KEEP_TMP_LVM_SYSTEM_DIR -eq 1 ]; then
- echo "${SCRIPTNAME}: LVM_SYSTEM_DIR (${TMP_LVM_SYSTEM_DIR}) must be cleaned up manually."
- else
- "$RM" -rf -- "${TMP_LVM_SYSTEM_DIR}"
- fi
- }
-
- SCRIPTNAME=`"$BASENAME" $0`
-
-
- if [ "$UID" != "0" -a "$EUID" != "0" ]
- then
- die 3 "${SCRIPTNAME} must be run as root."
- fi
-
- LVM_OPTS=""
- TEST_OPT=""
- DISKS=""
- # for compatibility: using mktemp -t rather than --tmpdir
- TMP_LVM_SYSTEM_DIR=`"$MKTEMP" -d -t snap.XXXXXXXX`
- KEEP_TMP_LVM_SYSTEM_DIR=0
- CHANGES_MADE=0
- IMPORT=0
- DEBUG=""
- VERBOSE=""
- VERBOSE_COUNT=0
- DEVNO=0
-
- if [ -n "${LVM_SYSTEM_DIR}" ]; then
- export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}"
- fi
-
- trap cleanup 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
-
- #####################################################################
- ### Get and check arguments
- #####################################################################
- OPTIONS=`"$GETOPT" -o n:dhitv \
- -l basevgname:,debug,help,import,quiet,test,verbose,version \
- -n "${SCRIPTNAME}" -- "$@"`
- [ $? -ne 0 ] && usage
- eval set -- "$OPTIONS"
-
- while true
- do
- case $1 in
- -n|--basevgname)
- NEWVG="$2"; shift; shift
- ;;
- -i|--import)
- IMPORT=1; shift
- ;;
- -t|--test)
- TEST_OPT="-t"
- shift
- ;;
- --quiet)
- LVM_OPTS="--quiet ${LVM_OPTS}"
- shift
- ;;
- -v|--verbose)
- let VERBOSE_COUNT=VERBOSE_COUNT+1
- if [ -z "$VERBOSE" ]
- then
- VERBOSE="-v"
- else
- VERBOSE="${VERBOSE}v"
- fi
- shift
- ;;
- -d|--debug)
- if [ -z "$DEBUG" ]
- then
- DEBUG="-d"
- set -x
- else
- DEBUG="${DEBUG}d"
- fi
- shift
- ;;
- --version)
- "$LVM" version
- shift
- exit 0
- ;;
- -h|--help)
- usage; shift
- ;;
- --)
- shift; break
- ;;
- *)
- usage
- ;;
- esac
- done
-
- # turn on DEBUG (special case associated with -v use)
- if [ -z "$DEBUG" -a $VERBOSE_COUNT -gt 3 ]; then
- DEBUG="-d"
- set -x
- fi
-
- # setup LVM_OPTS
- if [ -n "${DEBUG}" -o -n "${VERBOSE}" ]
- then
- LVM_OPTS="${LVM_OPTS} ${DEBUG} ${VERBOSE}"
- fi
-
- # process remaining arguments (which should be disks)
- for ARG
- do
- if [ -b "$ARG" ]
- then
- PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG" 2>/dev/null`
- checkvalue $? "$ARG is not a PV."
- PV_VGNAME=$(echo $PVS_OUT | $GREP -v '[[:space:]]+$')
- [ -z "$PV_VGNAME" ] && die 3 "$ARG is not in a VG."
-
- ln -s "$ARG" ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}
- DISKS="${DISKS} ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}"
- DEVNO=$((${DEVNO}+1))
- else
- die 3 "$ARG is not a block device."
- fi
- done
-
- ### check we have suitable values for important variables
- if [ -z "${DISKS}" ]
- then
- usage
- fi
-
- #####################################################################
- ### Get the existing state so we can use it later
- #####################################################################
-
- OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null`
- checkvalue $? "Current VG names could not be collected without errors"
-
- #####################################################################
- ### Prepare the temporary lvm environment
- #####################################################################
-
- for BLOCK in ${DISKS}
- do
- FILTER="\"a|^${BLOCK}$|\", ${FILTER}"
- done
- export FILTER="filter=[ ${FILTER} \"r|.*|\" ]"
-
- LVMCONF=${TMP_LVM_SYSTEM_DIR}/lvm.conf
-
- "$LVM" dumpconfig ${LVM_OPTS} | \
- "$AWK" -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/.cache \
- -v CACHE_DIR=${TMP_LVM_SYSTEM_DIR}/cache \
- '/^[[:space:]]*filter[[:space:]]*=/{print ENVIRON["FILTER"];next} \
- /^[[:space:]]*scan[[:space:]]*=/{print "scan = [ \"" DEV "\" ]";next} \
- /^[[:space:]]*cache[[:space:]]*=/{print "cache = \"" CACHE "\"";next} \
- /^[[:space:]]*cache_dir[[:space:]]*=/{print "cache_dir = \"" CACHE_DIR "\"";next} \
- {print $0}' > ${LVMCONF}
-
- checkvalue $? "Failed to generate ${LVMCONF}"
- # Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping
- [ -n "${DEBUG}" ] && KEEP_TMP_LVM_SYSTEM_DIR=1
-
- # verify the config contains the filter, scan and cache_dir (or cache) config keywords
- "$GREP" -q '^[[:space:]]*filter[[:space:]]*=' ${LVMCONF} || \
- die 5 "Temporary lvm.conf must contain 'filter' config."
- "$GREP" -q '^[[:space:]]*scan[[:space:]]*=' ${LVMCONF} || \
- die 6 "Temporary lvm.conf must contain 'scan' config."
-
- # check for either 'cache' or 'cache_dir' config values
- "$GREP" -q '[[:space:]]*cache[[:space:]]*=' ${LVMCONF}
- CACHE_RET=$?
- "$GREP" -q '^[[:space:]]*cache_dir' ${LVMCONF}
- CACHE_DIR_RET=$?
- [ $CACHE_RET -eq 0 -o $CACHE_DIR_RET -eq 0 ] || \
- die 7 "Temporary lvm.conf must contain 'cache' or 'cache_dir' config."
-
- ### set to use new lvm.conf
- export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR}
-
-
- #####################################################################
- ### Rename the VG(s) and change the VG and PV UUIDs.
- #####################################################################
-
- PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null`
- checkvalue $? "PV info could not be collected without errors"
-
- # output VG info so each line looks like: name:exported?:disk1,disk2,...
- VGINFO=`echo "${PVINFO}" | \
- "$AWK" -F : '{{sub(/^[[:space:]]*/,"")} \
- {sub(/unknown device/,"unknown_device")} \
- {vg[$2]=$1","vg[$2]} if($3 ~ /^..x/){x[$2]="x"}} \
- END{for(k in vg){printf("%s:%s:%s\n", k, x[k], vg[k])}}'`
- checkvalue $? "PV info could not be parsed without errors"
-
- for VG in ${VGINFO}
- do
- VGNAME=`echo "${VG}" | "$CUT" -d: -f1`
- EXPORTED=`echo "${VG}" | "$CUT" -d: -f2`
- PVLIST=`echo "${VG}" | "$CUT" -d: -f3- | "$TR" , ' '`
-
- if [ -z "${VGNAME}" ]
- then
- FOLLOWLIST=""
- for DEV in $PVLIST; do
- FOLLOW=`"$READLINK" $DEV`
- FOLLOWLIST="$FOLLOW $FOLLOWLIST"
- done
- die 8 "Specified PV(s) ($FOLLOWLIST) don't belong to a VG."
- fi
-
- if [ -n "${EXPORTED}" ]
- then
- if [ ${IMPORT} -eq 1 ]
- then
- "$LVM" vgimport ${LVM_OPTS} ${TEST_OPT} "${VGNAME}"
- checkvalue $? "Volume Group ${VGNAME} could not be imported"
- else
- echo "Volume Group ${VGNAME} exported, skipping."
- continue
- fi
- fi
-
- ### change the pv uuids
- if [[ "${PVLIST}" =~ "unknown" ]]
- then
- echo "Volume Group ${VGNAME} has unknown PV(s), skipping."
- echo "- Were all associated PV(s) supplied as arguments?"
- continue
- fi
-
- for BLOCKDEV in ${PVLIST}
- do
- "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid ${BLOCKDEV} --config 'global{activation=0}'
- checkvalue $? "Unable to change PV uuid for ${BLOCKDEV}"
- done
-
- NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"`
-
- "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid "${VGNAME}" --config 'global{activation=0}'
- checkvalue $? "Unable to change VG uuid for ${VGNAME}"
-
- ## if the name isn't going to get changed dont even try.
- if [ "${VGNAME}" != "${NEWVGNAME}" ]
- then
- "$LVM" vgrename ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" "${NEWVGNAME}"
- checkvalue $? "Unable to rename ${VGNAME} to ${NEWVGNAME}"
- fi
-
- CHANGES_MADE=1
- done
-
- #####################################################################
- ### Restore the old environment
- #####################################################################
- ### set to use old lvm.conf
- if [ -z "${ORIG_LVM_SYS_DIR}" ]
- then
- unset LVM_SYSTEM_DIR
- else
- LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
- fi
-
- ### update the device cache and make sure all
- ### the device nodes we need are straight
- if [ ${CHANGES_MADE} -eq 1 ]
- then
- "$LVM" vgscan ${LVM_OPTS} --mknodes
- fi
-
- exit 0
-