]>
Commit | Line | Data |
---|---|---|
75153387 | 1 | #!/bin/bash |
761e5741 MS |
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 | |
21 | RM=rm | |
22 | BASENAME=basename | |
23 | MKTEMP=mktemp | |
24 | AWK=awk | |
25 | CUT=cut | |
26 | TR=tr | |
27 | READLINK=readlink | |
28 | GREP=grep | |
29 | GETOPT=getopt | |
30 | ||
31 | # user may override lvm location by setting LVM_BINARY | |
32 | LVM="${LVM_BINARY:-lvm}" | |
33 | ||
34 | die() { | |
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 | ||
43 | function 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 | ||
64 | function 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 | ||
73 | function 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 | ||
89 | function cleanup { | |
90 | #set to use old lvm.conf | |
91 | LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR} | |
92 | ||
87422138 MS |
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 | |
761e5741 MS |
98 | } |
99 | ||
100 | SCRIPTNAME=`"$BASENAME" $0` | |
101 | ||
102 | ||
103 | if [ "$UID" != "0" -a "$EUID" != "0" ] | |
104 | then | |
105 | die 3 "${SCRIPTNAME} must be run as root." | |
106 | fi | |
107 | ||
108 | LVM_OPTS="" | |
109 | TEST_OPT="" | |
110 | DISKS="" | |
111 | # for compatibility: using mktemp -t rather than --tmpdir | |
112 | TMP_LVM_SYSTEM_DIR=`"$MKTEMP" -d -t snap.XXXXXXXX` | |
87422138 MS |
113 | KEEP_TMP_LVM_SYSTEM_DIR=0 |
114 | CHANGES_MADE=0 | |
761e5741 MS |
115 | IMPORT=0 |
116 | DEBUG="" | |
117 | VERBOSE="" | |
87422138 | 118 | VERBOSE_COUNT=0 |
761e5741 MS |
119 | DEVNO=0 |
120 | ||
121 | if [ -n "${LVM_SYSTEM_DIR}" ]; then | |
122 | export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}" | |
123 | fi | |
124 | ||
125 | trap 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 | ##################################################################### | |
130 | OPTIONS=`"$GETOPT" -o n:dhitv \ | |
131 | -l basevgname:,debug,help,import,quiet,test,verbose,version \ | |
132 | -n "${SCRIPTNAME}" -- "$@"` | |
133 | [ $? -ne 0 ] && usage | |
134 | eval set -- "$OPTIONS" | |
135 | ||
136 | while true | |
137 | do | |
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) | |
87422138 | 154 | let VERBOSE_COUNT=VERBOSE_COUNT+1 |
761e5741 MS |
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 | |
188 | done | |
189 | ||
87422138 MS |
190 | # turn on DEBUG (special case associated with -v use) |
191 | if [ -z "$DEBUG" -a $VERBOSE_COUNT -gt 3 ]; then | |
192 | DEBUG="-d" | |
193 | set -x | |
194 | fi | |
195 | ||
196 | # setup LVM_OPTS | |
197 | if [ -n "${DEBUG}" -o -n "${VERBOSE}" ] | |
198 | then | |
199 | LVM_OPTS="${LVM_OPTS} ${DEBUG} ${VERBOSE}" | |
200 | fi | |
201 | ||
761e5741 MS |
202 | # process remaining arguments (which should be disks) |
203 | for ARG | |
204 | do | |
205 | if [ -b "$ARG" ] | |
206 | then | |
87422138 MS |
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 | ||
761e5741 MS |
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 | |
218 | done | |
219 | ||
761e5741 MS |
220 | ### check we have suitable values for important variables |
221 | if [ -z "${DISKS}" ] | |
222 | then | |
223 | usage | |
224 | fi | |
225 | ||
226 | ##################################################################### | |
227 | ### Get the existing state so we can use it later | |
228 | ##################################################################### | |
229 | ||
230 | OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null` | |
231 | checkvalue $? "Current VG names could not be collected without errors" | |
232 | ||
233 | ##################################################################### | |
234 | ### Prepare the temporary lvm environment | |
235 | ##################################################################### | |
236 | ||
237 | for BLOCK in ${DISKS} | |
238 | do | |
239 | FILTER="\"a|^${BLOCK}$|\", ${FILTER}" | |
240 | done | |
241 | export FILTER="filter=[ ${FILTER} \"r|.*|\" ]" | |
242 | ||
243 | LVMCONF=${TMP_LVM_SYSTEM_DIR}/lvm.conf | |
244 | ||
77f771eb | 245 | # FIXME convert to cmdline override |
761e5741 MS |
246 | "$LVM" dumpconfig ${LVM_OPTS} | \ |
247 | "$AWK" -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/.cache \ | |
248 | -v CACHE_DIR=${TMP_LVM_SYSTEM_DIR}/cache \ | |
77f771eb ZK |
249 | '/^[ \t]*filter[ \t]*=/{print ENVIRON["FILTER"];next} \ |
250 | /^[ \t]*scan[ \t]*=/{print "scan = [ \"" DEV "\" ]";next} \ | |
251 | /^[ \t]*cache[ \t]*=/{print "cache = \"" CACHE "\"";next} \ | |
dae08226 | 252 | /^[ \t]*use_lvmetad[ \t]*=/{print "use_lvmetad = 0";next} \ |
77f771eb | 253 | /^[ \t]*cache_dir[ \t]*=/{print "cache_dir = \"" CACHE_DIR "\"";next} \ |
761e5741 MS |
254 | {print $0}' > ${LVMCONF} |
255 | ||
256 | checkvalue $? "Failed to generate ${LVMCONF}" | |
87422138 MS |
257 | # Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping |
258 | [ -n "${DEBUG}" ] && KEEP_TMP_LVM_SYSTEM_DIR=1 | |
761e5741 MS |
259 | |
260 | # verify the config contains the filter, scan and cache_dir (or cache) config keywords | |
261 | "$GREP" -q '^[[:space:]]*filter[[:space:]]*=' ${LVMCONF} || \ | |
262 | die 5 "Temporary lvm.conf must contain 'filter' config." | |
263 | "$GREP" -q '^[[:space:]]*scan[[:space:]]*=' ${LVMCONF} || \ | |
264 | die 6 "Temporary lvm.conf must contain 'scan' config." | |
265 | ||
266 | # check for either 'cache' or 'cache_dir' config values | |
267 | "$GREP" -q '[[:space:]]*cache[[:space:]]*=' ${LVMCONF} | |
268 | CACHE_RET=$? | |
269 | "$GREP" -q '^[[:space:]]*cache_dir' ${LVMCONF} | |
270 | CACHE_DIR_RET=$? | |
271 | [ $CACHE_RET -eq 0 -o $CACHE_DIR_RET -eq 0 ] || \ | |
272 | die 7 "Temporary lvm.conf must contain 'cache' or 'cache_dir' config." | |
273 | ||
274 | ### set to use new lvm.conf | |
275 | export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR} | |
276 | ||
277 | ||
278 | ##################################################################### | |
279 | ### Rename the VG(s) and change the VG and PV UUIDs. | |
280 | ##################################################################### | |
281 | ||
282 | PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null` | |
283 | checkvalue $? "PV info could not be collected without errors" | |
284 | ||
285 | # output VG info so each line looks like: name:exported?:disk1,disk2,... | |
286 | VGINFO=`echo "${PVINFO}" | \ | |
77f771eb | 287 | "$AWK" -F : '{{sub(/^[ \t]*/,"")} \ |
87422138 | 288 | {sub(/unknown device/,"unknown_device")} \ |
761e5741 MS |
289 | {vg[$2]=$1","vg[$2]} if($3 ~ /^..x/){x[$2]="x"}} \ |
290 | END{for(k in vg){printf("%s:%s:%s\n", k, x[k], vg[k])}}'` | |
291 | checkvalue $? "PV info could not be parsed without errors" | |
292 | ||
293 | for VG in ${VGINFO} | |
294 | do | |
295 | VGNAME=`echo "${VG}" | "$CUT" -d: -f1` | |
296 | EXPORTED=`echo "${VG}" | "$CUT" -d: -f2` | |
297 | PVLIST=`echo "${VG}" | "$CUT" -d: -f3- | "$TR" , ' '` | |
298 | ||
299 | if [ -z "${VGNAME}" ] | |
300 | then | |
301 | FOLLOWLIST="" | |
302 | for DEV in $PVLIST; do | |
303 | FOLLOW=`"$READLINK" $DEV` | |
304 | FOLLOWLIST="$FOLLOW $FOLLOWLIST" | |
305 | done | |
306 | die 8 "Specified PV(s) ($FOLLOWLIST) don't belong to a VG." | |
307 | fi | |
308 | ||
309 | if [ -n "${EXPORTED}" ] | |
310 | then | |
311 | if [ ${IMPORT} -eq 1 ] | |
312 | then | |
313 | "$LVM" vgimport ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" | |
314 | checkvalue $? "Volume Group ${VGNAME} could not be imported" | |
315 | else | |
316 | echo "Volume Group ${VGNAME} exported, skipping." | |
317 | continue | |
318 | fi | |
319 | fi | |
320 | ||
321 | ### change the pv uuids | |
322 | if [[ "${PVLIST}" =~ "unknown" ]] | |
323 | then | |
87422138 MS |
324 | echo "Volume Group ${VGNAME} has unknown PV(s), skipping." |
325 | echo "- Were all associated PV(s) supplied as arguments?" | |
761e5741 MS |
326 | continue |
327 | fi | |
328 | ||
329 | for BLOCKDEV in ${PVLIST} | |
330 | do | |
331 | "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid ${BLOCKDEV} --config 'global{activation=0}' | |
332 | checkvalue $? "Unable to change PV uuid for ${BLOCKDEV}" | |
333 | done | |
334 | ||
335 | NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"` | |
336 | ||
337 | "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid "${VGNAME}" --config 'global{activation=0}' | |
338 | checkvalue $? "Unable to change VG uuid for ${VGNAME}" | |
339 | ||
340 | ## if the name isn't going to get changed dont even try. | |
341 | if [ "${VGNAME}" != "${NEWVGNAME}" ] | |
342 | then | |
343 | "$LVM" vgrename ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" "${NEWVGNAME}" | |
344 | checkvalue $? "Unable to rename ${VGNAME} to ${NEWVGNAME}" | |
345 | fi | |
346 | ||
87422138 | 347 | CHANGES_MADE=1 |
761e5741 MS |
348 | done |
349 | ||
350 | ##################################################################### | |
351 | ### Restore the old environment | |
352 | ##################################################################### | |
353 | ### set to use old lvm.conf | |
87422138 MS |
354 | if [ -z "${ORIG_LVM_SYS_DIR}" ] |
355 | then | |
356 | unset LVM_SYSTEM_DIR | |
357 | else | |
358 | LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR} | |
359 | fi | |
761e5741 MS |
360 | |
361 | ### update the device cache and make sure all | |
362 | ### the device nodes we need are straight | |
87422138 MS |
363 | if [ ${CHANGES_MADE} -eq 1 ] |
364 | then | |
365 | "$LVM" vgscan ${LVM_OPTS} --mknodes | |
366 | fi | |
761e5741 MS |
367 | |
368 | exit 0 |