Whamcloud - gitweb
b=13537
[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     -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
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 # Heartbeat software requires that node names in the configuration directive
247 # must (normally) match the "uname -n" of that machine. Since the value of the
248 # "failover nids" field in the csv file is the NID(s) of failover partner node,
249 # we have to figure out the corresponding hostname of that node.
250 declare -a FAILOVERS_NAMES
251
252 VERIFY_CONNECT=true
253 CONFIG_MD_LVM=false
254 MODIFY_FSTAB=true
255 VERBOSE_OUTPUT=false
256 # Get and check the positional parameters
257 while getopts "aw:x:t:ndfmhv" OPTION; do
258     case $OPTION in
259     a)
260         [ -z "${SPECIFIED_NODELIST}" ] && [ -z "${EXCLUDED_NODELIST}" ] \
261         && USE_ALLNODES=true
262         NODELIST_OPT="${NODELIST_OPT} -a"
263         ;;
264     w)
265         USE_ALLNODES=false
266         SPECIFIED_NODELIST=$OPTARG
267         NODELIST_OPT="${NODELIST_OPT} -w ${SPECIFIED_NODELIST}"
268         ;;
269     x)
270         USE_ALLNODES=false
271         EXCLUDED_NODELIST=$OPTARG
272         NODELIST_OPT="${NODELIST_OPT} -x ${EXCLUDED_NODELIST}"
273         ;;
274     t)
275         HATYPE_OPT=$OPTARG
276         if [ "${HATYPE_OPT}" != "${HBVER_HBV1}" ] \
277         && [ "${HATYPE_OPT}" != "${HBVER_HBV2}" ] \
278         && [ "${HATYPE_OPT}" != "${HATYPE_CLUMGR}" ]; then
279             echo >&2 $"`basename $0`: Invalid HA software type" \
280                       "- ${HATYPE_OPT}!"
281             usage
282         fi
283         ;;
284     n)
285         VERIFY_CONNECT=false
286         ;;
287     d)
288         CONFIG_MD_LVM=true
289         ;;
290     f)
291         REFORMAT_OPTION=$"--reformat "
292         ;;
293     m)
294         MODIFY_FSTAB=false
295         ;;
296     h)
297         sample
298         ;;
299     v)
300         VERBOSE_OPT=$" -v"
301         VERBOSE_OUTPUT=true
302         ;;
303     ?)
304         usage 
305     esac
306 done
307
308 # Toss out the parameters we've already processed
309 shift  `expr $OPTIND - 1`
310
311 # Here we expect the csv file
312 if [ $# -eq 0 ]; then
313     echo >&2 $"`basename $0`: Missing csv file!"
314     usage
315 fi
316
317 # Check the items required for OSTs, MDTs and MGS
318 #
319 # When formatting an OST, the following items: hostname, module_opts,
320 # device name, device type and mgs nids, cannot have null value.
321 #
322 # When formatting an MDT or MGS, the following items: hostname,
323 # module_opts, device name and device type, cannot have null value.
324 check_item() {
325     # Check argument
326     if [ $# -eq 0 ]; then
327         echo >&2 $"`basename $0`: check_item() error: Missing argument"\
328                   "for function check_item()!"
329         return 1
330     fi
331
332     declare -i i=$1
333
334     # Check hostname, module_opts, device name and device type
335     if [ -z "${HOST_NAME[i]}" ]||[ -z "${MODULE_OPTS[i]}" ]\
336     ||[ -z "${DEVICE_NAME[i]}" ]||[ -z "${DEVICE_TYPE[i]}" ]; then
337         echo >&2 $"`basename $0`: check_item() error: Some required"\
338                   "item has null value! Check hostname, module_opts,"\
339                   "device name and device type!"
340         return 1
341     fi
342
343     # Check mgs nids
344     if [ "${DEVICE_TYPE[i]}" = "ost" ]&&[ -z "${MGS_NIDS[i]}" ]; then
345         echo >&2 $"`basename $0`: check_item() error: OST's mgs nids"\
346                   "item has null value!"
347         return 1
348     fi
349
350     # Check mount point
351     if [ -z "${MOUNT_POINT[i]}" ]; then
352         echo >&2 $"`basename $0`: check_item() error: mount"\
353                   "point item of target ${DEVICE_NAME[i]} has null value!"
354         return 1
355     fi
356
357     return 0
358 }
359
360 # Get the number of MGS nodes in the cluster
361 get_mgs_num() {
362     INIT_IDX=0
363     MGS_NUM=${#MGS_NODENAME[@]}
364     [ -z "${MGS_NODENAME[0]}" ] && let "INIT_IDX += 1" \
365     && let "MGS_NUM += 1"
366 }
367
368 # is_mgs_node hostname
369 # Verify whether @hostname is a MGS node
370 is_mgs_node() {
371     local host_name=$1
372     declare -i i
373
374     get_mgs_num
375     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
376         [ "${MGS_NODENAME[i]}" = "${host_name}" ] && return 0
377     done
378
379     return 1
380 }
381
382 # Check whether the MGS nodes are in the same failover group
383 check_mgs_group() {
384     declare -i i
385     declare -i j
386     declare -i idx
387     local mgs_node
388
389     get_mgs_num
390     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
391         mgs_node=${MGS_NODENAME[i]}
392         for ((j = ${INIT_IDX}; j < ${MGS_NUM}; j++)); do
393           [ "${MGS_NODENAME[j]}" = "${mgs_node}" ] && continue 1
394
395           idx=${MGS_IDX[j]}
396           if [ "${FAILOVERS_NAMES[idx]#*$mgs_node*}" = "${FAILOVERS_NAMES[idx]}" ]
397           then
398             echo >&2 $"`basename $0`: check_mgs_group() error:"\
399             "MGS node ${mgs_node} is not in the ${HOST_NAME[idx]}"\
400             "failover group!"
401             return 1
402           fi
403         done
404     done
405
406     return 0
407 }
408
409 # Get and check MGS servers.
410 # There should be no more than one MGS specified in the entire csv file.
411 check_mgs() {
412     declare -i i
413     declare -i j
414     declare -i exp_idx    # Index of explicit MGS servers
415     declare -i imp_idx    # Index of implicit MGS servers
416     local is_exp_mgs is_imp_mgs
417     local mgs_node
418
419     # Initialize the MGS_NODENAME and MGS_IDX arrays
420     unset MGS_NODENAME
421     unset MGS_IDX
422
423     exp_idx=1
424     imp_idx=1
425     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
426         is_exp_mgs=false
427         is_imp_mgs=false
428
429         # Check whether this node is an explicit MGS node 
430         # or an implicit one
431         if [ "${DEVICE_TYPE[i]#*mgs*}" != "${DEVICE_TYPE[i]}" ]; then
432             verbose_output "Explicit MGS target" \
433             "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
434             is_exp_mgs=true
435         fi
436
437         if [ "${DEVICE_TYPE[i]}" = "mdt" -a -z "${MGS_NIDS[i]}" ]; then
438             verbose_output "Implicit MGS target" \
439             "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
440             is_imp_mgs=true
441         fi
442
443         # Get and check MGS servers
444         if ${is_exp_mgs} || ${is_imp_mgs}; then
445             # Check whether more than one MGS target in one MGS node
446             if is_mgs_node ${HOST_NAME[i]}; then
447                 echo >&2 $"`basename $0`: check_mgs() error:"\
448                 "More than one MGS target in the same node -"\
449                 "\"${HOST_NAME[i]}\"!"
450                 return 1
451             fi
452
453             # Get and check primary MGS server and backup MGS server        
454             if [ "${FORMAT_OPTIONS[i]}" = "${FORMAT_OPTIONS[i]#*noformat*}" ]
455             then
456                 # Primary MGS server
457                 if [ -z "${MGS_NODENAME[0]}" ]; then
458                     if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
459                     || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
460                         echo >&2 $"`basename $0`: check_mgs() error:"\
461                         "There exist both explicit and implicit MGS"\
462                         "targets in the csv file!"
463                         return 1
464                     fi
465                     MGS_NODENAME[0]=${HOST_NAME[i]}
466                     MGS_IDX[0]=$i
467                 else
468                     mgs_node=${MGS_NODENAME[0]}
469                     if [ "${FAILOVERS_NAMES[i]#*$mgs_node*}" = "${FAILOVERS_NAMES[i]}" ]
470                     then
471                         echo >&2 $"`basename $0`: check_mgs() error:"\
472                         "More than one primary MGS nodes in the csv" \
473                         "file - ${MGS_NODENAME[0]} and ${HOST_NAME[i]}!"
474                     else
475                         echo >&2 $"`basename $0`: check_mgs() error:"\
476                         "MGS nodes ${MGS_NODENAME[0]} and ${HOST_NAME[i]}"\
477                         "are failover pair, one of them should use"\
478                         "\"--noformat\" in the format options item!"
479                     fi
480                     return 1
481                 fi
482             else    # Backup MGS server
483                 if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
484                 || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
485                     echo >&2 $"`basename $0`: check_mgs() error:"\
486                     "There exist both explicit and implicit MGS"\
487                     "targets in the csv file!"
488                     return 1
489                 fi
490
491                 if ${is_exp_mgs}; then # Explicit MGS
492                     MGS_NODENAME[exp_idx]=${HOST_NAME[i]}
493                     MGS_IDX[exp_idx]=$i
494                     exp_idx=$(( exp_idx + 1 ))
495                 else    # Implicit MGS
496                     MGS_NODENAME[imp_idx]=${HOST_NAME[i]}
497                     MGS_IDX[imp_idx]=$i
498                     imp_idx=$(( imp_idx + 1 ))
499                 fi
500             fi
501         fi #End of "if ${is_exp_mgs} || ${is_imp_mgs}"
502     done
503
504     # Check whether the MGS nodes are in the same failover group
505     if ! check_mgs_group; then
506         return 1
507     fi
508
509     return 0
510 }
511
512 # Construct the command line of mkfs.lustre
513 construct_mkfs_cmdline() {
514     # Check argument
515     if [ $# -eq 0 ]; then
516         echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
517                   "Missing argument for function construct_mkfs_cmdline()!"
518         return 1
519     fi
520
521     declare -i i=$1
522     local mgsnids mgsnids_str
523     local failnids failnids_str
524
525     MKFS_CMD=${MKFS}$" "
526     MKFS_CMD=${MKFS_CMD}${REFORMAT_OPTION}
527
528     case "${DEVICE_TYPE[i]}" in
529     "ost")
530         MKFS_CMD=${MKFS_CMD}$"--ost "
531         ;;
532     "mdt")
533         MKFS_CMD=${MKFS_CMD}$"--mdt "
534         ;;
535     "mgs")
536         MKFS_CMD=${MKFS_CMD}$"--mgs "
537         ;;
538     "mdt|mgs" | "mgs|mdt")
539         MKFS_CMD=${MKFS_CMD}$"--mdt --mgs "
540         ;;
541     *)
542         echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
543                   "Invalid device type - \"${DEVICE_TYPE[i]}\"!"
544         return 1
545         ;;
546     esac
547
548     if [ -n "${FS_NAME[i]}" ]; then
549         MKFS_CMD=${MKFS_CMD}$"--fsname="${FS_NAME[i]}$" "
550     fi
551
552     if [ -n "${MGS_NIDS[i]}" ]; then
553         mgsnids_str=${MGS_NIDS[i]}
554         for mgsnids in ${mgsnids_str//:/ }; do
555             MKFS_CMD=${MKFS_CMD}$"--mgsnode="${mgsnids}$" "
556         done
557     fi
558
559     if [ -n "${INDEX[i]}" ]; then
560         MKFS_CMD=${MKFS_CMD}$"--index="${INDEX[i]}$" "
561     fi
562
563     if [ -n "${FORMAT_OPTIONS[i]}" ]; then
564         MKFS_CMD=${MKFS_CMD}${FORMAT_OPTIONS[i]}$" "
565     fi
566
567     if [ -n "${MKFS_OPTIONS[i]}" ]; then
568         MKFS_CMD=${MKFS_CMD}$"--mkfsoptions="$"\""${MKFS_OPTIONS[i]}$"\""$" "
569     fi
570
571     if [ -n "${MOUNT_OPTIONS[i]}" ]; then
572         if ! ${MODIFY_FSTAB}; then
573             MKFS_CMD=${MKFS_CMD}$"--mountfsoptions="$"\""${MOUNT_OPTIONS[i]}$"\""$" "
574         fi
575     fi
576
577     if [ -n "${FAILOVERS[i]}" ]; then
578         failnids_str=${FAILOVERS[i]}
579         for failnids in ${failnids_str//:/ }; do
580             MKFS_CMD=${MKFS_CMD}$"--failnode="${failnids}$" "
581         done
582     fi
583
584     MKFS_CMD=${MKFS_CMD}${DEVICE_NAME[i]}
585     return 0
586
587
588 # Get all the node names in this failover group
589 get_nodenames() {
590     # Check argument
591     if [ $# -eq 0 ]; then
592         echo >&2 $"`basename $0`: get_nodenames() error: Missing"\
593                   "argument for function get_nodenames()!"
594         return 1
595     fi
596
597     declare -i i=$1
598     declare -i idx
599     local nids
600
601     # Initialize the NODE_NAMES array
602     unset NODE_NAMES
603
604     NODE_NAMES[0]=${HOST_NAME[i]}
605
606     idx=1
607     for nids in ${FAILOVERS_NAMES[i]//:/ }
608     do
609         NODE_NAMES[idx]=$(nids2hostname ${nids})
610         if [ ${PIPESTATUS[0]} -ne 0 ]; then
611             echo >&2 "${NODE_NAMES[idx]}"
612             return 1
613         fi
614     
615         idx=$idx+1
616     done
617
618     return 0
619 }
620
621 # Verify whether the format line has HA items
622 is_ha_line() {
623     declare -i i=$1
624
625     [ -n "${FAILOVERS[i]}" ] && return 0
626
627     return 1
628 }
629
630 # Produce HA software's configuration files
631 gen_ha_config() {
632     declare -i i=$1
633     declare -i idx
634     local  cmd_line
635
636     # Prepare parameters
637     # Hostnames option
638     HOSTNAME_OPT=${HOST_NAME[i]}
639
640     if ! get_nodenames $i; then
641         echo >&2 $"`basename $0`: gen_ha_config() error: Can not get the"\
642         "failover nodenames from failover nids - \"${FAILOVERS[i]}\" in"\
643         "the \"${HOST_NAME[i]}\" failover group!"
644         return 1
645     fi
646
647     for ((idx = 1; idx < ${#NODE_NAMES[@]}; idx++)); do
648         HOSTNAME_OPT=${HOSTNAME_OPT}$":"${NODE_NAMES[idx]}
649     done
650
651     # Target devices option
652     DEVICE_OPT=" -d "${TARGET_OPTS[0]}
653     for ((idx = 1; idx < ${#TARGET_OPTS[@]}; idx++)); do
654         DEVICE_OPT=${DEVICE_OPT}" -d "${TARGET_OPTS[idx]}
655     done
656
657     # Construct the generation script command line
658     case "${HATYPE_OPT}" in
659     "${HBVER_HBV1}"|"${HBVER_HBV2}")    # Heartbeat 
660         cmd_line=${GEN_HB_CONFIG}$" -r ${HATYPE_OPT} -n ${HOSTNAME_OPT}"
661         cmd_line=${cmd_line}${DEVICE_OPT}${VERBOSE_OPT}
662         ;;
663     "${HATYPE_CLUMGR}")                 # CluManager
664         cmd_line=${GEN_CLUMGR_CONFIG}$" -n ${HOSTNAME_OPT}"
665         cmd_line=${cmd_line}${DEVICE_OPT}${VERBOSE_OPT}
666         ;;
667     esac
668     
669     # Execute script to generate HA software's configuration files
670     verbose_output "Generating HA software's configurations in"\
671                "${HOST_NAME[i]} failover group..."
672     verbose_output "${cmd_line}"
673     eval $(echo "${cmd_line}")
674     if [ ${PIPESTATUS[0]} -ne 0 ]; then
675         return 1
676     fi
677     verbose_output "Generate HA software's configurations in"\
678                "${HOST_NAME[i]} failover group OK"
679     
680     return 0
681 }
682
683 # Configure HA software
684 config_ha() {
685     if [ -z "${HATYPE_OPT}" ]; then
686         return 0
687     fi
688
689     declare -i i j k
690     declare -i prim_idx         # Index for PRIM_HOSTNAMES array
691     declare -i target_idx       # Index for TARGET_OPTS and HOST_INDEX arrays
692
693     declare -a PRIM_HOSTNAMES   # Primary hostnames in all the failover
694                                 # groups in the lustre cluster
695     declare -a HOST_INDEX       # Indices for the same node in all the 
696                                 # format lines in the csv file
697     local prim_host
698
699     # Initialize the PRIM_HOSTNAMES array
700     prim_idx=0
701     unset PRIM_HOSTNAMES
702
703     # Get failover groups and generate HA configuration files
704     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
705         prim_host=${HOST_NAME[i]}
706
707         for ((j = 0; j < ${#PRIM_HOSTNAMES[@]}; j++)); do
708             [ "${prim_host}" = "${PRIM_HOSTNAMES[j]}" ] && continue 2
709         done
710
711         target_idx=0
712         unset HOST_INDEX
713         unset TARGET_OPTS
714         for ((k = 0; k < ${#HOST_NAME[@]}; k++)); do
715             if [ "${prim_host}" = "${HOST_NAME[k]}" ] && is_ha_line "${k}"
716             then
717                 HOST_INDEX[target_idx]=$k
718                 TARGET_OPTS[target_idx]=${DEVICE_NAME[k]}:${MOUNT_POINT[k]}
719                 target_idx=$(( target_idx + 1 ))
720             fi
721         done
722
723         if [ ${#TARGET_OPTS[@]} -ne 0 ]; then
724             PRIM_HOSTNAMES[prim_idx]=${prim_host}
725             prim_idx=$(( prim_idx + 1 ))
726
727             if ! gen_ha_config ${HOST_INDEX[0]}; then
728                 return 1
729             fi
730         fi
731     done
732
733     if [ ${#PRIM_HOSTNAMES[@]} -eq 0 ]; then
734         verbose_output "There are no \"failover nids\" items in the"\
735         "csv file. No HA configuration files are generated!"
736     fi
737
738     rm -rf ${TMP_DIRS}
739     return 0
740 }
741
742 # Get all the items in the csv file and do some checks.
743 get_items() {
744     # Check argument
745     if [ $# -eq 0 ]; then
746         echo >&2 $"`basename $0`: get_items() error: Missing argument"\
747                   "for function get_items()!"
748         return 1
749     fi
750
751     CSV_FILE=$1
752     local LINE
753     local marker
754     local hostname
755     declare -i line_num=0
756     declare -i idx=0
757
758     exec 9< ${CSV_FILE}
759     while read -u 9 -r LINE; do
760         line_num=${line_num}+1
761         # verbose_output "Parsing line ${line_num}: $LINE"
762
763         # Get rid of the empty line
764         if [ -z "`echo ${LINE}|awk '/[[:alnum:]]/ {print $0}'`" ]; then
765             continue
766         fi
767
768         # Get rid of the comment line
769         if [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ]
770         then
771             continue
772         fi
773
774         # Skip the Linux MD/LVM line
775         marker=$(echo ${LINE} | cut -d, -f 2)
776         if [ "${marker}" = "${MD_MARKER}" -o "${marker}" = "${PV_MARKER}" ] \
777         || [ "${marker}" = "${VG_MARKER}" -o "${marker}" = "${LV_MARKER}" ]; then
778             continue
779         fi
780
781         # Skip the host which is not specified in the host list
782         if ! ${USE_ALLNODES}; then
783             hostname=$(echo ${LINE} | cut -d, -f 1)
784             ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
785         fi
786
787         # Parse the config line into CONFIG_ITEM
788         if ! parse_line "$LINE"; then
789             echo >&2 $"`basename $0`: parse_line() error: Occurred"\
790                   "on line ${line_num} in ${CSV_FILE}: $LINE"
791             return 1    
792         fi
793
794         HOST_NAME[idx]=${CONFIG_ITEM[0]}
795         MODULE_OPTS[idx]=${CONFIG_ITEM[1]}
796         DEVICE_NAME[idx]=${CONFIG_ITEM[2]}
797         MOUNT_POINT[idx]=${CONFIG_ITEM[3]}
798         DEVICE_TYPE[idx]=${CONFIG_ITEM[4]}
799         FS_NAME[idx]=${CONFIG_ITEM[5]}
800         MGS_NIDS[idx]=${CONFIG_ITEM[6]}
801         INDEX[idx]=${CONFIG_ITEM[7]}
802         FORMAT_OPTIONS[idx]=${CONFIG_ITEM[8]}
803         MKFS_OPTIONS[idx]=${CONFIG_ITEM[9]}
804         MOUNT_OPTIONS[idx]=${CONFIG_ITEM[10]}
805         FAILOVERS[idx]=${CONFIG_ITEM[11]}
806
807         MODULE_OPTS[idx]=`echo "${MODULE_OPTS[idx]}" | sed 's/"/\\\"/g'`
808
809         # Convert IP addresses in NIDs to hostnames
810         FAILOVERS_NAMES[idx]=$(ip2hostname_multi_node ${FAILOVERS[idx]})
811         if [ ${PIPESTATUS[0]} -ne 0 ]; then
812             echo >&2 "${FAILOVERS_NAMES[idx]}"
813             return 1
814         fi
815
816         # Check some required items for formatting target
817         if ! check_item $idx; then
818             echo >&2 $"`basename $0`: check_item() error:"\
819                   "Occurred on line ${line_num} in ${CSV_FILE}."
820             return 1    
821         fi
822
823         idx=${idx}+1
824     done
825
826     return 0
827 }
828
829 # check_lnet_connect hostname_index mgs_hostname
830 # Check whether the target node can contact the MGS node @mgs_hostname
831 # If @mgs_hostname is null, then it means the primary MGS node
832 check_lnet_connect() {
833     declare -i i=$1
834     local mgs_node=$2
835
836     local COMMAND RET_STR
837     local mgs_prim_nids
838     local nids_str=
839     local mgs_nid 
840     local ping_mgs
841
842     # Execute remote command to check that 
843     # this node can contact the MGS node
844     verbose_output "Checking lnet connectivity between" \
845     "${HOST_NAME[i]} and the MGS node ${mgs_node}"
846     mgs_prim_nids=`echo ${MGS_NIDS[i]} | awk -F: '{print $1}'`
847
848     if [ -z "${mgs_node}" -o $MGS_NUM -eq 1 ]; then
849         nids_str=${mgs_prim_nids}    # nids of primary MGS node
850         if [ -z "${nids_str}" ]; then
851             echo >&2 $"`basename $0`: check_lnet_connect() error:"\
852             "Check the mgs nids item of host ${HOST_NAME[i]}!"\
853             "Missing nids of the primary MGS node!"
854             return 1
855         fi
856     else
857         # Get the corresponding NID(s) of the MGS node ${mgs_node}
858         # from the "mgs nids" field
859         nids_str=$(get_mgs_nids ${mgs_node} ${MGS_NIDS[i]})
860         if [ ${PIPESTATUS[0]} -ne 0 ]; then
861             echo >&2 "${nids_str}"
862             return 1
863         fi
864     fi
865
866     ping_mgs=false
867     for mgs_nid in ${nids_str//,/ }
868     do
869         COMMAND=$"${LCTL} ping ${mgs_nid} 5 || echo failed 2>&1"
870         RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
871         if [ ${PIPESTATUS[0]} -eq 0 -a "${RET_STR}" = "${RET_STR#*failed*}" ]
872         then
873             # This node can contact the MGS node
874             verbose_output "${HOST_NAME[i]} can contact the MGS" \
875             "node ${mgs_node} by using nid \"${mgs_nid}\"!"
876             ping_mgs=true
877             break
878         fi
879     done
880
881     if ! ${ping_mgs}; then
882         echo >&2 "`basename $0`: check_lnet_connect() error:" \
883         "${HOST_NAME[i]} cannot contact the MGS node ${mgs_node}"\
884         "with nids - \"${nids_str}\"! Check ${LCTL} command!"
885         return 1
886     fi
887
888     return 0
889 }
890
891 # Start lnet network in the cluster node and check that 
892 # this node can contact the MGS node
893 check_lnet() {
894     if ! ${VERIFY_CONNECT}; then
895         return 0
896     fi
897
898     # Check argument
899     if [ $# -eq 0 ]; then
900         echo >&2 $"`basename $0`: check_lnet() error: Missing"\
901               "argument for function check_lnet()!"
902         return 1
903     fi
904
905     declare -i i=$1
906     declare -i j
907     local COMMAND RET_STR
908
909     # Execute remote command to start lnet network
910     verbose_output "Starting lnet network in ${HOST_NAME[i]}"
911     COMMAND="PATH=\$PATH:/sbin:/usr/sbin modprobe lnet; ${LCTL} network up 2>&1"
912     RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
913     if [ ${PIPESTATUS[0]} -ne 0 -o "${RET_STR}" = "${RET_STR#*LNET configured*}" ]
914     then
915         echo >&2 "`basename $0`: check_lnet() error: remote" \
916                  "${HOST_NAME[i]} error: ${RET_STR}"
917         return 1
918     fi
919
920     if is_mgs_node ${HOST_NAME[i]}; then
921         return 0
922     fi
923
924     # Execute remote command to check that 
925     # this node can contact the MGS node
926     for ((j = 0; j < ${MGS_NUM}; j++)); do
927         if ! check_lnet_connect $i ${MGS_NODENAME[j]}; then
928             return 1
929         fi
930     done
931
932     return 0
933 }
934
935 # Start lnet network in the MGS node
936 start_mgs_lnet() {
937     declare -i i
938     declare -i idx
939     local COMMAND
940
941     if [ -z "${MGS_NODENAME[0]}" -a  -z "${MGS_NODENAME[1]}" ]; then
942         if ${USE_ALLNODES}; then
943             verbose_output "There is no MGS target in the ${CSV_FILE} file."
944         else
945             verbose_output "There is no MGS target in the node list \"${NODES_TO_USE}\"."
946         fi
947         return 0
948     fi
949
950     for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
951         # Execute remote command to add lnet options lines to 
952         # the MGS node's modprobe.conf/modules.conf
953         idx=${MGS_IDX[i]}
954         COMMAND=$"echo \"${MODULE_OPTS[${idx}]}\"|${MODULE_CONFIG}"
955         verbose_output "Adding lnet module options to ${MGS_NODENAME[i]}"
956         ${REMOTE} ${MGS_NODENAME[i]} "${COMMAND}" >&2 
957         if [ ${PIPESTATUS[0]} -ne 0 ]; then
958             echo >&2 "`basename $0`: start_mgs_lnet() error:"\
959                  "Failed to execute remote command to" \
960                  "add module options to ${MGS_NODENAME[i]}!"\
961                  "Check ${MODULE_CONFIG}!"
962             return 1
963         fi
964
965         # Start lnet network in the MGS node
966         if ! check_lnet ${idx}; then
967             return 1    
968         fi
969     done
970
971     return 0
972 }
973
974 # Execute remote command to add lnet options lines to remote nodes'
975 # modprobe.conf/modules.conf and format(mkfs.lustre) Lustre targets
976 mass_config() {
977     local COMMAND
978     declare -a REMOTE_PID 
979     declare -a REMOTE_CMD 
980     declare -i pid_num=0
981     declare -i i=0
982
983     if [ ${#HOST_NAME[@]} -eq 0 ]; then
984         verbose_output "There are no Lustre targets to be formatted."
985         return 0
986     fi
987
988     # Start lnet network in the MGS node
989     if ! start_mgs_lnet; then
990         return 1    
991     fi
992
993     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
994         # Construct the command line of mkfs.lustre
995         if ! construct_mkfs_cmdline $i; then
996             return 1    
997         fi
998
999         # create the mount point on the node
1000         COMMAND="mkdir -p ${MOUNT_POINT[i]}"
1001         verbose_output "Creating the mount point ${MOUNT_POINT[i]} on" \
1002                        "${HOST_NAME[i]}"
1003         ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2 
1004         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1005             echo >&2 "`basename $0`: mass_config() error:"\
1006                  "Failed to execute remote command to"\
1007                  "create the mountpoint on ${HOST_NAME[i]}!"
1008             return 1
1009         fi
1010
1011         if ! is_mgs_node ${HOST_NAME[i]}; then
1012             # Execute remote command to add lnet options lines to 
1013             # modprobe.conf/modules.conf
1014             COMMAND=$"echo \"${MODULE_OPTS[i]}\"|${MODULE_CONFIG}"
1015             verbose_output "Adding lnet module options to" \
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                      "add module options to ${HOST_NAME[i]}!"
1022                 return 1
1023             fi
1024
1025             # Check lnet networks
1026             if ! check_lnet $i; then
1027                 return 1    
1028             fi
1029         fi
1030
1031         # Execute remote command to format Lustre target
1032         verbose_output "Formatting Lustre target ${DEVICE_NAME[i]} on ${HOST_NAME[i]}..."
1033         REMOTE_CMD[${pid_num}]="${REMOTE} ${HOST_NAME[i]} \"(${EXPORT_PATH} ${MKFS_CMD})\""
1034         verbose_output "Format command line is: ${REMOTE_CMD[${pid_num}]}"
1035         ${REMOTE} ${HOST_NAME[i]} "(${EXPORT_PATH} ${MKFS_CMD})" >&2 &  
1036         REMOTE_PID[${pid_num}]=$!
1037         pid_num=${pid_num}+1
1038         sleep 1
1039     done
1040
1041     # Wait for the exit status of the background remote command
1042     verbose_output "Waiting for the return of the remote command..."
1043     fail_exit_status=false
1044     for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
1045         wait ${REMOTE_PID[${pid_num}]}
1046         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1047             echo >&2 "`basename $0`: mass_config() error: Failed"\
1048             "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
1049             fail_exit_status=true
1050         fi
1051     done
1052
1053     if ${fail_exit_status}; then
1054         return 1
1055     fi    
1056
1057     verbose_output "All the Lustre targets are formatted successfully!"
1058     return 0
1059 }
1060
1061 # get_mntopts hostname device_name failovers
1062 # Construct the mount options of Lustre target @device_name in host @hostname
1063 get_mntopts() {
1064     local host_name=$1
1065     local device_name=$2
1066     local failovers=$3
1067     local mnt_opts=
1068     local ret_str
1069
1070     [ -n "${failovers}" ] && mnt_opts=defaults,noauto || mnt_opts=defaults
1071
1072     # Execute remote command to check whether the device
1073     # is a block device or not
1074     ret_str=$(${REMOTE} ${host_name} \
1075             "[ -b ${device_name} ] && echo block || echo loop" 2>&1)
1076     if [ ${PIPESTATUS[0]} -ne 0 -a -n "${ret_str}" ]; then
1077         echo "`basename $0`: get_mntopts() error:" \
1078         "remote command to ${host_name} error: ${ret_str}"
1079         return 1
1080     fi
1081
1082     if [ -z "${ret_str}" ]; then
1083         echo "`basename $0`: get_mntopts() error: remote error:" \
1084         "No results from remote!" \
1085         "Check network connectivity between the local host and ${host_name}!"
1086         return 1
1087     fi
1088
1089     [ "${ret_str}" != "${ret_str#*loop}" ] && mnt_opts=${mnt_opts},loop
1090
1091     echo ${mnt_opts}
1092     return 0
1093 }
1094
1095 # Execute remote command to modify /etc/fstab to add the new Lustre targets
1096 modify_fstab() {
1097     declare -i i
1098     local mntent mntopts device_name
1099     local COMMAND
1100
1101     if ! ${MODIFY_FSTAB}; then
1102         return 0    
1103     fi
1104
1105     for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
1106         verbose_output "Modify /etc/fstab of host ${HOST_NAME[i]}"\
1107                    "to add Lustre target ${DEVICE_NAME[i]}"
1108         mntent=${DEVICE_NAME[i]}"\t\t"${MOUNT_POINT[i]}"\t\t"${FS_TYPE}
1109
1110         # Get mount options
1111         if [ -n "${MOUNT_OPTIONS[i]}" ]; then
1112             # The mount options already specified in the csv file.
1113             mntopts=${MOUNT_OPTIONS[i]}
1114         else
1115             mntopts=$(get_mntopts ${HOST_NAME[i]} ${DEVICE_NAME[i]}\
1116                     ${FAILOVERS[i]})
1117             if [ ${PIPESTATUS[0]} -ne 0 ]; then
1118                 echo >&2 "${mntopts}"
1119                 return 1
1120             fi
1121         fi
1122
1123         mntent=${mntent}"\t"${mntopts}"\t"0" "0
1124         verbose_output "`echo -e ${mntent}`"
1125
1126         # Execute remote command to modify /etc/fstab
1127         device_name=${DEVICE_NAME[i]//\//\\/}
1128         COMMAND=". @scriptlibdir@/lc_common; \
1129                 sed -i \"/^${device_name}\t/d\" \$(fcanon /etc/fstab); \
1130                 echo -e \"${mntent}\" >> \$(fcanon /etc/fstab)"
1131         ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2
1132         if [ ${PIPESTATUS[0]} -ne 0 ]; then
1133             echo >&2 "`basename $0`: modify_fstab() error:"\
1134             "Failed to modify /etc/fstab of host ${HOST_NAME[i]}"\
1135             "to add Lustre target ${DEVICE_NAME[i]}!"
1136             return 1
1137         fi
1138     done
1139
1140     return 0
1141 }
1142
1143 # Main flow
1144 # Check the csv file
1145 if ! check_file $1; then
1146     exit 1 
1147 fi
1148
1149 # Get the list of nodes to be operated on
1150 NODES_TO_USE=$(get_nodelist)
1151 [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1
1152
1153 # Check the node list
1154 check_nodelist ${NODES_TO_USE} || exit 1
1155
1156 if ${VERIFY_CONNECT}; then
1157 # Check the network connectivity and hostnames
1158     echo "`basename $0`: Checking the cluster network connectivity"\
1159          "and hostnames..."
1160     if ! ${VERIFY_CLUSTER_NET} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1161         exit 1
1162     fi
1163     echo "`basename $0`: Check the cluster network connectivity"\
1164          "and hostnames OK!"
1165     echo
1166 fi
1167
1168 if ${CONFIG_MD_LVM}; then
1169 # Configure Linux MD/LVM devices
1170     echo "`basename $0`: Configuring Linux MD/LVM devices..."
1171     if ! ${SCRIPT_CONFIG_MD} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1172         exit 1
1173     fi
1174
1175     if ! ${SCRIPT_CONFIG_LVM} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1176         exit 1
1177     fi
1178     echo "`basename $0`: Configure Linux MD/LVM devices OK!"
1179     echo
1180 fi
1181
1182 # Configure the Lustre cluster
1183 echo "`basename $0`: ******** Lustre cluster configuration START ********"
1184 if ! get_items ${CSV_FILE}; then
1185     exit 1
1186 fi
1187
1188 if ! check_mgs; then
1189     exit 1
1190 fi
1191
1192 if ! mass_config; then
1193     exit 1
1194 fi
1195
1196 if ! modify_fstab; then
1197     exit 1
1198 fi
1199
1200 # Produce HA software's configuration files
1201 if ! config_ha; then
1202     rm -rf ${TMP_DIRS}
1203     exit 1
1204 fi
1205
1206 echo "`basename $0`: ******** Lustre cluster configuration END **********"
1207
1208 exit 0