#!/bin/bash # # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4: # # lc_md - configure Linux MD devices from a csv file # ################################################################################ # Usage usage() { cat >&2 < This script is used to configure Linux MD devices in a Lustre cluster from a csv file. Options: -a select all the nodes from the csv file to operate on -w hostname,hostname,... select the specified list of nodes (separated by commas) -x hostname,hostname,... exclude the specified list of nodes (separated by commas) -h help and examples -v verbose mode csv file a spreadsheet that contains configuration parameters (separated by commas) for each Linux MD device to be configured in a Lustre cluster EOF exit 1 } # Samples sample() { cat <&1) if [ ${PIPESTATUS[0]} -ne 0 ]; then if [ -n "${ret_str}" ]; then error_output "md_is_active():"\ "remote command to ${host_name} error: ${ret_str}!" return 2 # Error occurred else return 1 # inactive fi fi return 0 # active } # construct_mdadm_create_cmdline index # # Construct the create operation command line of mdadm for ${MD_NAME[index]} construct_mdadm_create_cmdline() { declare -i i=$1 local cmd_line local echo_disk disk line declare -i alldisks=0 declare -i raiddisks=0 declare -i sparedisks=0 cmd_line="${MDADM} -C -R ${MD_NAME[i]} ${OP_OPTS[i]} -l ${RAID_LEVEL[i]}" if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -n*}" ]\ || [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--raid-devices*}" ]; then cmd_line=${cmd_line}" ${MD_DEVS[i]}" echo ${cmd_line} return 0 fi # FIXME: Get the number of component devices in the array echo_disk="for disk in ${MD_DEVS[i]}; do echo $disk; done" while read line; do let "alldisks += 1" done < <(${REMOTE} ${HOST_NAME[i]} "${echo_disk}") if [ ${alldisks} -eq 0 ]; then echo "`basename $0`: construct_mdadm_create_cmdline() error:"\ "Failed to execute remote command to get the number of"\ "component devices of array ${MD_NAME[i]} from host ${HOST_NAME[i]}!" return 1 fi # Get the specified number of spare (eXtra) devices if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -x*}" ]; then sparedisks=`echo ${OP_OPTS[i]##* -x}|awk -F" " '{print $1}'` elif [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--spare-devices*}" ]; then sparedisks=`echo ${OP_OPTS[i]##*--spare-devices=}|awk -F" " '{print $1}'` fi # Get the number of raid devices in the array # The number of raid devices in the array plus the number of spare devices # listed on the command line must equal the number of component devices # (including "missing" devices). let "raiddisks = alldisks - sparedisks" if [ ${raiddisks} -lt 1 ]; then echo "`basename $0`: construct_mdadm_create_cmdline() error:"\ "Invalid number of raid devices in array ${MD_NAME[i]}: ${raiddisks}!"\ "Check the number of spare devices and whether all the component devices"\ "\"${MD_DEVS[i]}\" (except \"missing\" devices) exist in host ${HOST_NAME[i]}!" return 1 fi cmd_line=${cmd_line}" -n ${raiddisks} ${MD_DEVS[i]}" echo ${cmd_line} return 0 } # construct_mdadm_rm_cmdline index # # Construct the remove operation command line of mdadm for ${MD_NAME[index]} construct_mdadm_rm_cmdline() { declare -i i=$1 local mdadm_cmd local real_devs # Deactivate the MD array, releasing all resources mdadm_cmd="${MDADM} -S ${MD_NAME[i]}" if [ -n "${MD_DEVS[i]}" ]; then # Remove the "missing" devices from the component devices real_devs=`echo ${MD_DEVS[i]} | sed 's/missing//g'` # Over-written the superblock with zeros mdadm_cmd=${mdadm_cmd}" && ${MDADM} --zero-superblock ${real_devs} || true" fi echo ${mdadm_cmd} return 0 } # construct_mdadm_cmdline host_name # # Construct the command line of mdadm to be run in $host_name construct_mdadm_cmdline() { MDADM_CMDLINE= local host_name=$1 local mdadm_stop_cmd mdadm_cmd local rc OK declare -i i # Construct command line for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do mdadm_stop_cmd= mdadm_cmd= if [ "${host_name}" = "${HOST_NAME[i]}" ]; then case "${OP_MODE[i]}" in "" | create) # Check the status of the MD array md_is_active ${host_name} ${MD_NAME[i]} rc=${PIPESTATUS[0]} if [ "$rc" -eq "2" ]; then return 1 elif [ "$rc" -eq "0" ]; then OK= echo -n "`basename $0`: ${MD_NAME[i]} is active on"\ "${host_name}, go ahead to deactivate it and create"\ "the new array? [y/n]:" read OK if [ "${OK}" = "n" ]; then echo "`basename $0`: ${MD_NAME[i]} on host"\ "${host_name} remains as it is." continue fi # Construct the remove command line mdadm_stop_cmd=$(construct_mdadm_rm_cmdline ${i}) fi # Construct the create command line mdadm_cmd=$(construct_mdadm_create_cmdline ${i}) if [ ${PIPESTATUS[0]} -ne 0 ]; then error_output "${mdadm_cmd}" return 1 fi [ -n "${mdadm_stop_cmd}" ] && mdadm_cmd=${mdadm_stop_cmd}" && "${mdadm_cmd} ;; remove) if [ -z "${MD_NAME[i]}" ]; then OK= echo -n "`basename $0`: Do you really want to remove"\ "all the MD devices in the host ${HOST_NAME[i]}? [y/n]:" read OK if [ "${OK}" = "n" ]; then echo "`basename $0`: MD devices on host"\ "${HOST_NAME[i]} remain as they are." continue fi # Construct the teardown command line mdadm_cmd="(cat /proc/mdstat | egrep \"^md[[:digit:]]\" |" mdadm_cmd=${mdadm_cmd}" while read md rest; do ${MDADM} -S /dev/\$md; done)" else # Construct the remove command line mdadm_cmd=$(construct_mdadm_rm_cmdline ${i}) fi ;; *) # Other operations mdadm_cmd="${MDADM} ${OP_MODE[i]} ${MD_NAME[i]} ${OP_OPTS[i]} ${MD_DEVS[i]}" ;; esac if [ -z "${MDADM_CMDLINE}" ]; then MDADM_CMDLINE=${mdadm_cmd} else MDADM_CMDLINE=${MDADM_CMDLINE}" && "${mdadm_cmd} fi fi done return 0 } # config_md_devs host_name # # Run remote command to configure MD devices in $host_name config_md_devs() { local host_name=$1 # Construct mdadm command line if ! construct_mdadm_cmdline ${host_name}; then return 1 fi if [ -z "${MDADM_CMDLINE}" ]; then verbose_output "There are no MD devices on host ${host_name}"\ "needed to be configured." return 0 fi # Run remote command to configure MD devices in $host_name verbose_output "Configuring MD devices in host ${host_name}..." verbose_output "Configure command line is: \"${MDADM_CMDLINE}\"" REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${MDADM_CMDLINE}\"" $REMOTE $host_name "export PATH=\$PATH:/sbin:/usr/sbin; $MDADM_CMDLINE" & REMOTE_PID[pid_num]=$! let "pid_num += 1" sleep 1 return 0 } # Run remote command to configure all the MD devices specified in the csv file config_md() { declare -i i=0 declare -i idx=0 # Index of NODE_NAME array local host_name local failed_status # Initialize the NODE_NAME array unset NODE_NAME for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do host_name=${HOST_NAME[i]} configured_host ${host_name} && continue NODE_NAME[idx]=${host_name} let "idx += 1" # Run remote command to configure MD devices in $host_name if ! config_md_devs ${host_name}; then return 1 fi done if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then verbose_output "There are no MD devices to be configured." return 0 fi # Wait for the exit status of the background remote command verbose_output "Waiting for the return of the remote command..." failed_status=false for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do wait ${REMOTE_PID[${pid_num}]} if [ ${PIPESTATUS[0]} -ne 0 ]; then error_output "config_md(): Failed"\ "to execute \"${REMOTE_CMD[${pid_num}]}\"!" failed_status=true fi done if ${failed_status}; then return 1 fi verbose_output "All the MD devices are configured successfully!" return 0 } # Main flow # Check the csv file check_file $CSV_FILE || exit ${PIPESTATUS[0]} # Get the list of nodes to be operated on NODES_TO_USE=$(get_nodelist) || error_exit ${PIPESTATUS[0]} "$NODES_TO_USE" # Check the node list check_nodelist ${NODES_TO_USE} || exit 1 # Get all the MD device items from the csv file if ! get_md_items ${CSV_FILE}; then exit 1 fi # Configure the MD devices if ! config_md; then exit 1 fi exit 0