1#!/bin/sh 2 3# Copyright (C) 2009 Chris Procter All rights reserved. 4# Copyright (C) 2009 Red Hat, Inc. All rights reserved. 5# 6# This file is part of LVM2. 7# 8# This copyrighted material is made available to anyone wishing to use, 9# modify, copy, or redistribute it subject to the terms and conditions 10# of the GNU General Public License v.2. 11# 12# You should have received a copy of the GNU General Public License 13# along with this program; if not, write to the Free Software Foundation, 14# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15# 16# vgimportclone: This script is used to rename the VG and change the associated 17# VG and PV UUIDs (primary application being HW snapshot restore) 18 19# following external commands are used throughout the script 20# echo and test are internal in bash at least 21RM=rm 22BASENAME=basename 23MKTEMP=mktemp 24AWK=awk 25CUT=cut 26TR=tr 27READLINK=readlink 28GREP=grep 29GETOPT=getopt 30 31# user may override lvm location by setting LVM_BINARY 32LVM="${LVM_BINARY:-lvm}" 33 34die() { 35 code=$1; shift 36 echo "Fatal: $@" 1>&2 37 exit $code 38} 39 40"$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'" 41 42 43function getvgname { 44### get a unique vg name 45### $1 = list of exists VGs 46### $2 = the name we want 47 VGLIST=$1 48 VG=$2 49 NEWVG=$3 50 51 BNAME="${NEWVG:-${VG}}" 52 NAME="${BNAME}" 53 I=0 54 55 while [[ "${VGLIST}" =~ "${NAME}" ]] 56 do 57 I=$(($I+1)) 58 NAME="${BNAME}$I" 59 done 60 echo "${NAME}" 61} 62 63 64function checkvalue { 65### check return value and error if non zero 66 if [ $1 -ne 0 ] 67 then 68 die $1 "$2, error: $1" 69 fi 70} 71 72 73function usage { 74### display usage message 75 echo "Usage: ${SCRIPTNAME} [options] PhysicalVolume [PhysicalVolume...]" 76 echo " -n|--basevgname - Base name for the new volume group(s)" 77 echo " -i|--import - Import any exported volume groups found" 78 echo " -t|--test - Run in test mode" 79 echo " --quiet - Suppress output" 80 echo " -v|--verbose - Set verbose level" 81 echo " -d|--debug - Set debug level" 82 echo " --version - Display version information" 83 echo " -h|--help - Display this help message" 84 echo "" 85 exit 1 86} 87 88 89function cleanup { 90 #set to use old lvm.conf 91 LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR} 92 93 if [ $KEEP_TMP_LVM_SYSTEM_DIR -eq 1 ]; then 94 echo "${SCRIPTNAME}: LVM_SYSTEM_DIR (${TMP_LVM_SYSTEM_DIR}) must be cleaned up manually." 95 else 96 "$RM" -rf -- "${TMP_LVM_SYSTEM_DIR}" 97 fi 98} 99 100SCRIPTNAME=`"$BASENAME" $0` 101 102 103if [ "$UID" != "0" -a "$EUID" != "0" ] 104then 105 die 3 "${SCRIPTNAME} must be run as root." 106fi 107 108LVM_OPTS="" 109TEST_OPT="" 110DISKS="" 111# for compatibility: using mktemp -t rather than --tmpdir 112TMP_LVM_SYSTEM_DIR=`"$MKTEMP" -d -t snap.XXXXXXXX` 113KEEP_TMP_LVM_SYSTEM_DIR=0 114CHANGES_MADE=0 115IMPORT=0 116DEBUG="" 117VERBOSE="" 118VERBOSE_COUNT=0 119DEVNO=0 120 121if [ -n "${LVM_SYSTEM_DIR}" ]; then 122 export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}" 123fi 124 125trap cleanup 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 126 127##################################################################### 128### Get and check arguments 129##################################################################### 130OPTIONS=`"$GETOPT" -o n:dhitv \ 131 -l basevgname:,debug,help,import,quiet,test,verbose,version \ 132 -n "${SCRIPTNAME}" -- "$@"` 133[ $? -ne 0 ] && usage 134eval set -- "$OPTIONS" 135 136while true 137do 138 case $1 in 139 -n|--basevgname) 140 NEWVG="$2"; shift; shift 141 ;; 142 -i|--import) 143 IMPORT=1; shift 144 ;; 145 -t|--test) 146 TEST_OPT="-t" 147 shift 148 ;; 149 --quiet) 150 LVM_OPTS="--quiet ${LVM_OPTS}" 151 shift 152 ;; 153 -v|--verbose) 154 let VERBOSE_COUNT=VERBOSE_COUNT+1 155 if [ -z "$VERBOSE" ] 156 then 157 VERBOSE="-v" 158 else 159 VERBOSE="${VERBOSE}v" 160 fi 161 shift 162 ;; 163 -d|--debug) 164 if [ -z "$DEBUG" ] 165 then 166 DEBUG="-d" 167 set -x 168 else 169 DEBUG="${DEBUG}d" 170 fi 171 shift 172 ;; 173 --version) 174 "$LVM" version 175 shift 176 exit 0 177 ;; 178 -h|--help) 179 usage; shift 180 ;; 181 --) 182 shift; break 183 ;; 184 *) 185 usage 186 ;; 187 esac 188done 189 190# turn on DEBUG (special case associated with -v use) 191if [ -z "$DEBUG" -a $VERBOSE_COUNT -gt 3 ]; then 192 DEBUG="-d" 193 set -x 194fi 195 196# setup LVM_OPTS 197if [ -n "${DEBUG}" -o -n "${VERBOSE}" ] 198then 199 LVM_OPTS="${LVM_OPTS} ${DEBUG} ${VERBOSE}" 200fi 201 202# process remaining arguments (which should be disks) 203for ARG 204do 205 if [ -b "$ARG" ] 206 then 207 PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG" 2>/dev/null` 208 checkvalue $? "$ARG is not a PV." 209 PV_VGNAME=$(echo $PVS_OUT | $GREP -v '[[:space:]]+$') 210 [ -z "$PV_VGNAME" ] && die 3 "$ARG is not in a VG." 211 212 ln -s "$ARG" ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO} 213 DISKS="${DISKS} ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}" 214 DEVNO=$((${DEVNO}+1)) 215 else 216 die 3 "$ARG is not a block device." 217 fi 218done 219 220### check we have suitable values for important variables 221if [ -z "${DISKS}" ] 222then 223 usage 224fi 225 226##################################################################### 227### Get the existing state so we can use it later 228##################################################################### 229 230OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null` 231checkvalue $? "Current VG names could not be collected without errors" 232 233##################################################################### 234### Prepare the temporary lvm environment 235##################################################################### 236 237for BLOCK in ${DISKS} 238do 239 FILTER="\"a|^${BLOCK}$|\", ${FILTER}" 240done 241export FILTER="filter=[ ${FILTER} \"r|.*|\" ]" 242 243LVMCONF=${TMP_LVM_SYSTEM_DIR}/lvm.conf 244 245"$LVM" dumpconfig ${LVM_OPTS} | \ 246"$AWK" -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/.cache \ 247 -v CACHE_DIR=${TMP_LVM_SYSTEM_DIR}/cache \ 248 '/^[[:space:]]*filter[[:space:]]*=/{print ENVIRON["FILTER"];next} \ 249 /^[[:space:]]*scan[[:space:]]*=/{print "scan = [ \"" DEV "\" ]";next} \ 250 /^[[:space:]]*cache[[:space:]]*=/{print "cache = \"" CACHE "\"";next} \ 251 /^[[:space:]]*cache_dir[[:space:]]*=/{print "cache_dir = \"" CACHE_DIR "\"";next} \ 252 {print $0}' > ${LVMCONF} 253 254checkvalue $? "Failed to generate ${LVMCONF}" 255# Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping 256[ -n "${DEBUG}" ] && KEEP_TMP_LVM_SYSTEM_DIR=1 257 258# verify the config contains the filter, scan and cache_dir (or cache) config keywords 259"$GREP" -q '^[[:space:]]*filter[[:space:]]*=' ${LVMCONF} || \ 260 die 5 "Temporary lvm.conf must contain 'filter' config." 261"$GREP" -q '^[[:space:]]*scan[[:space:]]*=' ${LVMCONF} || \ 262 die 6 "Temporary lvm.conf must contain 'scan' config." 263 264# check for either 'cache' or 'cache_dir' config values 265"$GREP" -q '[[:space:]]*cache[[:space:]]*=' ${LVMCONF} 266CACHE_RET=$? 267"$GREP" -q '^[[:space:]]*cache_dir' ${LVMCONF} 268CACHE_DIR_RET=$? 269[ $CACHE_RET -eq 0 -o $CACHE_DIR_RET -eq 0 ] || \ 270 die 7 "Temporary lvm.conf must contain 'cache' or 'cache_dir' config." 271 272### set to use new lvm.conf 273export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR} 274 275 276##################################################################### 277### Rename the VG(s) and change the VG and PV UUIDs. 278##################################################################### 279 280PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null` 281checkvalue $? "PV info could not be collected without errors" 282 283# output VG info so each line looks like: name:exported?:disk1,disk2,... 284VGINFO=`echo "${PVINFO}" | \ 285 "$AWK" -F : '{{sub(/^[[:space:]]*/,"")} \ 286 {sub(/unknown device/,"unknown_device")} \ 287 {vg[$2]=$1","vg[$2]} if($3 ~ /^..x/){x[$2]="x"}} \ 288 END{for(k in vg){printf("%s:%s:%s\n", k, x[k], vg[k])}}'` 289checkvalue $? "PV info could not be parsed without errors" 290 291for VG in ${VGINFO} 292do 293 VGNAME=`echo "${VG}" | "$CUT" -d: -f1` 294 EXPORTED=`echo "${VG}" | "$CUT" -d: -f2` 295 PVLIST=`echo "${VG}" | "$CUT" -d: -f3- | "$TR" , ' '` 296 297 if [ -z "${VGNAME}" ] 298 then 299 FOLLOWLIST="" 300 for DEV in $PVLIST; do 301 FOLLOW=`"$READLINK" $DEV` 302 FOLLOWLIST="$FOLLOW $FOLLOWLIST" 303 done 304 die 8 "Specified PV(s) ($FOLLOWLIST) don't belong to a VG." 305 fi 306 307 if [ -n "${EXPORTED}" ] 308 then 309 if [ ${IMPORT} -eq 1 ] 310 then 311 "$LVM" vgimport ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" 312 checkvalue $? "Volume Group ${VGNAME} could not be imported" 313 else 314 echo "Volume Group ${VGNAME} exported, skipping." 315 continue 316 fi 317 fi 318 319 ### change the pv uuids 320 if [[ "${PVLIST}" =~ "unknown" ]] 321 then 322 echo "Volume Group ${VGNAME} has unknown PV(s), skipping." 323 echo "- Were all associated PV(s) supplied as arguments?" 324 continue 325 fi 326 327 for BLOCKDEV in ${PVLIST} 328 do 329 "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid ${BLOCKDEV} --config 'global{activation=0}' 330 checkvalue $? "Unable to change PV uuid for ${BLOCKDEV}" 331 done 332 333 NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"` 334 335 "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid "${VGNAME}" --config 'global{activation=0}' 336 checkvalue $? "Unable to change VG uuid for ${VGNAME}" 337 338 ## if the name isn't going to get changed dont even try. 339 if [ "${VGNAME}" != "${NEWVGNAME}" ] 340 then 341 "$LVM" vgrename ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" "${NEWVGNAME}" 342 checkvalue $? "Unable to rename ${VGNAME} to ${NEWVGNAME}" 343 fi 344 345 CHANGES_MADE=1 346done 347 348##################################################################### 349### Restore the old environment 350##################################################################### 351### set to use old lvm.conf 352if [ -z "${ORIG_LVM_SYS_DIR}" ] 353then 354 unset LVM_SYSTEM_DIR 355else 356 LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR} 357fi 358 359### update the device cache and make sure all 360### the device nodes we need are straight 361if [ ${CHANGES_MADE} -eq 1 ] 362then 363 "$LVM" vgscan ${LVM_OPTS} --mknodes 364fi 365 366exit 0 367