2010-05-14 15:33:20 +04:00
#!/bin/bash
2009-05-14 20:46:12 +04:00
# 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 }
2009-06-17 19:47:01 +04:00
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
2009-05-14 20:46:12 +04:00
}
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`
2009-06-17 19:47:01 +04:00
KEEP_TMP_LVM_SYSTEM_DIR = 0
CHANGES_MADE = 0
2009-05-14 20:46:12 +04:00
IMPORT = 0
DEBUG = ""
VERBOSE = ""
2009-06-17 19:47:01 +04:00
VERBOSE_COUNT = 0
2009-05-14 20:46:12 +04:00
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)
2009-06-17 19:47:01 +04:00
let VERBOSE_COUNT = VERBOSE_COUNT+1
2009-05-14 20:46:12 +04:00
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
2009-06-17 19:47:01 +04:00
# 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
2009-05-14 20:46:12 +04:00
# process remaining arguments (which should be disks)
for ARG
do
if [ -b " $ARG " ]
then
2009-06-17 19:47:01 +04:00
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. "
2009-05-14 20:46:12 +04:00
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 } "
2009-06-17 19:47:01 +04:00
# Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping
[ -n " ${ DEBUG } " ] && KEEP_TMP_LVM_SYSTEM_DIR = 1
2009-05-14 20:46:12 +04:00
# 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:] ] */,"" ) } \
2009-06-17 19:47:01 +04:00
{ sub( /unknown device/,"unknown_device" ) } \
2009-05-14 20:46:12 +04:00
{ 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
2009-06-17 19:47:01 +04:00
echo " Volume Group ${ VGNAME } has unknown PV(s), skipping. "
echo "- Were all associated PV(s) supplied as arguments?"
2009-05-14 20:46:12 +04:00
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
2009-06-17 19:47:01 +04:00
CHANGES_MADE = 1
2009-05-14 20:46:12 +04:00
done
#####################################################################
### Restore the old environment
#####################################################################
### set to use old lvm.conf
2009-06-17 19:47:01 +04:00
if [ -z " ${ ORIG_LVM_SYS_DIR } " ]
then
unset LVM_SYSTEM_DIR
else
LVM_SYSTEM_DIR = ${ ORIG_LVM_SYS_DIR }
fi
2009-05-14 20:46:12 +04:00
### update the device cache and make sure all
### the device nodes we need are straight
2009-06-17 19:47:01 +04:00
if [ ${ CHANGES_MADE } -eq 1 ]
then
" $LVM " vgscan ${ LVM_OPTS } --mknodes
fi
2009-05-14 20:46:12 +04:00
exit 0