[linux-lvm] Re: Script to import hardware snapshoted VGs

chris procter chris-procter at talk21.com
Tue May 12 23:15:16 UTC 2009


> Chris,

> 
> I reviewed and tested your latest script.  I had issues with your use of
> whitespace delimiters (could be email ate it).  In any case, I reworked
> it so that delimiters are all non-whitespace.
>
> As you'll see in the below patch I made various changes:
> . cleaned up whitespace
> . added some extra negative checks for safety
> . use different lvm commands that lend themselves to simpler parsing
> . set cache_dir override in lvm.conf
> . eliminated vgscan at the end
> 
> Let me know what you think, I'll commit this into the LVM2 CVS once I
> get your feedback.
> 
> Thanks,
> Mike

I was using whitespace as a seperator in a
couple of places but mostly I was using ascii \002 which do appear to
have been mangled somewhere on the line. This may also explain why I
cant get your changes to apply cleanly, but they mostly look pretty
sensible.   I've inlined my comments below.



> diff --git a/vgimportclone b/vgimportclone
> index 7781d66..0bb77c6
> --- a/vgimportclone
> +++ b/vgimportclone
> @@ -1,6 +1,10 @@
> #!/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.
> @@ -8,6 +12,9 @@
> # 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)
> 
> 
> function getvgname {
> @@ -28,7 +35,7 @@ function getvgname {
>          NAME="${BASENAME}.$I"
>      done
>      echo "${NAME}"
> -} 
> +}
> 
> 
> function checkvalue {
> @@ -50,7 +57,7 @@ function usage {
>      echo -e "\t\t-h\t\t- Display this usage message"
>      echo -e "\t\t-i\t\t- Import any exported volume groups found"
>      echo -e "\t\t-n\t\t- Name for the new volume group(s)"
> -    echo -e "\t\t-l [path]\t - location of lvm.conf (default ${LVMCONF})"
> +    echo -e "\t\t-l [path]\t- location of lvm.conf (default ${LVMCONF})"
>      exit 0
> }
> 
> @@ -59,7 +66,7 @@ function cleanup {
>      LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
> 
>      if [ ${DEBUG} -eq 0 ]
> -    then 
> +    then
>           rm -r ${TMP_LVM_SYSTEM_DIR}
>      fi
> }
> @@ -76,13 +83,16 @@ fi
> SHOW=0
> DISKS=""
> LVMCONF="/etc/lvm/lvm.conf"
> -TMP_LVM_SYSTEM_DIR=`mktemp -d -t snap.XXXXXXXX`
> +TMP_LVM_SYSTEM_DIR=`mktemp -d --tmpdir snap.XXXXXXXX`


[root at boyle disktools]# mktemp -d --tmpdir snap.XXXXXXXX
mktemp: invalid option -- -
Usage: mktemp [-V] | [-dqtu] [-p prefix] [template]

So the long option doesn't work. The result of an old version of coreutils (coreutils-5.97-19.el5)  on CentOS5.3  not tested it on RHEL yet but if its different I'd be surprised!



> NOVGFLAG=0

The SHOW and  NOVGFLAG variables aren't actually used any more so could be removed (my fault!)


> IMPORT=0
> DEBUG=0
> DEVNO=0
> 
> -export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}"
> +if [ -n "${LVM_SYSTEM_DIR}" ]; then
> +    export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}"
> +    LVMCONF="${LVM_SYSTEM_DIR}/lvm.conf"
> +fi
> 
> trap  cleanup 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
> 
> @@ -94,7 +104,7 @@ do
>      case $1 in
>          -d)    DEBUG=1
>              exec 2> ./${SCRIPTNAME}.log
> -               set -x
> +            set -x
>              echo "Using $TMP_LVM_SYSTEM_DIR/lvm.conf"
>              shift
>              ;;
> @@ -135,13 +145,22 @@ LVM="${LVM_BINARY:-lvm}"
> "${LVM}" version >&/dev/null
> checkvalue $? "${LVM} doesn't look like an lvm binary."
> 
> +if [ -n "$NEWVG" ] ; then
> +    "${LVM}" vgs $NEWVG >& /dev/null
> +    if [ $? -eq 0 ] ; then
> +        echo "Error: New VG ($NEWVG) already exists." >&2
> +        exit 1
> +    fi
> +fi

You dont have to test if the name given with the -n option is unique
because it gets run through the getvgname function which uses the given
name as a starting point to build a unique name, but if you prefer to
enforce the given name rather then just use it as a hint then thats
fine.



> +
> +test -f $LVMCONF
> +checkvalue $? "${LVMCONF} doesn't exist."
> 
> #####################################################################
> ### Get the existing state so we can use it later
> #####################################################################
> 
> -OLDVGS=`"${LVM}" pvs --noheadings --trustcache --separator  2>/dev/null  | awk 
> -F  '/lvm2/{printf("%s ",$2)}'`
> -
> +OLDVGS=`"${LVM}" vgs -o name --noheadings 2>/dev/null`
> 
> #####################################################################
> ### Prepare the temporay lvm environment
> @@ -150,15 +169,19 @@ OLDVGS=`"${LVM}" pvs --noheadings --trustcache 
> --separator  2>/dev/null  | awk -
> ###create filter
> for BLOCK in ${DISKS}
> do
> -    FILTER="\"a|^${BLOCK}$|\",${FILTER}"
> +    FILTER="\"a|^${BLOCK}$|\", ${FILTER}"
> done
> 
> export FILTER="filter=[ ${FILTER} \"r|.*|\" ]"
> 
> -awk -v DEV=${TMP_LVM_SYSTEM_DIR} '/^[[:space:]]*filter/{print 
> ENVIRON["FILTER"];next}\
> -                    /^[[:space:]]*scan/{print "scan = [ \"" DEV  "\" ]";next} \
> +awk -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/cache \
> +                   '/^[[:space:]]*filter/{print ENVIRON["FILTER"];next} \
> +                    /^[[:space:]]*scan/{print "scan = [ \"" DEV "\" ]";next} \
> +                    /^[[:space:]]*cache_dir/{print "cache_dir = \"" CACHE 
> "\"";next} \
>                      {print $0}' < ${LVMCONF} > ${TMP_LVM_SYSTEM_DIR}/lvm.conf


This doesn't work on my setup, there is no cache_dir directive as far as I can tell, I think you're trying to change "cache =" instead so that should be:

awk -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/cache \
                   '/^[[:space:]]*filter/{print ENVIRON["FILTER"];next} \
                    /^[[:space:]]*scan/{print "scan = [ \"" DEV "\" ]";next} \
                    /^[[:space:]]*cache/{print "cache = \"" CACHE "\"";next} \
                     {print $0}' < ${LVMCONF} > ${TMP_LVM_SYSTEM_DIR}/lvm.conf

Otherwise it fails :(   Is this an "upgrade to the latest version" thing?


> +checkvalue $? "Failed to generate ${TMP_LVM_SYSTEM_DIR}/lvm.conf"
> +
> ### set to use new lvm.conf
> export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR}
> 
> @@ -166,18 +189,30 @@ export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR}
> #####################################################################
> ### Change the uuids.
> #####################################################################
> -PVSCAN=`"${LVM}" pvscan`

I was trying to keep the number of lvm commands to the minimum because they can be pretty slow on some of our systems with a lot of luns presented, so I was trading complicated awk for speed. Wether it really makes a difference is debatable though.


> -### create a space seperated list of VGs where each VG looks like: 
> nameexported?disk1disk2... 
> -VGS=`echo "${PVSCAN}" |awk 
> '$1~/PV/{for(i=1;i<=NF;i++){if($i=="VG"){vg[$(i+1)]=vg[$(i+1)]""$2} \
> -                            if($i=="exported"){x[$(i+2)]="x"}}} \
> -                END{for(k in vg){printf k""x[k] vg[k]" "}}'`


> +PVINFO=`"${LVM}" pvs -o pv_name,vg_name,vg_attr --noheadings --separator : | 
> sed -e "s/ //g"`
> +
> +# output VG info so each line looks like: name:exported?:disk1,disk2,...
> +VGINFO=`echo "${PVINFO}" | \
> +    awk -F : '{{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])}}'`

You could replace from PVINFO= to here with

VGINFO=`lvm pvs -o pv_name,vg_name,vg_attr --noheadings --separator : | \
                  awk -F : '{sub(/^[[:space:]]*/,"");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])}}'`

But I'm a sed-a-phobe ;)


> -for VG in ${VGS}
> +for VG in ${VGINFO}
> do
> -    VGNAME=`echo -e "${VG}" |cut -d -f1`
> -    EXPORTED=`echo -e "${VG}" | cut -d -f2`
> -    PVLIST=`echo -e "${VG}" | cut -d -f3-`
> +    VGNAME=`echo "${VG}" | cut -d: -f1`
> +    EXPORTED=`echo "${VG}" | cut -d: -f2`
> +    PVLIST=`echo "${VG}" | cut -d: -f3- | tr , ' '`

I was avoiding using colons as the seperator because its a valid character in device names, which is why I was then using control characters as separators, but given that we're now linking them all to sensible names it probably doesn't matter.


> +    if [ -z "${VGNAME}" ] ; then
> +        FOLLOWLIST=""
> +        for DEV in $PVLIST; do
> +            FOLLOW=`readlink $DEV`
> +            FOLLOWLIST="$FOLLOW $FOLLOWLIST"
> +        done
> +        echo "Error: Specified PV(s) ($FOLLOWLIST) don't belong to a VG." >&2
> +        exit 1
> +    fi
> 
>      if [ -n "${EXPORTED}" ]
>      then
> @@ -188,20 +223,18 @@ do
>              echo "Volume Group ${VGNAME} exported, skipping."
>              continue
>          fi
> -
>      fi
> 
>      ### change the pv uuids
> -    BLOCKDEVS=`echo ${PVLIST} | tr '' ' '`
> -    if [[ "${BLOCKDEVS}" =~ "unknown" ]]
> +    if [[ "${PVLIST}" =~ "unknown" ]]
>      then
>          echo "Volume Group ${VGNAME} incomplete, skipping."
>          continue
>      fi
> 
> -    for BLOCKDEV in ${BLOCKDEVS}
> +    for BLOCKDEV in ${PVLIST}
>      do
> -        "${LVM}" pvchange --uuid ${BLOCKDEV} --config 'global{activation=0}' 
> +        "${LVM}" pvchange --uuid ${BLOCKDEV} --config 'global{activation=0}'
>          checkvalue $? "Unable to change pvuuid for ${BLOCKDEV}"
>      done
> 
> @@ -225,12 +258,10 @@ done
> ### set to use old lvm.conf
> LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
> 
> -### make sure all the device nodes we need are straight
> -"${LVM}" vgmknodes >/dev/null
> -
> -### sort out caches.
> +### update the device cache and make sure all
> +### the device nodes we need are straight
> 
> "${LVM}" pvscan
> -"${LVM}" vgscan >/dev/null
>
> +"${LVM}" vgmknodes
> 
> exit 0



      




More information about the linux-lvm mailing list