Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / lustre / scripts / lustre_config.in
1 #!/bin/bash
2
3 # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
4
5 #
6 # lustre_config - format and set up multiple lustre servers from a csv file
7 #
8 # This script is used to parse each line of a spreadsheet (csv file) and 
9 # execute remote commands to format (mkfs.lustre) every Lustre target 
10 # that will be part of the Lustre cluster.
11
12 # In addition, it can also verify the network connectivity and hostnames in 
13 # the cluster, configure Linux MD/LVM devices and produce High-Availability
14 # software configurations for Heartbeat or CluManager.
15 #
16 ################################################################################
17
18 # Usage
19 usage() {
20     cat >&2 <<EOF
21
22 Usage:  `basename $0` [options] <csv file>
23
24     This script is used to format and set up multiple lustre servers from a
25     csv file.
26
27     Options:
28     -h          help and examples
29     -a          select all the nodes from the csv file to operate on
30     -w hostname,hostname,...
31                 select the specified list of nodes (separated by commas) to
32                 operate on rather than all the nodes in the csv file
33     -x hostname,hostname,...
34                 exclude the specified list of nodes (separated by commas)
35     -t HAtype   produce High-Availability software configurations
36                 The argument following -t is used to indicate the High-
37                 Availability software type. The HA software types which 
38                 are currently supported are: hbv1 (Heartbeat version 1)
39                 and hbv2 (Heartbeat version 2).
40     -n          no net - don't verify network connectivity and hostnames
41                 in the cluster
42     -d          configure Linux MD/LVM devices before formatting the
43                 Lustre targets
44     -f          force-format the Lustre targets using --reformat option
45     -m          no fstab change - don't modify /etc/fstab to add the new
46                 Lustre targets
47                 If using this option, then the value of "mount options"
48                 item in the csv file will be passed to mkfs.lustre, else
49                 the value will be added into the /etc/fstab.
50     -u          upgrade Lustre targets from 1.4 to 1.6
51     -v          verbose mode
52     csv file    a spreadsheet that contains configuration parameters
53                 (separated by commas) for each target in a Lustre cluster
54
55 EOF
56     exit 1
57 }
58
59 # Samples 
60 sample() {
61     cat <<EOF
62
63 This script is used to parse each line of a spreadsheet (csv file) and 
64 execute remote commands to format (mkfs.lustre) every Lustre target 
65 that will be part of the Lustre cluster.
66
67 It can also optionally: 
68  * upgrade Lustre targets from 1.4 to 1.6
69  * verify the network connectivity and hostnames in the cluster
70  * configure Linux MD/LVM devices
71  * modify /etc/modprobe.conf to add Lustre networking info
72  * add the Lustre server info to /etc/fstab
73  * produce configurations for Heartbeat or CluManager.
74
75 There are 5 kinds of line formats in the csv file. They represent the following 
76 targets:
77 1) Linux MD device
78 The format is:
79 hostname,MD,md name,operation mode,options,raid level,component devices
80
81 hostname            hostname of the node in the cluster
82 MD                  marker of MD device line
83 md name             MD device name, e.g. /dev/md0
84 operation mode      create or remove, default is create
85 options             a "catchall" for other mdadm options, e.g. "-c 128"
86 raid level          raid level: 0,1,4,5,6,10,linear and multipath
87 component devices   block devices to be combined into the MD device
88                     Multiple devices are separated by space or by using
89                     shell expansions, e.g. "/dev/sd{a,b,c}"
90
91 2) Linux LVM PV (Physical Volume)
92 The format is:
93 hostname,PV,pv names,operation mode,options
94
95 hostname            hostname of the node in the cluster
96 PV                  marker of PV line
97 pv names            devices or loopback files to be initialized for later
98                     use by LVM or to be wiped the label, e.g. /dev/sda
99                     Multiple devices or files are separated by space or by
100                     using shell expansions, e.g. "/dev/sd{a,b,c}"
101 operation mode      create or remove, default is create
102 options             a "catchall" for other pvcreate/pvremove options
103                     e.g. "-vv"
104
105 3) Linux LVM VG (Volume Group)
106 The format is:
107 hostname,VG,vg name,operation mode,options,pv paths
108
109 hostname            hostname of the node in the cluster
110 VG                  marker of VG line
111 vg name             name of the volume group, e.g. ost_vg
112 operation mode      create or remove, default is create
113 options             a "catchall" for other vgcreate/vgremove options
114                     e.g. "-s 32M"
115 pv paths            physical volumes to construct this VG, required by
116                     create mode
117                     Multiple PVs are separated by space or by using
118                     shell expansions, e.g. "/dev/sd[k-m]1"
119
120 4) Linux LVM LV (Logical Volume)
121 The format is:
122 hostname,LV,lv name,operation mode,options,lv size,vg name
123
124 hostname            hostname of the node in the cluster
125 LV                  marker of LV line
126 lv name             name of the logical volume to be created (optional)
127                     or path of the logical volume to be removed (required
128                     by remove mode)
129 operation mode      create or remove, default is create
130 options             a "catchall" for other lvcreate/lvremove options
131                     e.g. "-i 2 -I 128"
132 lv size             size [kKmMgGtT] to be allocated for the new LV
133                     Default unit is megabytes.
134 vg name             name of the VG in which the new LV will be created
135
136 5) Lustre target
137 The format is:
138 hostname,module_opts,device name,mount point,device type,fsname,mgs nids,index,
139 format options,mkfs options,mount options,failover nids
140
141 hostname            hostname of the node in the cluster, must match "uname -n"
142 module_opts         Lustre networking module options
143 device name         Lustre target (block device or loopback file)
144 mount point         Lustre target mount point
145 device type         Lustre target type (mgs, mdt, ost, mgs|mdt, mdt|mgs)
146 fsname              Lustre filesystem name, should be limited to 8 characters 
147                     Default is "lustre".
148 mgs nids            NID(s) of remote mgs node, required for mdt and ost targets
149                     If this item is not given for an mdt, it is assumed that
150                     the mdt will also be an mgs, according to mkfs.lustre.
151 index               Lustre target index
152 format options      a "catchall" contains options to be passed to mkfs.lustre
153                     "--device-size", "--param", etc. all goes into this item.
154 mkfs options        format options to be wrapped with --mkfsoptions="" and
155                     passed to mkfs.lustre
156 mount options       If this script is invoked with "-m" option, then the value of
157                     this item will be wrapped with --mountfsoptions="" and passed
158                     to mkfs.lustre, else the value will be added into /etc/fstab.
159 failover nids       NID(s) of failover partner node
160
161 All the NIDs in one node are delimited by commas (','). When multiple nodes are
162 specified, they are delimited by a colon (':').
163
164 Items left blank will be set to defaults.
165
166 Example 1 - Simple, with combo MGS/MDT:
167 -------------------------------------------------------------------------------
168 # combo mdt/mgs
169 lustre-mgs,options lnet networks=tcp,/tmp/mgs,/mnt/mgs,mgs|mdt,,,,--device-size=10240
170
171 # ost0
172 lustre-ost,options lnet networks=tcp,/tmp/ost0,/mnt/ost0,ost,,lustre-mgs@tcp0,,--device-size=10240
173
174 # ost1
175 lustre-ost,options lnet networks=tcp,/tmp/ost1,/mnt/ost1,ost,,lustre-mgs@tcp0,,--device-size=10240
176 -------------------------------------------------------------------------------
177
178 Example 2 - Separate MGS/MDT, two networks interfaces:
179 -------------------------------------------------------------------------------
180 # mgs
181 lustre-mgs1,options lnet 'networks="tcp,elan"',/dev/sda,/mnt/mgs,mgs,,,,--quiet --param="sys.timeout=50",,"defaults,noauto","lustre-mgs2,2@elan"
182
183 # mdt
184 lustre-mdt1,options lnet 'networks="tcp,elan"',/dev/sdb,/mnt/mdt,mdt,lustre2,"lustre-mgs1,1@elan:lustre-mgs2,2@elan",,--quiet --param="lov.stripesize=4194304",-J size=16,"defaults,noauto",lustre-mdt2
185
186 # ost
187 lustre-ost1,options lnet 'networks="tcp,elan"',/dev/sdc,/mnt/ost,ost,lustre2,"lustre-mgs1,1@elan:lustre-mgs2,2@elan",,--quiet,-I 512,"defaults,noauto",lustre-ost2
188 -------------------------------------------------------------------------------
189
190 Example 3 - with combo MGS/MDT failover pair and OST failover pair:
191 -------------------------------------------------------------------------------
192 # combo mgs/mdt
193 lustre-mgs1,options lnet networks=tcp,/tmp/mgs,/mnt/mgs,mgs|mdt,,,,--quiet --device-size=10240,,,lustre-mgs2@tcp0
194
195 # combo mgs/mdt backup (--noformat)
196 lustre-mgs2,options lnet networks=tcp,/tmp/mgs,/mnt/mgs,mgs|mdt,,,,--quiet --device-size=10240 --noformat,,,lustre-mgs1@tcp0
197
198 # ost
199 lustre-ost1,options lnet networks=tcp,/tmp/ost1,/mnt/ost1,ost,,"lustre-mgs1@tcp0:lustre-mgs2@tcp0",,--quiet --device-size=10240,,,lustre-ost2@tcp0
200
201 # ost backup (--noformat) (note different device name)
202 lustre-ost2,options lnet networks=tcp,/tmp/ost2,/mnt/ost2,ost,,"lustre-mgs1@tcp0:lustre-mgs2@tcp0",,--quiet --device-size=10240 --noformat,,,lustre-ost1@tcp0
203 -------------------------------------------------------------------------------
204
205 Example 4 - Configure Linux MD/LVM devices before formatting Lustre targets:
206 -------------------------------------------------------------------------------
207 # MD device on mgsnode
208 mgsnode,MD,/dev/md0,,-q,1,/dev/sda1 /dev/sdb1
209
210 # MD/LVM devices on ostnode
211 ostnode,MD,/dev/md0,,-q -c 128,5,"/dev/sd{a,b,c}"
212 ostnode,MD,/dev/md1,,-q -c 128,5,"/dev/sd{d,e,f}"
213 ostnode,PV,/dev/md0 /dev/md1
214 ostnode,VG,ost_vg,,-s 32M,/dev/md0 /dev/md1
215 ostnode,LV,ost0,,-i 2 -I 128,300G,ost_vg
216 ostnode,LV,ost1,,-i 2 -I 128,300G,ost_vg
217
218 # combo mgs/mdt
219 mgsnode,options lnet networks=tcp,/dev/md0,/mnt/mgs,mgs|mdt,,,,--quiet
220
221 # ost0
222 ostnode,options lnet networks=tcp,/dev/ost_vg/ost0,/mnt/ost0,ost,,mgsnode,,--quiet
223
224 # ost1
225 ostnode,options lnet networks=tcp,/dev/ost_vg/ost1,/mnt/ost1,ost,,mgsnode,,--quiet
226 -------------------------------------------------------------------------------
227
228 EOF
229     exit 0
230 }
231
232 # Get the library of functions
233 . @scriptlibdir@/lc_common
234
235 #***************************** Global variables *****************************#
236 declare -a MGS_NODENAME             # node names of the MGS servers
237 declare -a MGS_IDX                  # indexes of MGSs in the global arrays
238 declare -i MGS_NUM                  # number of MGS servers in the cluster
239 declare -i INIT_IDX
240
241 declare -a NODE_NAMES               # node names in the failover group
242 declare -a TARGET_OPTS              # target services in one failover group
243
244 # All the items in the csv file
245 declare -a HOST_NAME MODULE_OPTS DEVICE_NAME MOUNT_POINT DEVICE_TYPE FS_NAME
246 declare -a MGS_NIDS INDEX FORMAT_OPTIONS MKFS_OPTIONS MOUNT_OPTIONS FAILOVERS
247
248 # Heartbeat software requires that node names in the configuration directive
249 # must (normally) match the "uname -n" of that machine. Since the value of the
250 # "failover nids" field in the csv file is the NID(s) of failover partner node,
251 # we have to figure out the corresponding hostname of that node.
252 declare -a FAILOVERS_NAMES
253
254 VERIFY_CONNECT=true
255 CONFIG_MD_LVM=false
256 MODIFY_FSTAB=true
257 UPGRADE_TARGET=false
258 VERBOSE_OUTPUT=false
259 # Get and check the positional parameters
260 while getopts "aw:x:t:ndfmuhv" OPTION; do
261     case $OPTION in
262     a)
263         [ -z "${SPECIFIED_NODELIST}" ] && [ -z "${EXCLUDED_NODELIST}" ] \
264         && USE_ALLNODES=true
265         NODELIST_OPT="${NODELIST_OPT} -a"
266         ;;
267     w)
268         USE_ALLNODES=false
269         SPECIFIED_NODELIST=$OPTARG
270         NODELIST_OPT="${NODELIST_OPT} -w ${SPECIFIED_NODELIST}"
271         ;;
272     x)
273         USE_ALLNODES=false
274         EXCLUDED_NODELIST=$OPTARG
275         NODELIST_OPT="${NODELIST_OPT} -x ${EXCLUDED_NODELIST}"
276         ;;
277     t)
278         HATYPE_OPT=$OPTARG
279         if [ "${HATYPE_OPT}" != "${HBVER_HBV1}" ] \
280         && [ "${HATYPE_OPT}" != "${HBVER_HBV2}" ] \
281         && [ "${HATYPE_OPT}" != "${HATYPE_CLUMGR}" ]; then
282             echo >&2 $"`basename $0`: Invalid HA software type" \
283                       "- ${HATYPE_OPT}!"
284             usage
285         fi
286         ;;
287     n)
288         VERIFY_CONNECT=false
289         ;;
290     d)
291         CONFIG_MD_LVM=true
292         ;;
293     f)
294         REFORMAT_OPTION=$"--reformat "
295         ;;
296     m)
297         MODIFY_FSTAB=false
298         ;;
299     u)
300         UPGRADE_TARGET=true 
301         ;;
302     h)
303         sample
304         ;;
305     v)
306         VERBOSE_OPT=$" -v"
307         VERBOSE_OUTPUT=true
308         ;;
309     ?)
310         usage 
311     esac
312 done
313
314 # Toss out the parameters we've already processed
315 shift  `expr $OPTIND - 1`
316
317 # Here we expect the csv file
318 if [ $# -eq 0 ]; then
319     echo >&2 $"`basename $0`: Missing csv file!"
320     usage
321 fi
322
323 # Check the items required for OSTs, MDTs and MGS
324 #
325 # When formatting an OST, the following items: hostname, module_opts,
326 # device name, device type and mgs nids, cannot have null value.
327 #
328 # When formatting an MDT or MGS, the following items: hostname,
329 # module_opts, device name and device type, cannot have null value.
330 check_item() {
331     # Check argument
332     if [ $# -eq 0 ]; then
333         echo >&2 $"`basename $0`: check_item() error: Missing argument"\
334                   "for function check_item()!"
335         return 1
336     fi
337
338     declare -i i=$1
339
340     # Check hostname, module_opts, device name and device type
341     if [ -z "${HOST_NAME[i]}" ]||[ -z "${MODULE_OPTS[i]}" ]\
342     ||[ -z "${DEVICE_NAME[i]}" ]||[ -z "${DEVICE_TYPE[i]}" ]; then
343         echo >&2 $"`basename $0`: check_item() error: Some required"\
344                   "item has null value! Check hostname, module_opts,"\
345                   "device name and device type!"
346         return 1
347     fi
348
349     # Check mgs nids
350     if [ "${DEVICE_TYPE[i]}" = "ost" ]&&[ -z "${MGS_NIDS[i]}" ]; then
351         echo >&2 $"`basename $0`: check_item() error: OST's mgs nids"\
352                   "item has null value!"
353         return 1
354     fi
355
356     # Check mount point
357     if [ -z "${MOUNT_POINT[i]}" ]; then
358         echo >&2 $"`basename $0`: check_item() error: mount"\
359                   "point item of target ${DEVICE_NAME[i]} has null value!"
360         return 1
361     fi
362
363     return 0
364 }
365
366 # Get the number of MGS nodes in the cluster
367 get_mgs_num() {
368     INIT_IDX=0
369     MGS_NUM=${#MGS_NODENAME[@]}
370     [ -z "${MGS_NODENAME[0]}" ] && let "INIT_IDX += 1" \
371     && let "MGS_NUM += 1"
372 }
373
374 # is_mgs_node hostname
375 # Verify whether @hostname is a MGS node
376 is_mgs_node() {
377     local host_name=$1
378     declare -i i
379
380     get_mgs_num
381     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
382         [ "${MGS_NODENAME[i]}" = "${host_name}" ] && return 0
383     done
384
385     return 1
386 }
387
388 # Check whether the MGS nodes are in the same failover group
389 check_mgs_group() {
390     declare -i i
391     declare -i j
392     declare -i idx
393     local mgs_node
394
395     get_mgs_num
396     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
397         mgs_node=${MGS_NODENAME[i]}
398         for ((j = ${INIT_IDX}; j < ${MGS_NUM}; j++)); do
399           [ "${MGS_NODENAME[j]}" = "${mgs_node}" ] && continue 1
400
401           idx=${MGS_IDX[j]}
402           if [ "${FAILOVERS_NAMES[idx]#*$mgs_node*}" = "${FAILOVERS_NAMES[idx]}" ]
403           then
404             echo >&2 $"`basename $0`: check_mgs_group() error:"\
405             "MGS node ${mgs_node} is not in the ${HOST_NAME[idx]}"\
406             "failover group!"
407             return 1
408           fi
409         done
410     done
411
412     return 0
413 }
414
415 # Get and check MGS servers.
416 # There should be no more than one MGS specified in the entire csv file.
417 check_mgs() {
418     declare -i i
419     declare -i j
420     declare -i exp_idx    # Index of explicit MGS servers
421     declare -i imp_idx    # Index of implicit MGS servers
422     local is_exp_mgs is_imp_mgs
423     local mgs_node
424
425     # Initialize the MGS_NODENAME and MGS_IDX arrays
426     unset MGS_NODENAME
427     unset MGS_IDX
428
429     exp_idx=1
430     imp_idx=1
431     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
432         is_exp_mgs=false
433         is_imp_mgs=false
434
435         # Check whether this node is an explicit MGS node 
436         # or an implicit one
437         if [ "${DEVICE_TYPE[i]#*mgs*}" != "${DEVICE_TYPE[i]}" ]; then
438             verbose_output "Explicit MGS target" \
439             "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
440             is_exp_mgs=true
441         fi
442
443         if [ "${DEVICE_TYPE[i]}" = "mdt" -a -z "${MGS_NIDS[i]}" ]; then
444             verbose_output "Implicit MGS target" \
445             "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
446             is_imp_mgs=true
447         fi
448
449         # Get and check MGS servers
450         if ${is_exp_mgs} || ${is_imp_mgs}; then
451             # Check whether more than one MGS target in one MGS node
452             if is_mgs_node ${HOST_NAME[i]}; then
453                 echo >&2 $"`basename $0`: check_mgs() error:"\
454                 "More than one MGS target in the same node -"\
455                 "\"${HOST_NAME[i]}\"!"
456                 return 1
457             fi
458
459             # Get and check primary MGS server and backup MGS server        
460             if [ "${FORMAT_OPTIONS[i]}" = "${FORMAT_OPTIONS[i]#*noformat*}" ]
461             then
462                 # Primary MGS server
463                 if [ -z "${MGS_NODENAME[0]}" ]; then
464                     if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
465                     || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
466                         echo >&2 $"`basename $0`: check_mgs() error:"\
467                         "There exist both explicit and implicit MGS"\
468                         "targets in the csv file!"
469                         return 1
470                     fi
471                     MGS_NODENAME[0]=${HOST_NAME[i]}
472                     MGS_IDX[0]=$i
473                 else
474                     mgs_node=${MGS_NODENAME[0]}
475                     if [ "${FAILOVERS_NAMES[i]#*$mgs_node*}" = "${FAILOVERS_NAMES[i]}" ]
476                     then
477                         echo >&2 $"`basename $0`: check_mgs() error:"\
478                         "More than one primary MGS nodes in the csv" \
479                         "file - ${MGS_NODENAME[0]} and ${HOST_NAME[i]}!"
480                     else
481                         echo >&2 $"`basename $0`: check_mgs() error:"\
482                         "MGS nodes ${MGS_NODENAME[0]} and ${HOST_NAME[i]}"\
483                         "are failover pair, one of them should use"\
484                         "\"--noformat\" in the format options item!"
485                     fi
486                     return 1
487                 fi
488             else    # Backup MGS server
489                 if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
490                 || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
491                     echo >&2 $"`basename $0`: check_mgs() error:"\
492                     "There exist both explicit and implicit MGS"\
493                     "targets in the csv file!"
494                     return 1
495                 fi
496
497                 if ${is_exp_mgs}; then # Explicit MGS
498                     MGS_NODENAME[exp_idx]=${HOST_NAME[i]}
499                     MGS_IDX[exp_idx]=$i
500                     exp_idx=$(( exp_idx + 1 ))
501                 else    # Implicit MGS
502                     MGS_NODENAME[imp_idx]=${HOST_NAME[i]}
503                     MGS_IDX[imp_idx]=$i
504                     imp_idx=$(( imp_idx + 1 ))
505                 fi
506             fi
507         fi #End of "if ${is_exp_mgs} || ${is_imp_mgs}"
508     done
509
510     # Check whether the MGS nodes are in the same failover group
511     if ! check_mgs_group; then
512         return 1
513     fi
514
515     return 0
516 }
517
518 # Construct the command line of mkfs.lustre
519 construct_mkfs_cmdline() {
520     # Check argument
521     if [ $# -eq 0 ]; then
522         echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
523                   "Missing argument for function construct_mkfs_cmdline()!"
524         return 1
525     fi
526
527     declare -i i=$1
528     local mgsnids mgsnids_str
529     local failnids failnids_str
530
531     if $UPGRADE_TARGET; then
532         MKFS_CMD="$TUNEFS --writeconf"
533     else
534         MKFS_CMD="$MKFS $REFORMAT_OPTION"
535     fi
536
537     case "${DEVICE_TYPE[i]}" in
538     "ost")
539         MKFS_CMD="$MKFS_CMD --ost"
540         ;;
541     "mdt")
542         MKFS_CMD="$MKFS_CMD --mdt"
543         ;;
544     "mgs")
545         MKFS_CMD="$MKFS_CMD --mgs"
546         ;;
547     "mdt|mgs" | "mgs|mdt")
548         MKFS_CMD="$MKFS_CMD --mgs --mdt"
549         ;;
550     *)
551         echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
552                   "Invalid device type - \"${DEVICE_TYPE[i]}\"!"
553         return 1
554         ;;
555     esac
556
557     if [ -n "${FS_NAME[i]}" ]; then
558         MKFS_CMD="$MKFS_CMD --fsname=${FS_NAME[i]}"
559     fi
560
561     if [ -n "${MGS_NIDS[i]}" ]; then
562         mgsnids_str=${MGS_NIDS[i]}
563         for mgsnids in ${mgsnids_str//:/ }; do
564             MKFS_CMD="$MKFS_CMD --mgsnode=$mgsnids"
565         done
566     fi
567
568     if [ -n "${INDEX[i]}" ]; then
569         MKFS_CMD="$MKFS_CMD --index=${INDEX[i]}"
570     fi
571
572     if [ -n "${FORMAT_OPTIONS[i]}" ]; then
573         MKFS_CMD="$MKFS_CMD ${FORMAT_OPTIONS[i]}"
574     fi
575
576     if ! $UPGRADE_TARGET && [ -n "${MKFS_OPTIONS[i]}" ]; then
577         MKFS_CMD="$MKFS_CMD --mkfsoptions=\"${MKFS_OPTIONS[i]}\""
578     fi
579
580     if [ -n "${MOUNT_OPTIONS[i]}" ] && ! $MODIFY_FSTAB; then
581         MKFS_CMD="$MKFS_CMD --mountfsoptions=\"${MOUNT_OPTIONS[i]}\""
582     fi
583
584     if [ -n "${FAILOVERS[i]}" ]; then
585         failnids_str=${FAILOVERS[i]}
586         for failnids in ${failnids_str//:/ }; do
587             MKFS_CMD="$MKFS_CMD --failnode=$failnids"
588         done
589     fi
590
591     MKFS_CMD="$MKFS_CMD ${DEVICE_NAME[i]}"
592     return 0
593
594
595 # Get all the node names in this failover group
596 get_nodenames() {
597     # Check argument
598     if [ $# -eq 0 ]; then
599         echo >&2 $"`basename $0`: get_nodenames() error: Missing"\
600                   "argument for function get_nodenames()!"
601         return 1
602     fi
603
604     declare -i i=$1
605     declare -i idx
606     local nids
607
608     # Initialize the NODE_NAMES array
609     unset NODE_NAMES
610
611     NODE_NAMES[0]=${HOST_NAME[i]}
612
613     idx=1
614     for nids in ${FAILOVERS_NAMES[i]//:/ }
615     do
616         NODE_NAMES[idx]=$(nids2hostname ${nids})
617         if [ ${PIPESTATUS[0]} -ne 0 ]; then
618             echo >&2 "${NODE_NAMES[idx]}"
619             return 1
620         fi
621     
622         idx=$idx+1
623     done
624
625     return 0
626 }
627
628 # Verify whether the format line has HA items
629 is_ha_line() {
630     declare -i i=$1
631
632     [ -n "${FAILOVERS[i]}" ] && return 0
633
634     return 1
635 }
636
637 # Produce HA software's configuration files
638 gen_ha_config() {
639     declare -i i=$1
640     declare -i idx
641     local  cmd_line
642
643     # Prepare parameters
644     # Hostnames option
645     HOSTNAME_OPT=${HOST_NAME[i]}
646
647     if ! get_nodenames $i; then
648         echo >&2 $"`basename $0`: gen_ha_config() error: Can not get the"\
649         "failover nodenames from failover nids - \"${FAILOVERS[i]}\" in"\
650         "the \"${HOST_NAME[i]}\" failover group!"
651         return 1
652     fi
653
654     for ((idx = 1; idx < ${#NODE_NAMES[@]}; idx++)); do
655         HOSTNAME_OPT=${HOSTNAME_OPT}$":"${NODE_NAMES[idx]}
656     done
657
658     # Target devices option
659     DEVICE_OPT=" -d "${TARGET_OPTS[0]}
660     for ((idx = 1; idx < ${#TARGET_OPTS[@]}; idx++)); do
661         DEVICE_OPT=${DEVICE_OPT}" -d "${TARGET_OPTS[idx]}
662     done
663
664     # Construct the generation script command line
665     case "${HATYPE_OPT}" in
666     "${HBVER_HBV1}"|"${HBVER_HBV2}")    # Heartbeat 
667         cmd_line=${GEN_HB_CONFIG}$" -r ${HATYPE_OPT} -n ${HOSTNAME_OPT}"
668         cmd_line=${cmd_line}${DEVICE_OPT}${VERBOSE_OPT}
669         ;;
670     "${HATYPE_CLUMGR}")                 # CluManager
671         cmd_line=${GEN_CLUMGR_CONFIG}$" -n ${HOSTNAME_OPT}"
672         cmd_line=${cmd_line}${DEVICE_OPT}${VERBOSE_OPT}
673         ;;
674     esac
675     
676     # Execute script to generate HA software's configuration files
677     verbose_output "Generating HA software's configurations in"\
678                "${HOST_NAME[i]} failover group..."
679     verbose_output "${cmd_line}"
680     eval $(echo "${cmd_line}")
681     if [ ${PIPESTATUS[0]} -ne 0 ]; then
682         return 1
683     fi
684     verbose_output "Generate HA software's configurations in"\
685                "${HOST_NAME[i]} failover group OK"
686     
687     return 0
688 }
689
690 # Configure HA software
691 config_ha() {
692     if $UPGRADE_TARGET || [ -z "${HATYPE_OPT}" ]; then
693         return 0
694     fi
695
696     declare -i i j k
697     declare -i prim_idx         # Index for PRIM_HOSTNAMES array
698     declare -i target_idx       # Index for TARGET_OPTS and HOST_INDEX arrays
699
700     declare -a PRIM_HOSTNAMES   # Primary hostnames in all the failover
701                                 # groups in the lustre cluster
702     declare -a HOST_INDEX       # Indices for the same node in all the 
703                                 # format lines in the csv file
704     local prim_host
705
706     # Initialize the PRIM_HOSTNAMES array
707     prim_idx=0
708     unset PRIM_HOSTNAMES
709
710     # Get failover groups and generate HA configuration files
711     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
712         prim_host=${HOST_NAME[i]}
713
714         for ((j = 0; j < ${#PRIM_HOSTNAMES[@]}; j++)); do
715             [ "${prim_host}" = "${PRIM_HOSTNAMES[j]}" ] && continue 2
716         done
717
718         target_idx=0
719         unset HOST_INDEX
720         unset TARGET_OPTS
721         for ((k = 0; k < ${#HOST_NAME[@]}; k++)); do
722             if [ "${prim_host}" = "${HOST_NAME[k]}" ] && is_ha_line "${k}"
723             then
724                 HOST_INDEX[target_idx]=$k
725                 TARGET_OPTS[target_idx]=${DEVICE_NAME[k]}:${MOUNT_POINT[k]}
726                 target_idx=$(( target_idx + 1 ))
727             fi
728         done
729
730         if [ ${#TARGET_OPTS[@]} -ne 0 ]; then
731             PRIM_HOSTNAMES[prim_idx]=${prim_host}
732             prim_idx=$(( prim_idx + 1 ))
733
734             if ! gen_ha_config ${HOST_INDEX[0]}; then
735                 return 1
736             fi
737         fi
738     done
739
740     if [ ${#PRIM_HOSTNAMES[@]} -eq 0 ]; then
741         verbose_output "There are no \"failover nids\" items in the"\
742         "csv file. No HA configuration files are generated!"
743     fi
744
745     rm -rf ${TMP_DIRS}
746     return 0
747 }
748
749 # Get all the items in the csv file and do some checks.
750 get_items() {
751     # Check argument
752     if [ $# -eq 0 ]; then
753         echo >&2 $"`basename $0`: get_items() error: Missing argument"\
754                   "for function get_items()!"
755         return 1
756     fi
757
758     CSV_FILE=$1
759     local LINE
760     local marker
761     local hostname
762     declare -i line_num=0
763     declare -i idx=0
764
765     exec 9< ${CSV_FILE}
766     while read -u 9 -r LINE; do
767         line_num=${line_num}+1
768         # verbose_output "Parsing line ${line_num}: $LINE"
769
770         # Get rid of the empty line
771         if [ -z "`echo ${LINE}|awk '/[[:alnum:]]/ {print $0}'`" ]; then
772             continue
773         fi
774
775         # Get rid of the comment line
776         if [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ]
777         then
778             continue
779         fi
780
781         # Skip the Linux MD/LVM line
782         marker=$(echo ${LINE} | cut -d, -f 2)
783         if [ "${marker}" = "${MD_MARKER}" -o "${marker}" = "${PV_MARKER}" ] \
784         || [ "${marker}" = "${VG_MARKER}" -o "${marker}" = "${LV_MARKER}" ]; then
785             continue
786         fi
787
788         # Skip the host which is not specified in the host list
789         if ! ${USE_ALLNODES}; then
790             hostname=$(echo ${LINE} | cut -d, -f 1)
791             ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
792         fi
793
794         # Parse the config line into CONFIG_ITEM
795         if ! parse_line "$LINE"; then
796             echo >&2 $"`basename $0`: parse_line() error: Occurred"\
797                   "on line ${line_num} in ${CSV_FILE}: $LINE"
798             return 1    
799         fi
800
801         HOST_NAME[idx]=${CONFIG_ITEM[0]}
802         MODULE_OPTS[idx]=${CONFIG_ITEM[1]}
803         DEVICE_NAME[idx]=${CONFIG_ITEM[2]}
804         MOUNT_POINT[idx]=${CONFIG_ITEM[3]}
805         DEVICE_TYPE[idx]=${CONFIG_ITEM[4]}
806         FS_NAME[idx]=${CONFIG_ITEM[5]}
807         MGS_NIDS[idx]=${CONFIG_ITEM[6]}
808         INDEX[idx]=${CONFIG_ITEM[7]}
809         FORMAT_OPTIONS[idx]=${CONFIG_ITEM[8]}
810         MKFS_OPTIONS[idx]=${CONFIG_ITEM[9]}
811         MOUNT_OPTIONS[idx]=${CONFIG_ITEM[10]}
812         FAILOVERS[idx]=${CONFIG_ITEM[11]}
813
814         MODULE_OPTS[idx]=`echo "${MODULE_OPTS[idx]}" | sed 's/"/\\\"/g'`
815
816         # Convert IP addresses in NIDs to hostnames
817         FAILOVERS_NAMES[idx]=$(ip2hostname_multi_node ${FAILOVERS[idx]})
818         if [ ${PIPESTATUS[0]} -ne 0 ]; then
819             echo >&2 "${FAILOVERS_NAMES[idx]}"
820             return 1
821         fi
822
823         # Check some required items for formatting target
824         if ! check_item $idx; then
825             echo >&2 $"`basename $0`: check_item() error:"\
826                   "Occurred on line ${line_num} in ${CSV_FILE}."
827             return 1    
828         fi
829
830         idx=${idx}+1
831     done
832
833     return 0
834 }
835
836 # check_lnet_connect hostname_index mgs_hostname
837 # Check whether the target node can contact the MGS node @mgs_hostname
838 # If @mgs_hostname is null, then it means the primary MGS node
839 check_lnet_connect() {
840     declare -i i=$1
841     local mgs_node=$2
842
843     local COMMAND RET_STR
844     local mgs_prim_nids
845     local nids_str=
846     local mgs_nid 
847     local ping_mgs
848
849     # Execute remote command to check that 
850     # this node can contact the MGS node
851     verbose_output "Checking lnet connectivity between" \
852     "${HOST_NAME[i]} and the MGS node ${mgs_node}"
853     mgs_prim_nids=`echo ${MGS_NIDS[i]} | awk -F: '{print $1}'`
854
855     if [ -z "${mgs_node}" -o $MGS_NUM -eq 1 ]; then
856         nids_str=${mgs_prim_nids}    # nids of primary MGS node
857         if [ -z "${nids_str}" ]; then
858             echo >&2 $"`basename $0`: check_lnet_connect() error:"\
859             "Check the mgs nids item of host ${HOST_NAME[i]}!"\
860             "Missing nids of the primary MGS node!"
861             return 1
862         fi
863     else
864         # Get the corresponding NID(s) of the MGS node ${mgs_node}
865         # from the "mgs nids" field
866         nids_str=$(get_mgs_nids ${mgs_node} ${MGS_NIDS[i]})
867         if [ ${PIPESTATUS[0]} -ne 0 ]; then
868             echo >&2 "${nids_str}"
869             return 1
870         fi
871     fi
872
873     ping_mgs=false
874     for mgs_nid in ${nids_str//,/ }
875     do
876         COMMAND=$"${LCTL} ping ${mgs_nid} 5 || echo failed 2>&1"
877         RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
878         if [ ${PIPESTATUS[0]} -eq 0 -a "${RET_STR}" = "${RET_STR#*failed*}" ]
879         then
880             # This node can contact the MGS node
881             verbose_output "${HOST_NAME[i]} can contact the MGS" \
882             "node ${mgs_node} by using nid \"${mgs_nid}\"!"
883             ping_mgs=true
884             break
885         fi
886     done
887
888     if ! ${ping_mgs}; then
889         echo >&2 "`basename $0`: check_lnet_connect() error:" \
890         "${HOST_NAME[i]} cannot contact the MGS node ${mgs_node}"\
891         "with nids - \"${nids_str}\"! Check ${LCTL} command!"
892         return 1
893     fi
894
895     return 0
896 }
897
898 # Start lnet network in the cluster node and check that 
899 # this node can contact the MGS node
900 check_lnet() {
901     if ! ${VERIFY_CONNECT}; then
902         return 0
903     fi
904
905     # Check argument
906     if [ $# -eq 0 ]; then
907         echo >&2 $"`basename $0`: check_lnet() error: Missing"\
908               "argument for function check_lnet()!"
909         return 1
910     fi
911
912     declare -i i=$1
913     declare -i j
914     local COMMAND RET_STR
915
916     # Execute remote command to start lnet network
917     verbose_output "Starting lnet network in ${HOST_NAME[i]}"
918     COMMAND="PATH=\$PATH:/sbin:/usr/sbin modprobe lnet; ${LCTL} network up 2>&1"
919     RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
920     if [ ${PIPESTATUS[0]} -ne 0 -o "${RET_STR}" = "${RET_STR#*LNET configured*}" ]
921     then
922         echo >&2 "`basename $0`: check_lnet() error: remote" \
923                  "${HOST_NAME[i]} error: ${RET_STR}"
924         return 1
925     fi
926
927     if is_mgs_node ${HOST_NAME[i]}; then
928         return 0
929     fi
930
931     # Execute remote command to check that 
932     # this node can contact the MGS node
933     for ((j = 0; j < ${MGS_NUM}; j++)); do
934         if ! check_lnet_connect $i ${MGS_NODENAME[j]}; then
935             return 1
936         fi
937     done
938
939     return 0
940 }
941
942 # Start lnet network in the MGS node
943 start_mgs_lnet() {
944     declare -i i
945     declare -i idx
946     local COMMAND
947
948     if [ -z "${MGS_NODENAME[0]}" -a  -z "${MGS_NODENAME[1]}" ]; then
949         if ${USE_ALLNODES}; then
950             verbose_output "There is no MGS target in the ${CSV_FILE} file."
951         else
952             verbose_output "There is no MGS target in the node list \"${NODES_TO_USE}\"."
953         fi
954         return 0
955     fi
956
957     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
958         # Execute remote command to add lnet options lines to 
959         # the MGS node's modprobe.conf/modules.conf
960         idx=${MGS_IDX[i]}
961         COMMAND=$"echo \"${MODULE_OPTS[${idx}]}\"|${MODULE_CONFIG}"
962         verbose_output "Adding lnet module options to ${MGS_NODENAME[i]}"
963         ${REMOTE} ${MGS_NODENAME[i]} "${COMMAND}" >&2 
964         if [ ${PIPESTATUS[0]} -ne 0 ]; then
965             echo >&2 "`basename $0`: start_mgs_lnet() error:"\
966                  "Failed to execute remote command to" \
967                  "add module options to ${MGS_NODENAME[i]}!"\
968                  "Check ${MODULE_CONFIG}!"
969             return 1
970         fi
971
972         # Start lnet network in the MGS node
973         if ! check_lnet ${idx}; then
974             return 1    
975         fi
976     done
977
978     return 0
979 }
980
981 # Execute remote command to add lnet options lines to remote nodes'
982 # modprobe.conf/modules.conf and format(mkfs.lustre) Lustre targets
983 mass_config() {
984     local COMMAND
985     declare -a REMOTE_PID 
986     declare -a REMOTE_CMD 
987     declare -i pid_num=0
988     declare -i i=0
989
990     if [ ${#HOST_NAME[@]} -eq 0 ]; then
991         verbose_output "There are no lustre targets specified."
992         return 0
993     fi
994
995     if ! $UPGRADE_TARGET; then
996         # Start lnet network in the MGS node
997         start_mgs_lnet || return 1    
998     fi
999
1000     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
1001         # Construct the command line of mkfs.lustre
1002         if ! construct_mkfs_cmdline $i; then
1003             return 1    
1004         fi
1005
1006         # create the mount point on the node
1007         COMMAND="mkdir -p ${MOUNT_POINT[i]}"
1008         verbose_output "Creating the mount point ${MOUNT_POINT[i]} on" \
1009                        "${HOST_NAME[i]}"
1010         ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2 
1011         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1012             echo >&2 "`basename $0`: mass_config() error:"\
1013                  "Failed to execute remote command to"\
1014                  "create the mountpoint on ${HOST_NAME[i]}!"
1015             return 1
1016         fi
1017
1018         if ! $UPGRADE_TARGET && ! is_mgs_node ${HOST_NAME[i]}; then
1019             # Execute remote command to add lnet options lines to 
1020             # modprobe.conf/modules.conf
1021             COMMAND=$"echo \"${MODULE_OPTS[i]}\"|${MODULE_CONFIG}"
1022             verbose_output "Adding lnet module options to" \
1023                        "${HOST_NAME[i]}"
1024             ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2 
1025             if [ ${PIPESTATUS[0]} -ne 0 ]; then
1026                 echo >&2 "`basename $0`: mass_config() error:"\
1027                      "Failed to execute remote command to"\
1028                      "add module options to ${HOST_NAME[i]}!"
1029                 return 1
1030             fi
1031
1032             # Check lnet networks
1033             if ! check_lnet $i; then
1034                 return 1    
1035             fi
1036         fi
1037
1038         # Execute remote command to format or upgrade Lustre target
1039         local OP
1040         $UPGRADE_TARGET && OP="Upgrading" || OP="Formatting"
1041         verbose_output "$OP Lustre target ${DEVICE_NAME[i]} on ${HOST_NAME[i]}..."
1042
1043         COMMAND="$EXPORT_PATH $MKFS_CMD"
1044         REMOTE_CMD[$pid_num]="$REMOTE ${HOST_NAME[i]} \"$COMMAND\""
1045         verbose_output "$OP command line is: ${REMOTE_CMD[$pid_num]}"
1046
1047         $REMOTE ${HOST_NAME[i]} "$COMMAND" &
1048         REMOTE_PID[$pid_num]=$!
1049         let pid_num=$pid_num+1
1050         sleep 1
1051     done
1052
1053     # Wait for the exit status of the background remote command
1054     verbose_output "Waiting for the return of the remote command..."
1055     fail_exit_status=false
1056     for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
1057         wait ${REMOTE_PID[${pid_num}]}
1058         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1059             echo >&2 "`basename $0`: mass_config() error: Failed"\
1060             "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
1061             fail_exit_status=true
1062         fi
1063     done
1064
1065     if ${fail_exit_status}; then
1066         return 1
1067     fi    
1068
1069     verbose_output "Success on all Lustre targets!"
1070     return 0
1071 }
1072
1073 # get_mntopts hostname device_name failovers
1074 # Construct the mount options of Lustre target @device_name in host @hostname
1075 get_mntopts() {
1076     local host_name=$1
1077     local device_name=$2
1078     local failovers=$3
1079     local mnt_opts=
1080     local ret_str
1081
1082     [ -n "${failovers}" ] && mnt_opts=defaults,noauto || mnt_opts=defaults
1083
1084     # Execute remote command to check whether the device
1085     # is a block device or not
1086     ret_str=$(${REMOTE} ${host_name} \
1087             "[ -b ${device_name} ] && echo block || echo loop" 2>&1)
1088     if [ ${PIPESTATUS[0]} -ne 0 -a -n "${ret_str}" ]; then
1089         echo "`basename $0`: get_mntopts() error:" \
1090         "remote command to ${host_name} error: ${ret_str}"
1091         return 1
1092     fi
1093
1094     if [ -z "${ret_str}" ]; then
1095         echo "`basename $0`: get_mntopts() error: remote error:" \
1096         "No results from remote!" \
1097         "Check network connectivity between the local host and ${host_name}!"
1098         return 1
1099     fi
1100
1101     [ "${ret_str}" != "${ret_str#*loop}" ] && mnt_opts=${mnt_opts},loop
1102
1103     echo ${mnt_opts}
1104     return 0
1105 }
1106
1107 # Execute remote command to modify /etc/fstab to add the new Lustre targets
1108 modify_fstab() {
1109     declare -i i
1110     local mntent mntopts device_name
1111     local COMMAND
1112
1113     if ! ${MODIFY_FSTAB}; then
1114         return 0    
1115     fi
1116
1117     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
1118         verbose_output "Modify /etc/fstab of host ${HOST_NAME[i]}"\
1119                    "to add Lustre target ${DEVICE_NAME[i]}"
1120         mntent=${DEVICE_NAME[i]}"\t\t"${MOUNT_POINT[i]}"\t\t"${FS_TYPE}
1121
1122         # Get mount options
1123         if [ -n "${MOUNT_OPTIONS[i]}" ]; then
1124             # The mount options already specified in the csv file.
1125             mntopts=${MOUNT_OPTIONS[i]}
1126         else
1127             mntopts=$(get_mntopts ${HOST_NAME[i]} ${DEVICE_NAME[i]}\
1128                     ${FAILOVERS[i]})
1129             if [ ${PIPESTATUS[0]} -ne 0 ]; then
1130                 echo >&2 "${mntopts}"
1131                 return 1
1132             fi
1133         fi
1134
1135         mntent=${mntent}"\t"${mntopts}"\t"0" "0
1136         verbose_output "`echo -e ${mntent}`"
1137
1138         # Execute remote command to modify /etc/fstab
1139         device_name=${DEVICE_NAME[i]//\//\\/}
1140         COMMAND=". @scriptlibdir@/lc_common; \
1141                 sed -i \"/^${device_name}\t/d\" \$(fcanon /etc/fstab); \
1142                 echo -e \"${mntent}\" >> \$(fcanon /etc/fstab)"
1143         ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2
1144         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1145             echo >&2 "`basename $0`: modify_fstab() error:"\
1146             "Failed to modify /etc/fstab of host ${HOST_NAME[i]}"\
1147             "to add Lustre target ${DEVICE_NAME[i]}!"
1148             return 1
1149         fi
1150     done
1151
1152     return 0
1153 }
1154
1155 # Main flow
1156 # Check the csv file
1157 if ! check_file $1; then
1158     exit 1 
1159 fi
1160
1161 # Get the list of nodes to be operated on
1162 NODES_TO_USE=$(get_nodelist)
1163 [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1
1164
1165 # Check the node list
1166 check_nodelist ${NODES_TO_USE} || exit 1
1167
1168 if ${VERIFY_CONNECT}; then
1169 # Check the network connectivity and hostnames
1170     echo "`basename $0`: Checking the cluster network connectivity"\
1171          "and hostnames..."
1172     if ! ${VERIFY_CLUSTER_NET} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1173         exit 1
1174     fi
1175     echo "`basename $0`: Check the cluster network connectivity"\
1176          "and hostnames OK!"
1177     echo
1178 fi
1179
1180 if $CONFIG_MD_LVM && ! $UPGRADE_TARGET; then
1181 # Configure Linux MD/LVM devices
1182     echo "`basename $0`: Configuring Linux MD/LVM devices..."
1183     if ! ${SCRIPT_CONFIG_MD} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1184         exit 1
1185     fi
1186
1187     if ! ${SCRIPT_CONFIG_LVM} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1188         exit 1
1189     fi
1190     echo "`basename $0`: Configure Linux MD/LVM devices OK!"
1191     echo
1192 fi
1193
1194 # Configure the Lustre cluster
1195 echo "`basename $0`: ******** Lustre cluster configuration START ********"
1196 if ! get_items ${CSV_FILE}; then
1197     exit 1
1198 fi
1199
1200 if ! check_mgs; then
1201     exit 1
1202 fi
1203
1204 if ! mass_config; then
1205     exit 1
1206 fi
1207
1208 if ! modify_fstab; then
1209     exit 1
1210 fi
1211
1212 # Produce HA software's configuration files
1213 if ! config_ha; then
1214     rm -rf ${TMP_DIRS}
1215     exit 1
1216 fi
1217
1218 echo "`basename $0`: ******** Lustre cluster configuration END **********"
1219
1220 exit 0