3 # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
6 # lc_common - This file contains common variables and functions to be used by
7 # Lustre cluster config scripts.
9 ################################################################################
11 #****************************** Common Variables ******************************#
12 export PATH=$PATH:/sbin:/usr/sbin
15 export REMOTE=${REMOTE:-"ssh -x -q"}
16 #export REMOTE=${REMOTE:-"pdsh -S -R ssh -w"}
19 export MKFS=${MKFS:-"mkfs.lustre"}
20 export TUNEFS=${TUNEFS:-"tunefs.lustre"}
21 export LCTL=${LCTL:-"lctl"}
23 # Software RAID command
24 export MDADM=${MDADM:-"mdadm"}
26 # Some scripts to be called
27 export MODULE_CONFIG=${MODULE_CONFIG:-"lc_modprobe"}
28 export VERIFY_CLUSTER_NET=${VERIFY_CLUSTER_NET:-"lc_net"}
29 export GEN_HB_CONFIG=${GEN_HB_CONFIG:-"lc_hb"}
30 export GEN_CLUMGR_CONFIG=${GEN_CLUMGR_CONFIG:-"lc_cluman"}
31 export SCRIPT_VERIFY_SRVIP=${SCRIPT_VERIFY_SRVIP:-"lc_servip"}
32 export SCRIPT_GEN_MONCF=${SCRIPT_GEN_MONCF:-"lc_mon"}
33 export SCRIPT_CONFIG_MD=${SCRIPT_CONFIG_MD:-"lc_md"}
34 export SCRIPT_CONFIG_LVM=${SCRIPT_CONFIG_LVM:-"lc_lvm"}
36 # Variables of HA software
37 HBVER_HBV1="hbv1" # Heartbeat version 1
38 HBVER_HBV2="hbv2" # Heartbeat version 2
39 HATYPE_CLUMGR="cluman" # Cluster Manager
41 # Configuration directories and files
42 HA_DIR=${HA_DIR:-"/etc/ha.d"} # Heartbeat configuration directory
43 MON_DIR=${MON_DIR:-"/etc/mon"} # mon configuration directory
44 CIB_DIR=${CIB_DIR:-"/var/lib/heartbeat/crm"} # cib.xml directory
46 HA_CF=${HA_DIR}/ha.cf # ha.cf file
47 HA_RES=${HA_DIR}/haresources # haresources file
48 HA_CIB=${CIB_DIR}/cib.xml
50 CLUMAN_DIR="/etc" # CluManager configuration directory
51 CLUMAN_CONFIG=${CLUMAN_DIR}/cluster.xml
53 CLUMAN_TOOLS_PATH=${CLUMAN_TOOLS_PATH:-"/usr/sbin"} # CluManager tools
54 CONFIG_CMD=${CONFIG_CMD:-"${CLUMAN_TOOLS_PATH}/redhat-config-cluster-cmd"}
56 HB_TMP_DIR="/tmp/heartbeat" # Temporary directory
57 CLUMGR_TMP_DIR="/tmp/clumanager"
58 TMP_DIRS="${HB_TMP_DIR} ${CLUMGR_TMP_DIR}"
60 FS_TYPE=${FS_TYPE:-"lustre"} # Lustre filesystem type
61 FILE_SUFFIX=${FILE_SUFFIX:-".lustre"} # Suffix of the generated config files
63 # Marker of the MD device line
64 export MD_MARKER=${MD_MARKER:-"MD"}
66 # Marker of the LVM device line
67 export PV_MARKER=${PV_MARKER:-"PV"}
68 export VG_MARKER=${VG_MARKER:-"VG"}
69 export LV_MARKER=${LV_MARKER:-"LV"}
71 declare -a CONFIG_ITEM # Items in each line of the CSV file
72 declare -a NODE_NAME # Hostnames of nodes have been configured
74 declare -a MGS_NODENAME # Node names of the MGS servers
75 declare -a MGS_IDX # Indexes of MGSs in the global arrays
76 declare -i MGS_NUM # Number of MGS servers in the cluster
79 # All of the Lustre target items in the CSV file
80 declare -a HOST_NAME MODULE_OPTS DEVICE_NAME MOUNT_POINT DEVICE_TYPE FS_NAME
81 declare -a MGS_NIDS INDEX FORMAT_OPTIONS MKFS_OPTIONS MOUNT_OPTIONS FAILOVERS
83 # Heartbeat software requires that node names in the configuration directive
84 # must (normally) match the "uname -n" of that machine. Since the value of the
85 # "failover nids" field in the CSV file is the NID(s) of failover partner node,
86 # we have to figure out the corresponding hostname of that node.
87 declare -a FAILOVERS_NAMES
89 export VERIFY_CONNECT=true # Verify network connectivity by default
90 export USE_ALLNODES=false # Not operating on all the nodes by default
91 export SPECIFIED_NODELIST="" # Specified list of nodes to be operated on
92 export EXCLUDED_NODELIST="" # Specified list of nodes to be excluded
93 export NODES_TO_USE="" # Defacto list of nodes to be operated on
94 export NODELIST_OPT=""
95 export VERBOSE_OUTPUT=false
99 #****************************** Common Functions ******************************#
101 # verbose_output string
102 # Output verbose information $string
104 if ${VERBOSE_OUTPUT}; then
105 echo "`basename $0`: $*"
110 # error_output string
111 # Output error string to stderr, prefixing with ERROR
112 # for easy error parsing from the rest of the output.
114 echo >&2 "$(basename $0): ERROR: $*"
118 # error_exit rc string
119 # Output error to stderr via error_output and exit with rc.
128 # Check whether the reomte command is pdsh
130 if [ "${REMOTE}" = "${REMOTE#*pdsh}" ]; then
137 # check_file csv_file
138 # Check the file $csv_file
141 if [ $# -eq 0 ]; then
142 error_output "check_file(): Missing CSV file!"
147 if [ ! -s ${CSV_FILE} ]; then
148 error_output "check_file(): ${CSV_FILE}"\
149 "does not exist or is empty!"
157 # Parse a line in the CSV file
160 if [ $# -eq 0 ]; then
161 error_output "parse_line(): Missing argument!"
165 declare -i i=0 # Index of the CONFIG_ITEM array
168 declare -i s_quote_flag=0 # Flag of the single quote character
169 declare -i d_quote_flag=0 # Flag of the double quotes character
170 local TMP_LETTER LINE
174 # Initialize the CONFIG_ITEM array
177 # Get the length of the line
181 while [ ${idx} -lt ${length} ]; do
182 # Get a letter from the line
183 TMP_LETTER=${LINE:${idx}:1}
185 case "${TMP_LETTER}" in
187 if [ ${s_quote_flag} -eq 1 -o ${d_quote_flag} -eq 1 ]
189 CONFIG_ITEM[i]=${CONFIG_ITEM[i]}${TMP_LETTER}
197 if [ ${s_quote_flag} -eq 0 ]; then
204 if [ ${d_quote_flag} -eq 0 ]; then
217 CONFIG_ITEM[i]=${CONFIG_ITEM[i]}${TMP_LETTER}
221 # Extract the real value of each field
222 # Remove surrounded double-quotes, etc.
223 for ((idx = 0; idx <= $i; idx++)); do
224 # Strip the leading and trailing space-characters
225 CONFIG_ITEM[idx]=`expr "${CONFIG_ITEM[idx]}" : '[[:space:]]*\(.*\)[[:space:]]*$'`
227 [ -z "${CONFIG_ITEM[idx]}" ] && continue
229 # Remove the surrounded double-quotes
230 while [ -z "`echo "${CONFIG_ITEM[idx]}"|sed -e 's/^".*"$//'`" ]; do
231 CONFIG_ITEM[idx]=`echo "${CONFIG_ITEM[idx]}" | sed -e 's/^"//' -e 's/"$//'`
234 CONFIG_ITEM[idx]=`echo "${CONFIG_ITEM[idx]}" | sed -e 's/""/"/g'`
241 # If $name is a symbolic link, then display it's value
245 if [ -h "$NAME" ]; then
252 # configured_host host_name
254 # Check whether the devices in $host_name has been configured or not
259 for ((i = 0; i < ${#NODE_NAME[@]}; i++)); do
260 [ "${host_name}" = "${NODE_NAME[i]}" ] && return 0
266 # remote_error fn_name host_addr ret_str
267 # Verify the return result from remote command
269 local fn_name host_addr ret_str
277 if [ "${ret_str}" != "${ret_str#*connect:*}" ]; then
278 error_output "${fn_name}(): ${ret_str}"
282 if [ -z "${ret_str}" ]; then
283 error_output "${fn_name}():" \
284 "No results from remote!" \
285 "Check network connectivity between the local host and ${host_addr}!"
293 # Convert $nid to hostname of the lustre cluster node
297 local addr nettype ip_addr
301 [ "${nid}" != "${nid#*@*}" ] && nettype=${nid#*@} || nettype=tcp
302 if [ -z "${addr}" ]; then
303 echo "`basename $0`: nid2hostname() error: Invalid nid - \"${nid}\"!"
308 lo*) host_name=`hostname`;;
310 # FIXME: Parse the /etc/elanhosts configuration file to
311 # convert ElanID to hostname
314 # FIXME: Use /usr/sbin/gmlndnid to find the hostname of
315 # the specified GM Global node ID
318 # FIXME: Convert portal ID to hostname
320 *) # tcp, o2ib, cib, openib, iib, vib, ra
322 # Is it IP address or hostname?
323 if [ -n "`echo ${ip_addr} | sed -e 's/\([0-9]\{1,3\}\.\)\{3,3\}[0-9]\{1,3\}//'`" ]
330 # Execute remote command to get the host name
331 ret_str=$(${REMOTE} ${ip_addr} "hostname" 2>&1 </dev/null)
332 if [ ${PIPESTATUS[0]} -ne 0 -a -n "${ret_str}" ]; then
333 echo "`basename $0`: nid2hostname() error:" \
334 "remote command to ${ip_addr} error: ${ret_str}"
337 remote_error "nid2hostname" ${ip_addr} "${ret_str}" && return 1
340 host_name=`echo ${ret_str} | awk '{print $2}'`
342 host_name=`echo ${ret_str} | awk '{print $1}'`
352 # Get the hostname of the lustre cluster node which has the nids - $nids
359 for nid in ${nids//,/ }; do
360 [ "${nid}" != "${nid#*@*}" ] && nettype=${nid#*@} || nettype=tcp
363 lo* | elan* | gm* | ptl*) ;;
364 *) # tcp, o2ib, cib, openib, iib, vib, ra
365 host_name=$(nid2hostname ${nid})
366 if [ ${PIPESTATUS[0]} -ne 0 ]; then
374 if [ -z "${host_name}" ]; then
375 echo "`basename $0`: nids2hostname() error:" \
376 "Can not get the hostname from nids - \"${nids}\"!"
384 # ip2hostname_single_node nids
385 # Convert IP addresses in $nids into hostnames
386 # NID in $nids are delimited by commas, ie all the $nids belong to one node
387 ip2hostname_single_node() {
393 for nid in ${orig_nids//,/ }; do
394 [ "${nid}" != "${nid#*@*}" ] && nettype=${nid#*@} || nettype=tcp
397 lo* | elan* | gm* | ptl*) ;;
398 *) # tcp, o2ib, cib, openib, iib, vib, ra
399 host_name=$(nid2hostname ${nid})
400 if [ ${PIPESTATUS[0]} -ne 0 ]; then
405 nid=${host_name}@${nettype}
409 [ -z "${nids}" ] && nids=${nid} || nids=${nids},${nid}
416 # ip2hostname_multi_node nids
417 # Convert IP addresses in $nids into hostnames
418 # NIDs belong to multiple nodes are delimited by colons in $nids
419 ip2hostname_multi_node() {
424 for nid in ${orig_nids//:/ }; do
425 nid=$(ip2hostname_single_node ${nid})
426 if [ ${PIPESTATUS[0]} -ne 0 ]; then
431 [ -z "${nids}" ] && nids=${nid} || nids=${nids}:${nid}
438 # comma_list space-delimited-list
439 # Convert a space-delimited list to a sorted list of unique values
440 # separated by commas.
442 # the sed converts spaces to commas, but leaves the last space
443 # alone, so the line doesn't end with a comma.
444 echo "$*" | tr -s " " "\n" | sort -b -u | tr "\n" " " | sed 's/ \([^$]\)/,\1/g'
447 # host_in_hostlist hostname hostlist
448 # Given a hostname, and a list of hostnames, return true if the hostname
449 # appears in the list of hostnames, or false otherwise.
454 [ -z "$HOST" -o -z "$HOSTLIST" ] && false && return
456 # Hostnames in the list are separated by commas.
457 [[ ,$HOSTLIST, == *,$HOST,* ]] && true && return
462 # exclude_items_from_list list_of_items list_of_items_to_exclude
463 # Given a list of items, and a second list of items to exclude from
464 # the first list, return the contents of the first list minus the contents
466 exclude_items_from_list() {
471 # Handle an empty inlist by throwing back an empty string.
472 if [ -z "$INLIST" ]; then
477 # Handle an empty excludelist by throwing back the inlist unmodified.
478 if [ -z "$EXCLUDELIST" ]; then
483 for ITEM in ${INLIST//,/ }; do
484 if ! host_in_hostlist $ITEM $EXCLUDELIST; then
485 OUTLIST="$OUTLIST,$ITEM"
489 # strip leading comma
493 # get_csv_nodelist csv_file
494 # Get the comma-separated list of all the nodes from the CSV file
500 ! check_file ${csv_file} 2>&1 && return 1
502 all_nodelist=$(egrep -v "([[:space:]]|^)#" ${csv_file} | cut -d, -f 1)
503 all_nodelist=$(comma_list ${all_nodelist})
510 # Get the comma-separated list of nodes to be operated on
511 # Note: CSV_FILE, USE_ALLNODES, SPECIFIED_NODELIST and EXCLUDED_NODELIST
512 # are global variables
516 # Get the list of all the nodes in the CSV file
517 ALL_NODELIST=$(get_csv_nodelist ${CSV_FILE})
518 [ ${PIPESTATUS[0]} -ne 0 ] && echo "${ALL_NODELIST}" && return 1
520 if [ -z "${ALL_NODELIST}" ]; then
521 echo "`basename $0`: get_nodelist() error:"\
522 "There are no hosts in the ${CSV_FILE} file!"
526 if ${USE_ALLNODES}; then
527 echo ${ALL_NODELIST} && return 0
530 if [ -n "${SPECIFIED_NODELIST}" ]; then
531 echo $(exclude_items_from_list ${SPECIFIED_NODELIST} ${EXCLUDED_NODELIST})
535 if [ -n "${EXCLUDED_NODELIST}" ]; then
536 echo $(exclude_items_from_list ${ALL_NODELIST} ${EXCLUDED_NODELIST})
540 # No hosts to be operated on
545 # check_nodelist nodelist
546 # Given a list of nodes to be operated on, check whether the nodelist is
547 # empty or not and output prompt message.
549 local nodes_to_use=$1
551 if [ -z "${nodes_to_use}" ]; then
552 error_output "There are no nodes to be operated on."\
553 "Check the node selection options (-a, -w or -x)."
557 verbose_output "Operating on the following nodes: ${nodes_to_use}"
563 # nid_in_nidlist nid nidlist
564 # Given a nid, and a list of nids in one node (delimited by comma ','),
565 # return true if the nid appears in the list of nids, or false otherwise.
571 [ -z "${nid}" -o -z "${nidlist}" ] && false && return
573 if [[ "${nid}" != *@* || "${nid#*@}" == tcp* ]]; then
574 # network type is tcp
575 for my_nid in ${nidlist//,/ }; do
576 [ "${nid%@*}" = "${my_nid%@*}" ] && true && return
579 # network type is not tcp
580 [[ ,${nidlist}, == *,${nid},* ]] && true && return
586 # get_mgs_nids mgs_hostname mgs_nids
587 # Get the corresponding NID(s) of the MGS node ${mgs_hostname} from the
588 # "mgs nids" field of one lustre target in the CSV file
591 local all_mgs_nids="$2"
595 # Check whether the hostname of the mgs node is in
596 # the mgs nids string
597 for mgs_nids in ${all_mgs_nids//:/ }; do
598 if nid_in_nidlist ${mgs_node} ${mgs_nids}; then
604 # Let's use lctl to get the real nids from the mgs node
605 ret_str=$($REMOTE $mgs_node "PATH=\$PATH:/sbin:/usr/sbin
606 $LCTL list_nids" 2>&1 </dev/null)
607 if [ ${PIPESTATUS[0]} -ne 0 -a -n "${ret_str}" ]; then
608 echo "$(basename $0): get_mgs_nids() error:" \
609 "remote command to ${mgs_node} error: ${ret_str}"
612 remote_error "get_mgs_nids" ${mgs_node} "${ret_str}" && return 1
614 local real_mgs_nids=${ret_str//${mgs_node}:/}
615 for real_mgs_nid in ${real_mgs_nids}; do
616 for mgs_nids in ${all_mgs_nids//:/ }; do
617 if nid_in_nidlist ${real_mgs_nid} ${mgs_nids}; then
624 echo "$(basename $0): get_mgs_nids() error:" \
625 "Can not figure out which nids corresponding to the MGS"\
626 "node ${mgs_node} from \"${all_mgs_nids}\"!"
631 # Check the items required for OSTs, MDTs and MGS
633 # When formatting an OST, the following items: hostname,
634 # device name, device type and mgs nids, cannot have null value.
636 # When formatting an MDT or MGS, the following items: hostname,
637 # device name and device type, cannot have null value.
638 check_lustre_item() {
640 if [ $# -eq 0 ]; then
641 error_output "check_lustre_item(): Missing argument"\
642 "for function check_lustre_item()!"
648 # Check hostname, device name and device type
649 if [ -z "${HOST_NAME[i]}" ] || \
650 [ -z "${DEVICE_NAME[i]}" ] || [ -z "${DEVICE_TYPE[i]}" ]; then
651 error_output "check_lustre_item(): Some required"\
652 "item has null value! Check hostname,"\
653 "device name and device type!"
658 if [ "${DEVICE_TYPE[i]}" = "ost" ]&&[ -z "${MGS_NIDS[i]}" ]; then
659 error_output "check_lustre_item(): OST's mgs nids"\
660 "item has null value!"
665 if [ -z "${MOUNT_POINT[i]}" ]; then
666 error_output "check_lustre_item(): mount"\
667 "point item of target ${DEVICE_NAME[i]} has null value!"
674 # Get the number of MGS nodes in the cluster
677 MGS_NUM=${#MGS_NODENAME[@]}
678 [ -z "${MGS_NODENAME[0]}" ] && let "INIT_IDX += 1" \
679 && let "MGS_NUM += 1"
682 # is_mgs_node hostname
683 # Verify whether @hostname is a MGS node
689 for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
690 [ "${MGS_NODENAME[i]}" = "${host_name}" ] && return 0
696 # Check whether the MGS nodes are in the same failover group
704 for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
705 mgs_node=${MGS_NODENAME[i]}
706 for ((j = ${INIT_IDX}; j < ${MGS_NUM}; j++)); do
707 [ "${MGS_NODENAME[j]}" = "${mgs_node}" ] && continue 1
710 if [ "${FAILOVERS_NAMES[idx]#*$mgs_node*}" = "${FAILOVERS_NAMES[idx]}" ]
712 error_output "check_mgs_group():"\
713 "MGS node ${mgs_node} is not in the ${HOST_NAME[idx]}"\
723 # Get and check MGS servers.
724 # There should be no more than one MGS specified in the entire CSV file.
728 declare -i exp_idx # Index of explicit MGS servers
729 declare -i imp_idx # Index of implicit MGS servers
730 local is_exp_mgs is_imp_mgs
733 # Initialize the MGS_NODENAME and MGS_IDX arrays
739 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
743 # Check whether this node is an explicit MGS node
745 if [ "${DEVICE_TYPE[i]#*mgs*}" != "${DEVICE_TYPE[i]}" ]; then
746 verbose_output "Explicit MGS target" \
747 "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
751 if [ "${DEVICE_TYPE[i]}" = "mdt" -a -z "${MGS_NIDS[i]}" ]; then
752 verbose_output "Implicit MGS target" \
753 "${DEVICE_NAME[i]} in host ${HOST_NAME[i]}."
757 # Get and check MGS servers
758 if ${is_exp_mgs} || ${is_imp_mgs}; then
759 # Check whether more than one MGS target in one MGS node
760 if is_mgs_node ${HOST_NAME[i]}; then
761 error_output "check_mgs():"\
762 "More than one MGS target in the same node -"\
763 "\"${HOST_NAME[i]}\"!"
767 # Get and check primary MGS server and backup MGS server
768 if [ "${FORMAT_OPTIONS[i]}" = "${FORMAT_OPTIONS[i]#*noformat*}" ]
771 if [ -z "${MGS_NODENAME[0]}" ]; then
772 if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
773 || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
774 error_output "check_mgs():"\
775 "There exist both explicit and implicit MGS"\
776 "targets in the CSV file!"
779 MGS_NODENAME[0]=${HOST_NAME[i]}
782 mgs_node=${MGS_NODENAME[0]}
783 if [ "${FAILOVERS_NAMES[i]#*$mgs_node*}" = "${FAILOVERS_NAMES[i]}" ]
785 error_output "check_mgs():"\
786 "More than one primary MGS nodes in the CSV" \
787 "file - ${MGS_NODENAME[0]} and ${HOST_NAME[i]}!"
789 error_output "check_mgs():"\
790 "MGS nodes ${MGS_NODENAME[0]} and ${HOST_NAME[i]}"\
791 "are failover pair, one of them should use"\
792 "\"--noformat\" in the format options item!"
796 else # Backup MGS server
797 if [ "${is_exp_mgs}" = "true" -a ${imp_idx} -gt 1 ] \
798 || [ "${is_imp_mgs}" = "true" -a ${exp_idx} -gt 1 ]; then
799 error_output "check_mgs():"\
800 "There exist both explicit and implicit MGS"\
801 "targets in the CSV file!"
805 if ${is_exp_mgs}; then # Explicit MGS
806 MGS_NODENAME[exp_idx]=${HOST_NAME[i]}
808 exp_idx=$(( exp_idx + 1 ))
810 MGS_NODENAME[imp_idx]=${HOST_NAME[i]}
812 imp_idx=$(( imp_idx + 1 ))
815 fi #End of "if ${is_exp_mgs} || ${is_imp_mgs}"
818 # Check whether the MGS nodes are in the same failover group
819 if ! check_mgs_group; then
826 # Execute remote command to add module options to
827 # the module configuration file
828 add_module_options() {
832 if [ -z "$hostname" ]; then
833 error_output "add_module_options(): Missing hostname!"
837 [ -z "${MODULE_OPTS[i]}" ] && return 0
839 # Execute remote command to add module options to
840 # the module configuration file
841 verbose_output "Adding module options to $hostname"
842 $REMOTE $hostname "PATH=\$PATH:/sbin:/usr/sbin
843 echo \"${MODULE_OPTS[i]}\" | $MODULE_CONFIG"
844 local RC=${PIPESTATUS[0]}
845 if [ $RC -ne 0 ]; then
846 error_output "add_module_options():"\
847 "Failed to add module options to $hostname!"
854 # check_lnet_connect hostname_index mgs_hostname
855 # Check whether the target node can contact the MGS node @mgs_hostname
856 # If @mgs_hostname is null, then it means the primary MGS node
857 check_lnet_connect() {
867 # Execute remote command to check that
868 # this node can contact the MGS node
869 verbose_output "Checking lnet connectivity between" \
870 "${HOST_NAME[i]} and the MGS node ${mgs_node}"
871 mgs_prim_nids=`echo ${MGS_NIDS[i]} | awk -F: '{print $1}'`
873 if [ -z "${mgs_node}" -o $MGS_NUM -eq 1 ]; then
874 nids_str=${mgs_prim_nids} # nids of primary MGS node
875 if [ -z "${nids_str}" ]; then
876 error_output "check_lnet_connect():"\
877 "Check the mgs nids item of host ${HOST_NAME[i]}!"\
878 "Missing nids of the primary MGS node!"
882 # Get the corresponding NID(s) of the MGS node ${mgs_node}
883 # from the "mgs nids" field
884 nids_str=$(get_mgs_nids ${mgs_node} ${MGS_NIDS[i]})
885 if [ ${PIPESTATUS[0]} -ne 0 ]; then
886 error_output "${nids_str}"
892 for mgs_nid in ${nids_str//,/ }
894 for try in $(seq 0 5); do
895 $REMOTE ${HOST_NAME[i]} "PATH=\$PATH:/sbin:/usr/sbin
896 $LCTL ping $mgs_nid 5 1>/dev/null"
897 if [ ${PIPESTATUS[0]} -eq 0 ]; then
898 # This node can contact the MGS node
899 verbose_output "${HOST_NAME[i]} can contact the MGS" \
900 "node $mgs_node by using nid \"$mgs_nid\"!"
907 if ! ${ping_mgs}; then
908 error_output "check_lnet_connect():" \
909 "${HOST_NAME[i]} cannot contact the MGS node ${mgs_node}"\
910 "with nids - \"${nids_str}\"! Check ${LCTL} command!"
917 # Start lnet network in the cluster node and check that
918 # this node can contact the MGS node
920 if ! $VERIFY_CONNECT; then
925 if [ $# -eq 0 ]; then
926 error_output "check_lnet(): Missing argument!"
934 # Execute remote command to start lnet network
935 verbose_output "Starting lnet network on ${HOST_NAME[i]}"
936 ret_str=$($REMOTE ${HOST_NAME[i]} "PATH=\$PATH:/sbin:/usr/sbin
937 modprobe lnet && $LCTL network up" 2>&1)
938 if [ ${PIPESTATUS[0]} -ne 0 ]; then
939 error_output "check_lnet(): start lnet network on" \
940 "${HOST_NAME[i]} error: $ret_str"
944 if is_mgs_node ${HOST_NAME[i]}; then
948 # Execute remote command to check that
949 # this node can contact the MGS node
950 for ((j = 0; j < ${MGS_NUM}; j++)); do
951 if ! check_lnet_connect $i ${MGS_NODENAME[j]}; then
959 # Start lnet network in the MGS node
964 if [ -z "${MGS_NODENAME[0]}" -a -z "${MGS_NODENAME[1]}" ]; then
965 if ${USE_ALLNODES}; then
966 verbose_output "There is no MGS target in the ${CSV_FILE} file."
968 verbose_output "There is no MGS target in the node list \"${NODES_TO_USE}\"."
973 for ((i = ${INIT_IDX}; i < ${MGS_NUM}; i++)); do
974 # Execute remote command to add lnet options lines to
975 # the MGS node's modprobe.conf/modules.conf
977 add_module_options $idx ${MGS_NODENAME[i]} || return ${PIPESTATUS[0]}
979 # Start lnet network in the MGS node
980 check_lnet $idx || return ${PIPESTATUS[0]}
986 # Get all the Lustre target items in the CSV file and do some checks.
989 if [ $# -eq 0 ]; then
990 error_output "get_lustre_items(): Missing argument"\
991 "for function get_lustre_items()!"
999 declare -i line_num=0
1003 while read -u 9 -r LINE; do
1004 line_num=${line_num}+1
1005 # verbose_output "Parsing line ${line_num}: $LINE"
1007 # Get rid of the empty line
1008 [ -z "`echo ${LINE} | awk '/[[:alnum:]]/ {print $0}'`" ] && continue
1010 # Get rid of the comment line
1011 [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue
1013 # Skip the Linux MD/LVM line
1014 marker=$(echo ${LINE} | cut -d, -f 2)
1015 if [ "${marker}" = "${MD_MARKER}" -o "${marker}" = "${PV_MARKER}" ] \
1016 || [ "${marker}" = "${VG_MARKER}" -o "${marker}" = "${LV_MARKER}" ]; then
1020 # Skip the host which is not specified in the host list
1021 if ! ${USE_ALLNODES}; then
1022 hostname=$(echo ${LINE} | cut -d, -f 1)
1023 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
1026 # Parse the config line into CONFIG_ITEM
1027 if ! parse_line "$LINE"; then
1028 error_output "parse_line(): Occurred"\
1029 "on line ${line_num} in ${CSV_FILE}: $LINE"
1033 HOST_NAME[idx]=${CONFIG_ITEM[0]}
1034 MODULE_OPTS[idx]=${CONFIG_ITEM[1]}
1035 DEVICE_NAME[idx]=${CONFIG_ITEM[2]}
1036 MOUNT_POINT[idx]=${CONFIG_ITEM[3]}
1037 DEVICE_TYPE[idx]=${CONFIG_ITEM[4]}
1038 FS_NAME[idx]=${CONFIG_ITEM[5]}
1039 MGS_NIDS[idx]=${CONFIG_ITEM[6]}
1040 INDEX[idx]=${CONFIG_ITEM[7]}
1041 FORMAT_OPTIONS[idx]=${CONFIG_ITEM[8]}
1042 MKFS_OPTIONS[idx]=${CONFIG_ITEM[9]}
1043 MOUNT_OPTIONS[idx]=${CONFIG_ITEM[10]}
1044 FAILOVERS[idx]=${CONFIG_ITEM[11]}
1046 MODULE_OPTS[idx]=`echo "${MODULE_OPTS[idx]}" | sed 's/"/\\\"/g'`
1048 # Convert IP addresses in NIDs to hostnames
1049 FAILOVERS_NAMES[idx]=$(ip2hostname_multi_node ${FAILOVERS[idx]})
1050 if [ ${PIPESTATUS[0]} -ne 0 ]; then
1051 error_output "${FAILOVERS_NAMES[idx]}"
1055 # Check some required items for formatting target
1056 if ! check_lustre_item $idx; then
1057 error_output "check_lustre_item():"\
1058 "Occurred on line ${line_num} in ${CSV_FILE}."