3 # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
5 # lc_md - configure Linux MD devices from a csv file
7 ################################################################################
13 Usage: `basename $0` [options] <csv file>
15 This script is used to configure Linux MD devices in a Lustre cluster
19 -a select all the nodes from the csv file to operate on
20 -w hostname,hostname,...
21 select the specified list of nodes (separated by commas)
22 -x hostname,hostname,...
23 exclude the specified list of nodes (separated by commas)
26 csv file a spreadsheet that contains configuration parameters
27 (separated by commas) for each Linux MD device to be
28 configured in a Lustre cluster
38 This script is used to configure Linux MD devices in a Lustre cluster
41 Each line marked with "MD" in the csv file represents one MD device.
43 hostname,MD,md name,operation mode,options,raid level,component devices
45 hostname hostname of the node in the cluster
46 MD marker of MD device line
47 md name MD device name, e.g. /dev/md0
48 operation mode create or remove, default is create
49 options a "catchall" for other mdadm options, e.g. "-c 128"
50 raid level raid level: 0,1,4,5,6,10,linear and multipath
51 component devices block devices to be combined into the MD device
52 Multiple devices are separated by space or by using
53 shell expansions, e.g. "/dev/sd{a,b,c}"
55 Items left blank will be set to defaults.
58 -------------------------------------------------------
59 # MD devices on mgsnode
60 mgsnode,MD,/dev/md0,,-q -c 32,1,/dev/sda1 /dev/sdb1
61 mgsnode,MD,/dev/md1,,-q -c 32,1,/dev/sdc1 /dev/sdd1
62 mgsnode,MD,/dev/md2,,-q -c 32,0,/dev/md0 /dev/md1
64 # MD device on ostnode
65 ostnode,MD,/dev/md0,,-q -c 128,5,"/dev/sd{a,b,c,d,e}"
66 -------------------------------------------------------
72 # Get the library of functions
73 . @scriptlibdir@/lc_common
75 #***************************** Global variables *****************************#
76 # All the MD device items in the csv file
77 declare -a HOST_NAME MD_NAME OP_MODE OP_OPTS RAID_LEVEL MD_DEVS
79 # Variables related to background executions
86 # Get and check the positional parameters
87 while getopts "aw:x:hv" OPTION; do
90 [ -z "${SPECIFIED_NODELIST}" ] && [ -z "${EXCLUDED_NODELIST}" ] \
95 SPECIFIED_NODELIST=$OPTARG
99 EXCLUDED_NODELIST=$OPTARG
112 # Toss out the parameters we've already processed
113 shift `expr $OPTIND - 1`
115 # Here we expect the csv file
116 if [ $# -eq 0 ]; then
117 error_output "Missing csv file!"
123 # check_md_item index
125 # Check the items required for managing MD device ${MD_NAME[index]}
128 if [ $# -eq 0 ]; then
129 error_output "check_md_item():"\
137 if [ -z "${HOST_NAME[i]}" ]; then
138 error_output "check_md_item():"\
139 "hostname item has null value!"
143 # Check items required by create mode
144 if [ -z "${OP_MODE[i]}" -o "${OP_MODE[i]}" = "create" ]; then
145 # Check MD device name
146 if [ -z "${MD_NAME[i]}" ]; then
147 error_output "check_md_item():"\
148 "md name item has null value!"
152 if [ -z "${RAID_LEVEL[i]}" ]; then
153 error_output "check_md_item():"\
154 "raid level item of MD device ${MD_NAME[i]} has null value!"
158 if [ -z "${MD_DEVS[i]}" ]; then
159 error_output "check_md_item():"\
160 "component devices item of ${MD_NAME[i]} has null value!"
168 # get_md_items csv_file
170 # Get all the MD device items in the $csv_file and do some checks.
173 if [ $# -eq 0 ]; then
174 error_output "get_md_items(): Missing csv file!"
181 declare -i line_num=0
184 while read -r LINE; do
187 # Skip the comment line
188 [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue
190 # Skip the non-MD line
191 [ "$(echo ${LINE} | cut -d, -f 2)" != "${MD_MARKER}" ] && continue
193 # Skip the host which is not specified in the host list
194 if ! ${USE_ALLNODES}; then
195 hostname=$(echo ${LINE} | cut -d, -f 1)
196 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
199 # Parse the config line into CONFIG_ITEM
200 if ! parse_line "$LINE"; then
204 HOST_NAME[idx]=${CONFIG_ITEM[0]}
205 MD_NAME[idx]=${CONFIG_ITEM[2]}
206 OP_MODE[idx]=${CONFIG_ITEM[3]}
207 OP_OPTS[idx]=${CONFIG_ITEM[4]}
208 RAID_LEVEL[idx]=${CONFIG_ITEM[5]}
209 MD_DEVS[idx]=${CONFIG_ITEM[6]}
211 # Check some required items
212 if ! check_md_item $idx; then
213 error_output "check_md_item():"\
214 "Occurred on line ${line_num} in ${CSV_FILE}."
224 # md_is_active host_name md_name
226 # Run remote command to check whether $md_name is active in @host_name
232 cmd="grep -q ${md_name##*/} /proc/mdstat 2>&1"
233 ret_str=$(${REMOTE} ${host_name} "${cmd}" 2>&1)
234 if [ ${PIPESTATUS[0]} -ne 0 ]; then
235 if [ -n "${ret_str}" ]; then
236 error_output "md_is_active():"\
237 "remote command to ${host_name} error: ${ret_str}!"
238 return 2 # Error occurred
247 # construct_mdadm_create_cmdline index
249 # Construct the create operation command line of mdadm for ${MD_NAME[index]}
250 construct_mdadm_create_cmdline() {
253 local echo_disk disk line
254 declare -i alldisks=0
255 declare -i raiddisks=0
256 declare -i sparedisks=0
258 cmd_line="${MDADM} -C -R ${MD_NAME[i]} ${OP_OPTS[i]} -l ${RAID_LEVEL[i]}"
260 if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -n*}" ]\
261 || [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--raid-devices*}" ]; then
262 cmd_line=${cmd_line}" ${MD_DEVS[i]}"
267 # FIXME: Get the number of component devices in the array
268 echo_disk="for disk in ${MD_DEVS[i]}; do echo $disk; done"
271 done < <(${REMOTE} ${HOST_NAME[i]} "${echo_disk}")
273 if [ ${alldisks} -eq 0 ]; then
274 echo "`basename $0`: construct_mdadm_create_cmdline() error:"\
275 "Failed to execute remote command to get the number of"\
276 "component devices of array ${MD_NAME[i]} from host ${HOST_NAME[i]}!"
280 # Get the specified number of spare (eXtra) devices
281 if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -x*}" ]; then
282 sparedisks=`echo ${OP_OPTS[i]##* -x}|awk -F" " '{print $1}'`
283 elif [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--spare-devices*}" ]; then
284 sparedisks=`echo ${OP_OPTS[i]##*--spare-devices=}|awk -F" " '{print $1}'`
287 # Get the number of raid devices in the array
288 # The number of raid devices in the array plus the number of spare devices
289 # listed on the command line must equal the number of component devices
290 # (including "missing" devices).
291 let "raiddisks = alldisks - sparedisks"
293 if [ ${raiddisks} -lt 1 ]; then
294 echo "`basename $0`: construct_mdadm_create_cmdline() error:"\
295 "Invalid number of raid devices in array ${MD_NAME[i]}: ${raiddisks}!"\
296 "Check the number of spare devices and whether all the component devices"\
297 "\"${MD_DEVS[i]}\" (except \"missing\" devices) exist in host ${HOST_NAME[i]}!"
301 cmd_line=${cmd_line}" -n ${raiddisks} ${MD_DEVS[i]}"
307 # construct_mdadm_rm_cmdline index
309 # Construct the remove operation command line of mdadm for ${MD_NAME[index]}
310 construct_mdadm_rm_cmdline() {
315 # Deactivate the MD array, releasing all resources
316 mdadm_cmd="${MDADM} -S ${MD_NAME[i]}"
318 if [ -n "${MD_DEVS[i]}" ]; then
319 # Remove the "missing" devices from the component devices
320 real_devs=`echo ${MD_DEVS[i]} | sed 's/missing//g'`
321 # Over-written the superblock with zeros
322 mdadm_cmd=${mdadm_cmd}" && ${MDADM} --zero-superblock ${real_devs} || true"
329 # construct_mdadm_cmdline host_name
331 # Construct the command line of mdadm to be run in $host_name
332 construct_mdadm_cmdline() {
335 local mdadm_stop_cmd mdadm_cmd
339 # Construct command line
340 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
343 if [ "${host_name}" = "${HOST_NAME[i]}" ]; then
344 case "${OP_MODE[i]}" in
346 # Check the status of the MD array
347 md_is_active ${host_name} ${MD_NAME[i]}
349 if [ "$rc" -eq "2" ]; then
351 elif [ "$rc" -eq "0" ]; then
353 echo -n "`basename $0`: ${MD_NAME[i]} is active on"\
354 "${host_name}, go ahead to deactivate it and create"\
355 "the new array? [y/n]:"
357 if [ "${OK}" = "n" ]; then
358 echo "`basename $0`: ${MD_NAME[i]} on host"\
359 "${host_name} remains as it is."
363 # Construct the remove command line
364 mdadm_stop_cmd=$(construct_mdadm_rm_cmdline ${i})
367 # Construct the create command line
368 mdadm_cmd=$(construct_mdadm_create_cmdline ${i})
369 if [ ${PIPESTATUS[0]} -ne 0 ]; then
370 error_output "${mdadm_cmd}"
374 [ -n "${mdadm_stop_cmd}" ] && mdadm_cmd=${mdadm_stop_cmd}" && "${mdadm_cmd}
377 if [ -z "${MD_NAME[i]}" ]; then
379 echo -n "`basename $0`: Do you really want to remove"\
380 "all the MD devices in the host ${HOST_NAME[i]}? [y/n]:"
382 if [ "${OK}" = "n" ]; then
383 echo "`basename $0`: MD devices on host"\
384 "${HOST_NAME[i]} remain as they are."
388 # Construct the teardown command line
389 mdadm_cmd="(cat /proc/mdstat | egrep \"^md[[:digit:]]\" |"
390 mdadm_cmd=${mdadm_cmd}" while read md rest; do ${MDADM} -S /dev/\$md; done)"
392 # Construct the remove command line
393 mdadm_cmd=$(construct_mdadm_rm_cmdline ${i})
398 mdadm_cmd="${MDADM} ${OP_MODE[i]} ${MD_NAME[i]} ${OP_OPTS[i]} ${MD_DEVS[i]}"
402 if [ -z "${MDADM_CMDLINE}" ]; then
403 MDADM_CMDLINE=${mdadm_cmd}
405 MDADM_CMDLINE=${MDADM_CMDLINE}" && "${mdadm_cmd}
413 # config_md_devs host_name
415 # Run remote command to configure MD devices in $host_name
419 # Construct mdadm command line
420 if ! construct_mdadm_cmdline ${host_name}; then
424 if [ -z "${MDADM_CMDLINE}" ]; then
425 verbose_output "There are no MD devices on host ${host_name}"\
426 "needed to be configured."
430 # Run remote command to configure MD devices in $host_name
431 verbose_output "Configuring MD devices in host ${host_name}..."
432 verbose_output "Configure command line is: \"${MDADM_CMDLINE}\""
433 REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${MDADM_CMDLINE}\""
434 $REMOTE $host_name "export PATH=\$PATH:/sbin:/usr/sbin; $MDADM_CMDLINE" &
435 REMOTE_PID[pid_num]=$!
442 # Run remote command to configure all the MD devices specified in the csv file
445 declare -i idx=0 # Index of NODE_NAME array
449 # Initialize the NODE_NAME array
452 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
453 host_name=${HOST_NAME[i]}
454 configured_host ${host_name} && continue
456 NODE_NAME[idx]=${host_name}
459 # Run remote command to configure MD devices in $host_name
460 if ! config_md_devs ${host_name}; then
465 if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then
466 verbose_output "There are no MD devices to be configured."
470 # Wait for the exit status of the background remote command
471 verbose_output "Waiting for the return of the remote command..."
473 for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
474 wait ${REMOTE_PID[${pid_num}]}
475 if [ ${PIPESTATUS[0]} -ne 0 ]; then
476 error_output "config_md(): Failed"\
477 "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
482 if ${failed_status}; then
486 verbose_output "All the MD devices are configured successfully!"
492 check_file $CSV_FILE || exit ${PIPESTATUS[0]}
494 # Get the list of nodes to be operated on
495 NODES_TO_USE=$(get_nodelist) || error_exit ${PIPESTATUS[0]} "$NODES_TO_USE"
497 # Check the node list
498 check_nodelist ${NODES_TO_USE} || exit 1
500 # Get all the MD device items from the csv file
501 if ! get_md_items ${CSV_FILE}; then
505 # Configure the MD devices