3 # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
6 # lustre_config - format and set up multiple lustre servers from a csv file
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.
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.
16 ################################################################################
22 Usage: `basename $0` [options] <csv file>
24 This script is used to format and set up multiple lustre servers from a
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
42 -d configure Linux MD/LVM devices before formatting the
44 -f force-format the Lustre targets using --reformat option
45 -m no fstab change - don't modify /etc/fstab to add the new
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.
51 csv file a spreadsheet that contains configuration parameters
52 (separated by commas) for each target in a Lustre cluster
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.
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.
73 There are 5 kinds of line formats in the csv file. They represent the following
77 hostname,MD,md name,operation mode,options,raid level,component devices
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}"
89 2) Linux LVM PV (Physical Volume)
91 hostname,PV,pv names,operation mode,options
93 hostname hostname of the node in the cluster
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
103 3) Linux LVM VG (Volume Group)
105 hostname,VG,vg name,operation mode,options,pv paths
107 hostname hostname of the node in the cluster
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
113 pv paths physical volumes to construct this VG, required by
115 Multiple PVs are separated by space or by using
116 shell expansions, e.g. "/dev/sd[k-m]1"
118 4) Linux LVM LV (Logical Volume)
120 hostname,LV,lv name,operation mode,options,lv size,vg name
122 hostname hostname of the node in the cluster
124 lv name name of the logical volume to be created (optional)
125 or path of the logical volume to be removed (required
127 operation mode create or remove, default is create
128 options a "catchall" for other lvcreate/lvremove options
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
136 hostname,module_opts,device name,mount point,device type,fsname,mgs nids,index,
137 format options,mkfs options,mount options,failover nids
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
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
159 All the NIDs in one node are delimited by commas (','). When multiple nodes are
160 specified, they are delimited by a colon (':').
162 Items left blank will be set to defaults.
164 Example 1 - Simple, with combo MGS/MDT:
165 -------------------------------------------------------------------------------
167 lustre-mgs,options lnet networks=tcp,/tmp/mgs,/mnt/mgs,mgs|mdt,,,,--device-size=10240
170 lustre-ost,options lnet networks=tcp,/tmp/ost0,/mnt/ost0,ost,,lustre-mgs@tcp0,,--device-size=10240
173 lustre-ost,options lnet networks=tcp,/tmp/ost1,/mnt/ost1,ost,,lustre-mgs@tcp0,,--device-size=10240
174 -------------------------------------------------------------------------------
176 Example 2 - Separate MGS/MDT, two networks interfaces:
177 -------------------------------------------------------------------------------
179 lustre-mgs1,options lnet 'networks="tcp,elan"',/dev/sda,/mnt/mgs,mgs,,,,--quiet --param="sys.timeout=50",,"defaults,noauto","lustre-mgs2,2@elan"
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
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 -------------------------------------------------------------------------------
188 Example 3 - with combo MGS/MDT failover pair and OST failover pair:
189 -------------------------------------------------------------------------------
191 lustre-mgs1,options lnet networks=tcp,/tmp/mgs,/mnt/mgs,mgs|mdt,,,,--quiet --device-size=10240,,,lustre-mgs2@tcp0
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
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
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 -------------------------------------------------------------------------------
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
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
217 mgsnode,options lnet networks=tcp,/dev/md0,/mnt/mgs,mgs|mdt,,,,--quiet
220 ostnode,options lnet networks=tcp,/dev/ost_vg/ost0,/mnt/ost0,ost,,mgsnode,,--quiet
223 ostnode,options lnet networks=tcp,/dev/ost_vg/ost1,/mnt/ost1,ost,,mgsnode,,--quiet
224 -------------------------------------------------------------------------------
230 # Get the library of functions
231 . @scriptlibdir@/lc_common
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
239 declare -a NODE_NAMES # node names in the failover group
240 declare -a TARGET_OPTS # target services in one failover group
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
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
256 # Get and check the positional parameters
257 while getopts "aw:x:t:ndfmhv" OPTION; do
260 [ -z "${SPECIFIED_NODELIST}" ] && [ -z "${EXCLUDED_NODELIST}" ] \
262 NODELIST_OPT="${NODELIST_OPT} -a"
266 SPECIFIED_NODELIST=$OPTARG
267 NODELIST_OPT="${NODELIST_OPT} -w ${SPECIFIED_NODELIST}"
271 EXCLUDED_NODELIST=$OPTARG
272 NODELIST_OPT="${NODELIST_OPT} -x ${EXCLUDED_NODELIST}"
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" \
291 REFORMAT_OPTION=$"--reformat "
308 # Toss out the parameters we've already processed
309 shift `expr $OPTIND - 1`
311 # Here we expect the csv file
312 if [ $# -eq 0 ]; then
313 echo >&2 $"`basename $0`: Missing csv file!"
317 # Check the items required for OSTs, MDTs and MGS
319 # When formatting an OST, the following items: hostname, module_opts,
320 # device name, device type and mgs nids, cannot have null value.
322 # When formatting an MDT or MGS, the following items: hostname,
323 # module_opts, device name and device type, cannot have null value.
326 if [ $# -eq 0 ]; then
327 echo >&2 $"`basename $0`: check_item() error: Missing argument"\
328 "for function check_item()!"
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!"
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!"
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!"
360 # Get the number of MGS nodes in the cluster
363 MGS_NUM=${#MGS_NODENAME[@]}
364 [ -z "${MGS_NODENAME[0]}" ] && let "INIT_IDX += 1" \
365 && let "MGS_NUM += 1"
368 # is_mgs_node hostname
369 # Verify whether @hostname is a MGS node
375 for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
376 [ "${MGS_NODENAME[i]}" = "${host_name}" ] && return 0
382 # Check whether the MGS nodes are in the same failover group
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
396 if [ "${FAILOVERS_NAMES[idx]#*$mgs_node*}" = "${FAILOVERS_NAMES[idx]}" ]
398 echo >&2 $"`basename $0`: check_mgs_group() error:"\
399 "MGS node ${mgs_node} is not in the ${HOST_NAME[idx]}"\
409 # Get and check MGS servers.
410 # There should be no more than one MGS specified in the entire csv file.
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
419 # Initialize the MGS_NODENAME and MGS_IDX arrays
425 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
429 # Check whether this node is an explicit MGS node
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]}."
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]}."
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]}\"!"
453 # Get and check primary MGS server and backup MGS server
454 if [ "${FORMAT_OPTIONS[i]}" = "${FORMAT_OPTIONS[i]#*noformat*}" ]
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!"
465 MGS_NODENAME[0]=${HOST_NAME[i]}
468 mgs_node=${MGS_NODENAME[0]}
469 if [ "${FAILOVERS_NAMES[i]#*$mgs_node*}" = "${FAILOVERS_NAMES[i]}" ]
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]}!"
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!"
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!"
491 if ${is_exp_mgs}; then # Explicit MGS
492 MGS_NODENAME[exp_idx]=${HOST_NAME[i]}
494 exp_idx=$(( exp_idx + 1 ))
496 MGS_NODENAME[imp_idx]=${HOST_NAME[i]}
498 imp_idx=$(( imp_idx + 1 ))
501 fi #End of "if ${is_exp_mgs} || ${is_imp_mgs}"
504 # Check whether the MGS nodes are in the same failover group
505 if ! check_mgs_group; then
512 # Construct the command line of mkfs.lustre
513 construct_mkfs_cmdline() {
515 if [ $# -eq 0 ]; then
516 echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
517 "Missing argument for function construct_mkfs_cmdline()!"
522 local mgsnids mgsnids_str
523 local failnids failnids_str
526 MKFS_CMD=${MKFS_CMD}${REFORMAT_OPTION}
528 case "${DEVICE_TYPE[i]}" in
530 MKFS_CMD=${MKFS_CMD}$"--ost "
533 MKFS_CMD=${MKFS_CMD}$"--mdt "
536 MKFS_CMD=${MKFS_CMD}$"--mgs "
538 "mdt|mgs" | "mgs|mdt")
539 MKFS_CMD=${MKFS_CMD}$"--mdt --mgs "
542 echo >&2 $"`basename $0`: construct_mkfs_cmdline() error:"\
543 "Invalid device type - \"${DEVICE_TYPE[i]}\"!"
548 if [ -n "${FS_NAME[i]}" ]; then
549 MKFS_CMD=${MKFS_CMD}$"--fsname="${FS_NAME[i]}$" "
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}$" "
559 if [ -n "${INDEX[i]}" ]; then
560 MKFS_CMD=${MKFS_CMD}$"--index="${INDEX[i]}$" "
563 if [ -n "${FORMAT_OPTIONS[i]}" ]; then
564 MKFS_CMD=${MKFS_CMD}${FORMAT_OPTIONS[i]}$" "
567 if [ -n "${MKFS_OPTIONS[i]}" ]; then
568 MKFS_CMD=${MKFS_CMD}$"--mkfsoptions="$"\""${MKFS_OPTIONS[i]}$"\""$" "
571 if [ -n "${MOUNT_OPTIONS[i]}" ]; then
572 if ! ${MODIFY_FSTAB}; then
573 MKFS_CMD=${MKFS_CMD}$"--mountfsoptions="$"\""${MOUNT_OPTIONS[i]}$"\""$" "
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}$" "
584 MKFS_CMD=${MKFS_CMD}${DEVICE_NAME[i]}
588 # Get all the node names in this failover group
591 if [ $# -eq 0 ]; then
592 echo >&2 $"`basename $0`: get_nodenames() error: Missing"\
593 "argument for function get_nodenames()!"
601 # Initialize the NODE_NAMES array
604 NODE_NAMES[0]=${HOST_NAME[i]}
607 for nids in ${FAILOVERS_NAMES[i]//:/ }
609 NODE_NAMES[idx]=$(nids2hostname ${nids})
610 if [ ${PIPESTATUS[0]} -ne 0 ]; then
611 echo >&2 "${NODE_NAMES[idx]}"
621 # Verify whether the format line has HA items
625 [ -n "${FAILOVERS[i]}" ] && return 0
630 # Produce HA software's configuration files
638 HOSTNAME_OPT=${HOST_NAME[i]}
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!"
647 for ((idx = 1; idx < ${#NODE_NAMES[@]}; idx++)); do
648 HOSTNAME_OPT=${HOSTNAME_OPT}$":"${NODE_NAMES[idx]}
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]}
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}
663 "${HATYPE_CLUMGR}") # CluManager
664 cmd_line=${GEN_CLUMGR_CONFIG}$" -n ${HOSTNAME_OPT}"
665 cmd_line=${cmd_line}${DEVICE_OPT}${VERBOSE_OPT}
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
677 verbose_output "Generate HA software's configurations in"\
678 "${HOST_NAME[i]} failover group OK"
683 # Configure HA software
685 if [ -z "${HATYPE_OPT}" ]; then
690 declare -i prim_idx # Index for PRIM_HOSTNAMES array
691 declare -i target_idx # Index for TARGET_OPTS and HOST_INDEX arrays
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
699 # Initialize the PRIM_HOSTNAMES array
703 # Get failover groups and generate HA configuration files
704 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
705 prim_host=${HOST_NAME[i]}
707 for ((j = 0; j < ${#PRIM_HOSTNAMES[@]}; j++)); do
708 [ "${prim_host}" = "${PRIM_HOSTNAMES[j]}" ] && continue 2
714 for ((k = 0; k < ${#HOST_NAME[@]}; k++)); do
715 if [ "${prim_host}" = "${HOST_NAME[k]}" ] && is_ha_line "${k}"
717 HOST_INDEX[target_idx]=$k
718 TARGET_OPTS[target_idx]=${DEVICE_NAME[k]}:${MOUNT_POINT[k]}
719 target_idx=$(( target_idx + 1 ))
723 if [ ${#TARGET_OPTS[@]} -ne 0 ]; then
724 PRIM_HOSTNAMES[prim_idx]=${prim_host}
725 prim_idx=$(( prim_idx + 1 ))
727 if ! gen_ha_config ${HOST_INDEX[0]}; then
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!"
742 # Get all the items in the csv file and do some checks.
745 if [ $# -eq 0 ]; then
746 echo >&2 $"`basename $0`: get_items() error: Missing argument"\
747 "for function get_items()!"
755 declare -i line_num=0
758 while read -r LINE; do
759 line_num=${line_num}+1
760 # verbose_output "Parsing line ${line_num}: $LINE"
762 # Get rid of the empty line
763 if [ -z "`echo ${LINE}|awk '/[[:alnum:]]/ {print $0}'`" ]; then
767 # Get rid of the comment line
768 if [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ]
773 # Skip the Linux MD/LVM line
774 marker=$(echo ${LINE} | cut -d, -f 2)
775 if [ "${marker}" = "${MD_MARKER}" -o "${marker}" = "${PV_MARKER}" ] \
776 || [ "${marker}" = "${VG_MARKER}" -o "${marker}" = "${LV_MARKER}" ]; then
780 # Skip the host which is not specified in the host list
781 if ! ${USE_ALLNODES}; then
782 hostname=$(echo ${LINE} | cut -d, -f 1)
783 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
786 # Parse the config line into CONFIG_ITEM
787 if ! parse_line "$LINE"; then
788 echo >&2 $"`basename $0`: parse_line() error: Occurred"\
789 "on line ${line_num} in ${CSV_FILE}: $LINE"
793 HOST_NAME[idx]=${CONFIG_ITEM[0]}
794 MODULE_OPTS[idx]=${CONFIG_ITEM[1]}
795 DEVICE_NAME[idx]=${CONFIG_ITEM[2]}
796 MOUNT_POINT[idx]=${CONFIG_ITEM[3]}
797 DEVICE_TYPE[idx]=${CONFIG_ITEM[4]}
798 FS_NAME[idx]=${CONFIG_ITEM[5]}
799 MGS_NIDS[idx]=${CONFIG_ITEM[6]}
800 INDEX[idx]=${CONFIG_ITEM[7]}
801 FORMAT_OPTIONS[idx]=${CONFIG_ITEM[8]}
802 MKFS_OPTIONS[idx]=${CONFIG_ITEM[9]}
803 MOUNT_OPTIONS[idx]=${CONFIG_ITEM[10]}
804 FAILOVERS[idx]=${CONFIG_ITEM[11]}
806 MODULE_OPTS[idx]=`echo "${MODULE_OPTS[idx]}" | sed 's/"/\\\"/g'`
808 # Convert IP addresses in NIDs to hostnames
809 FAILOVERS_NAMES[idx]=$(ip2hostname_multi_node ${FAILOVERS[idx]})
810 if [ ${PIPESTATUS[0]} -ne 0 ]; then
811 echo >&2 "${FAILOVERS_NAMES[idx]}"
815 # Check some required items for formatting target
816 if ! check_item $idx; then
817 echo >&2 $"`basename $0`: check_item() error:"\
818 "Occurred on line ${line_num} in ${CSV_FILE}."
828 # check_lnet_connect hostname_index mgs_hostname
829 # Check whether the target node can contact the MGS node @mgs_hostname
830 # If @mgs_hostname is null, then it means the primary MGS node
831 check_lnet_connect() {
835 local COMMAND RET_STR
841 # Execute remote command to check that
842 # this node can contact the MGS node
843 verbose_output "Checking lnet connectivity between" \
844 "${HOST_NAME[i]} and the MGS node ${mgs_node}"
845 mgs_prim_nids=`echo ${MGS_NIDS[i]} | awk -F: '{print $1}'`
847 if [ -z "${mgs_node}" -o $MGS_NUM -eq 1 ]; then
848 nids_str=${mgs_prim_nids} # nids of primary MGS node
849 if [ -z "${nids_str}" ]; then
850 echo >&2 $"`basename $0`: check_lnet_connect() error:"\
851 "Check the mgs nids item of host ${HOST_NAME[i]}!"\
852 "Missing nids of the primary MGS node!"
856 # Get the corresponding NID(s) of the MGS node ${mgs_node}
857 # from the "mgs nids" field
858 nids_str=$(get_mgs_nids ${mgs_node} ${MGS_NIDS[i]})
859 if [ ${PIPESTATUS[0]} -ne 0 ]; then
860 echo >&2 "${nids_str}"
866 for mgs_nid in ${nids_str//,/ }
868 COMMAND=$"${LCTL} ping ${mgs_nid} 5 || echo failed 2>&1"
869 RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
870 if [ ${PIPESTATUS[0]} -eq 0 -a "${RET_STR}" = "${RET_STR#*failed*}" ]
872 # This node can contact the MGS node
873 verbose_output "${HOST_NAME[i]} can contact the MGS" \
874 "node ${mgs_node} by using nid \"${mgs_nid}\"!"
880 if ! ${ping_mgs}; then
881 echo >&2 "`basename $0`: check_lnet_connect() error:" \
882 "${HOST_NAME[i]} cannot contact the MGS node ${mgs_node}"\
883 "with nids - \"${nids_str}\"! Check ${LCTL} command!"
890 # Start lnet network in the cluster node and check that
891 # this node can contact the MGS node
893 if ! ${VERIFY_CONNECT}; then
898 if [ $# -eq 0 ]; then
899 echo >&2 $"`basename $0`: check_lnet() error: Missing"\
900 "argument for function check_lnet()!"
906 local COMMAND RET_STR
908 # Execute remote command to start lnet network
909 verbose_output "Starting lnet network in ${HOST_NAME[i]}"
910 COMMAND="PATH=\$PATH:/sbin:/usr/sbin modprobe lnet; ${LCTL} network up 2>&1"
911 RET_STR=$(${REMOTE} ${HOST_NAME[i]} "${COMMAND}" 2>&1)
912 if [ ${PIPESTATUS[0]} -ne 0 -o "${RET_STR}" = "${RET_STR#*LNET configured*}" ]
914 echo >&2 "`basename $0`: check_lnet() error: remote" \
915 "${HOST_NAME[i]} error: ${RET_STR}"
919 if is_mgs_node ${HOST_NAME[i]}; then
923 # Execute remote command to check that
924 # this node can contact the MGS node
925 for ((j = 0; j < ${MGS_NUM}; j++)); do
926 if ! check_lnet_connect $i ${MGS_NODENAME[j]}; then
934 # Start lnet network in the MGS node
940 if [ -z "${MGS_NODENAME[0]}" -a -z "${MGS_NODENAME[1]}" ]; then
941 if ${USE_ALLNODES}; then
942 verbose_output "There is no MGS target in the ${CSV_FILE} file."
944 verbose_output "There is no MGS target in the node list \"${NODES_TO_USE}\"."
949 for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
950 # Execute remote command to add lnet options lines to
951 # the MGS node's modprobe.conf/modules.conf
953 COMMAND=$"echo \"${MODULE_OPTS[${idx}]}\"|${MODULE_CONFIG}"
954 verbose_output "Adding lnet module options to ${MGS_NODENAME[i]}"
955 ${REMOTE} ${MGS_NODENAME[i]} "${COMMAND}" >&2
956 if [ ${PIPESTATUS[0]} -ne 0 ]; then
957 echo >&2 "`basename $0`: start_mgs_lnet() error:"\
958 "Failed to execute remote command to" \
959 "add module options to ${MGS_NODENAME[i]}!"\
960 "Check ${MODULE_CONFIG}!"
964 # Start lnet network in the MGS node
965 if ! check_lnet ${idx}; then
973 # Execute remote command to add lnet options lines to remote nodes'
974 # modprobe.conf/modules.conf and format(mkfs.lustre) Lustre targets
977 declare -a REMOTE_PID
978 declare -a REMOTE_CMD
982 if [ ${#HOST_NAME[@]} -eq 0 ]; then
983 verbose_output "There are no Lustre targets to be formatted."
987 # Start lnet network in the MGS node
988 if ! start_mgs_lnet; then
992 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
993 # Construct the command line of mkfs.lustre
994 if ! construct_mkfs_cmdline $i; then
998 # create the mount point on the node
999 COMMAND="mkdir -p ${MOUNT_POINT[i]}"
1000 verbose_output "Creating the mount point ${MOUNT_POINT[i]} on" \
1002 ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2
1003 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1004 echo >&2 "`basename $0`: mass_config() error:"\
1005 "Failed to execute remote command to"\
1006 "create the mountpoint on ${HOST_NAME[i]}!"
1010 if ! is_mgs_node ${HOST_NAME[i]}; then
1011 # Execute remote command to add lnet options lines to
1012 # modprobe.conf/modules.conf
1013 COMMAND=$"echo \"${MODULE_OPTS[i]}\"|${MODULE_CONFIG}"
1014 verbose_output "Adding lnet module options to" \
1016 ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2
1017 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1018 echo >&2 "`basename $0`: mass_config() error:"\
1019 "Failed to execute remote command to"\
1020 "add module options to ${HOST_NAME[i]}!"
1024 # Check lnet networks
1025 if ! check_lnet $i; then
1030 # Execute remote command to format Lustre target
1031 verbose_output "Formatting Lustre target ${DEVICE_NAME[i]} on ${HOST_NAME[i]}..."
1032 REMOTE_CMD[${pid_num}]="${REMOTE} ${HOST_NAME[i]} \"(${EXPORT_PATH} ${MKFS_CMD})\""
1033 verbose_output "Format command line is: ${REMOTE_CMD[${pid_num}]}"
1034 ${REMOTE} ${HOST_NAME[i]} "(${EXPORT_PATH} ${MKFS_CMD})" >&2 &
1035 REMOTE_PID[${pid_num}]=$!
1036 pid_num=${pid_num}+1
1040 # Wait for the exit status of the background remote command
1041 verbose_output "Waiting for the return of the remote command..."
1042 fail_exit_status=false
1043 for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
1044 wait ${REMOTE_PID[${pid_num}]}
1045 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1046 echo >&2 "`basename $0`: mass_config() error: Failed"\
1047 "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
1048 fail_exit_status=true
1052 if ${fail_exit_status}; then
1056 verbose_output "All the Lustre targets are formatted successfully!"
1060 # get_mntopts hostname device_name failovers
1061 # Construct the mount options of Lustre target @device_name in host @hostname
1064 local device_name=$2
1069 [ -n "${failovers}" ] && mnt_opts=defaults,noauto || mnt_opts=defaults
1071 # Execute remote command to check whether the device
1072 # is a block device or not
1073 ret_str=$(${REMOTE} ${host_name} \
1074 "[ -b ${device_name} ] && echo block || echo loop" 2>&1)
1075 if [ ${PIPESTATUS[0]} -ne 0 -a -n "${ret_str}" ]; then
1076 echo "`basename $0`: get_mntopts() error:" \
1077 "remote command to ${host_name} error: ${ret_str}"
1081 if [ -z "${ret_str}" ]; then
1082 echo "`basename $0`: get_mntopts() error: remote error:" \
1083 "No results from remote!" \
1084 "Check network connectivity between the local host and ${host_name}!"
1088 [ "${ret_str}" != "${ret_str#*loop}" ] && mnt_opts=${mnt_opts},loop
1094 # Execute remote command to modify /etc/fstab to add the new Lustre targets
1097 local mntent mntopts device_name
1100 if ! ${MODIFY_FSTAB}; then
1104 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
1105 verbose_output "Modify /etc/fstab of host ${HOST_NAME[i]}"\
1106 "to add Lustre target ${DEVICE_NAME[i]}"
1107 mntent=${DEVICE_NAME[i]}"\t\t"${MOUNT_POINT[i]}"\t\t"${FS_TYPE}
1110 if [ -n "${MOUNT_OPTIONS[i]}" ]; then
1111 # The mount options already specified in the csv file.
1112 mntopts=${MOUNT_OPTIONS[i]}
1114 mntopts=$(get_mntopts ${HOST_NAME[i]} ${DEVICE_NAME[i]}\
1116 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1117 echo >&2 "${mntopts}"
1122 mntent=${mntent}"\t"${mntopts}"\t"0" "0
1123 verbose_output "`echo -e ${mntent}`"
1125 # Execute remote command to modify /etc/fstab
1126 device_name=${DEVICE_NAME[i]//\//\\/}
1127 COMMAND=". @scriptlibdir@/lc_common; \
1128 sed -i \"/^${device_name}\t/d\" \$(fcanon /etc/fstab); \
1129 echo -e \"${mntent}\" >> \$(fcanon /etc/fstab)"
1130 ${REMOTE} ${HOST_NAME[i]} "${COMMAND}" >&2
1131 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1132 echo >&2 "`basename $0`: modify_fstab() error:"\
1133 "Failed to modify /etc/fstab of host ${HOST_NAME[i]}"\
1134 "to add Lustre target ${DEVICE_NAME[i]}!"
1143 # Check the csv file
1144 if ! check_file $1; then
1148 # Get the list of nodes to be operated on
1149 NODES_TO_USE=$(get_nodelist)
1150 [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1
1152 # Check the node list
1153 check_nodelist ${NODES_TO_USE} || exit 1
1155 if ${VERIFY_CONNECT}; then
1156 # Check the network connectivity and hostnames
1157 echo "`basename $0`: Checking the cluster network connectivity"\
1159 if ! ${VERIFY_CLUSTER_NET} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1162 echo "`basename $0`: Check the cluster network connectivity"\
1167 if ${CONFIG_MD_LVM}; then
1168 # Configure Linux MD/LVM devices
1169 echo "`basename $0`: Configuring Linux MD/LVM devices..."
1170 if ! ${SCRIPT_CONFIG_MD} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1174 if ! ${SCRIPT_CONFIG_LVM} ${NODELIST_OPT} ${VERBOSE_OPT} ${CSV_FILE}; then
1177 echo "`basename $0`: Configure Linux MD/LVM devices OK!"
1181 # Configure the Lustre cluster
1182 echo "`basename $0`: ******** Lustre cluster configuration START ********"
1183 if ! get_items ${CSV_FILE}; then
1187 if ! check_mgs; then
1191 if ! mass_config; then
1195 if ! modify_fstab; then
1199 # Produce HA software's configuration files
1200 if ! config_ha; then
1205 echo "`basename $0`: ******** Lustre cluster configuration END **********"