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