#!/bin/bash # Copyright (C) 2009 Chris Procter All rights reserved. # Copyright (C) 2009-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 READLINK=readlink 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 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. ### The list of VG names is saved in this format: ### :vgname1:vgname2:...:vgnameN: ##################################################################### OLDVGS=":`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings --rows --separator : --config 'log{prefix=""}'`:" 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 CMD_CONFIG_LINE="devices { \ scan = [ \"${TMP_LVM_SYSTEM_DIR}\" ] \ cache_dir = \"${TMP_LVM_SYSTEM_DIR}/cache\" global_filter = [ \"a|.*|\" ] \ ${FILTER} } \ global { \ use_lvmetad = 0 \ }" $LVM dumpconfig ${LVM_OPTS} --file ${LVMCONF} --mergedconfig --config "${CMD_CONFIG_LINE}" 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 ### set to use new lvm.conf export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR} # Check if there are any PVs that don't belong to any VG # or even if there are disks which are not PVs at all. NOVGDEVLIST=`${LVM} pvs -a -o pv_name --select vg_name="" --noheadings` checkvalue $? "Failed to collect information for PV check" if [ -n "${NOVGDEVLIST}" ]; then FOLLOWLIST="" while read PVNAME; do FOLLOW=`$READLINK $PVNAME` FOLLOWLIST="$FOLLOWLIST $FOLLOW" done <<< "`echo "${NOVGDEVLIST}"`" die 8 "Specified devices don't belong to a VG:$FOLLOWLIST" fi ##################################################################### ### Rename the VG(s) and change the VG and PV UUIDs. ##################################################################### VGLIST=`${LVM} vgs -o vg_name,vg_exported,vg_missing_pv_count --noheadings --binary` checkvalue $? "Failed to collect VG information" while read VGNAME VGEXPORTED VGMISSINGPVCOUNT; do if [ $VGMISSINGPVCOUNT -gt 0 ]; then echo "Volume Group ${VGNAME} has unknown PV(s), skipping." echo "- Were all associated PV(s) supplied as arguments?" continue fi if [ "$VGEXPORTED" = "1" ]; 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 "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid --config 'global{activation=0}' --select "vg_name=${VGNAME}" checkvalue $? "Unable to change all PV uuids in VG ${VGNAME}" NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"` "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid --config 'global{activation=0}' ${VGNAME} 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 <<< "`echo "${VGLIST}"`" ##################################################################### ### 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 # get global/use_lvmetad config and if set also notify lvmetad about changes # since we were running LVM commands above with use_lvmetad=0 eval $(${LVM} dumpconfig ${LVM_OPTS} global/use_lvmetad) if [ "$use_lvmetad" = "1" ] then echo "Notifying lvmetad about changes since it was disabled temporarily." echo "(This resolves any WARNING message about restarting lvmetad that appears above.)" LVM_OPTS="${LVM_OPTS} --cache" fi "$LVM" vgscan ${LVM_OPTS} --mknodes fi exit 0