mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
8742213863
- validate the specified device is a PV and that it is in a VG - automatically enable DEBUG (-d) if >= 4 -v instances were supplied - preserve TMP_LVM_SYSTEM_DIR if it contains an lvm.conf and -d was specified - fix handling of special-case where PV is listed as "unknown device" - more descriptive error when a PV is missing ("unknown device") - unset LVM_SYSTEM_DIR if it was not originally set - skip final vgscan if no changes were made
367 lines
10 KiB
Bash
Executable File
367 lines
10 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# 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
|