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 echo >&2 "`basename $0`: Missing csv file!"
121 # check_md_item index
123 # Check the items required for managing MD device ${MD_NAME[index]}
126 if [ $# -eq 0 ]; then
127 echo >&2 "`basename $0`: check_md_item() error:"\
135 if [ -z "${HOST_NAME[i]}" ]; then
136 echo >&2 "`basename $0`: check_md_item() error:"\
137 "hostname item has null value!"
141 # Check items required by create mode
142 if [ -z "${OP_MODE[i]}" -o "${OP_MODE[i]}" = "create" ]; then
143 # Check MD device name
144 if [ -z "${MD_NAME[i]}" ]; then
145 echo >&2 "`basename $0`: check_md_item() error:"\
146 "md name item has null value!"
150 if [ -z "${RAID_LEVEL[i]}" ]; then
151 echo >&2 "`basename $0`: check_md_item() error:"\
152 "raid level item of MD device ${MD_NAME[i]} has null value!"
156 if [ -z "${MD_DEVS[i]}" ]; then
157 echo >&2 "`basename $0`: check_md_item() error:"\
158 "component devices item of ${MD_NAME[i]} has null value!"
166 # get_md_items csv_file
168 # Get all the MD device items in the $csv_file and do some checks.
171 if [ $# -eq 0 ]; then
172 echo >&2 "`basename $0`: get_md_items() error: Missing csv file!"
179 declare -i line_num=0
182 while read -r LINE; do
185 # Skip the comment line
186 [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue
188 # Skip the non-MD line
189 [ "$(echo ${LINE} | cut -d, -f 2)" != "${MD_MARKER}" ] && continue
191 # Skip the host which is not specified in the host list
192 if ! ${USE_ALLNODES}; then
193 hostname=$(echo ${LINE} | cut -d, -f 1)
194 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
197 # Parse the config line into CONFIG_ITEM
198 if ! parse_line "$LINE"; then
202 HOST_NAME[idx]=${CONFIG_ITEM[0]}
203 MD_NAME[idx]=${CONFIG_ITEM[2]}
204 OP_MODE[idx]=${CONFIG_ITEM[3]}
205 OP_OPTS[idx]=${CONFIG_ITEM[4]}
206 RAID_LEVEL[idx]=${CONFIG_ITEM[5]}
207 MD_DEVS[idx]=${CONFIG_ITEM[6]}
209 # Check some required items
210 if ! check_md_item $idx; then
211 echo >&2 "`basename $0`: check_md_item() error:"\
212 "Occurred on line ${line_num} in ${CSV_FILE}."
222 # md_is_active host_name md_name
224 # Run remote command to check whether $md_name is active in @host_name
230 cmd="grep -q ${md_name##*/} /proc/mdstat 2>&1"
231 ret_str=$(${REMOTE} ${host_name} "${cmd}" 2>&1)
232 if [ ${PIPESTATUS[0]} -ne 0 ]; then
233 if [ -n "${ret_str}" ]; then
234 echo >&2 "`basename $0`: md_is_active() error:"\
235 "remote command to ${host_name} error: ${ret_str}!"
236 return 2 # Error occurred
245 # construct_mdadm_create_cmdline index
247 # Construct the create operation command line of mdadm for ${MD_NAME[index]}
248 construct_mdadm_create_cmdline() {
251 local echo_disk disk line
252 declare -i alldisks=0
253 declare -i raiddisks=0
254 declare -i sparedisks=0
256 cmd_line="${MDADM} -C -R ${MD_NAME[i]} ${OP_OPTS[i]} -l ${RAID_LEVEL[i]}"
258 if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -n*}" ]\
259 || [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--raid-devices*}" ]; then
260 cmd_line=${cmd_line}" ${MD_DEVS[i]}"
265 # FIXME: Get the number of component devices in the array
266 echo_disk="for disk in ${MD_DEVS[i]}; do echo $disk; done"
269 done < <(${REMOTE} ${HOST_NAME[i]} "${echo_disk}")
271 if [ ${alldisks} -eq 0 ]; then
272 echo "`basename $0`: construct_mdadm_create_cmdline() error:"\
273 "Failed to execute remote command to get the number of"\
274 "component devices of array ${MD_NAME[i]} from host ${HOST_NAME[i]}!"
278 # Get the specified number of spare (eXtra) devices
279 if [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#* -x*}" ]; then
280 sparedisks=`echo ${OP_OPTS[i]##* -x}|awk -F" " '{print $1}'`
281 elif [ "${OP_OPTS[i]}" != "${OP_OPTS[i]#*--spare-devices*}" ]; then
282 sparedisks=`echo ${OP_OPTS[i]##*--spare-devices=}|awk -F" " '{print $1}'`
285 # Get the number of raid devices in the array
286 # The number of raid devices in the array plus the number of spare devices
287 # listed on the command line must equal the number of component devices
288 # (including "missing" devices).
289 let "raiddisks = alldisks - sparedisks"
291 if [ ${raiddisks} -lt 1 ]; then
292 echo "`basename $0`: construct_mdadm_create_cmdline() error:"\
293 "Invalid number of raid devices in array ${MD_NAME[i]}: ${raiddisks}!"\
294 "Check the number of spare devices and whether all the component devices"\
295 "\"${MD_DEVS[i]}\" (except \"missing\" devices) exist in host ${HOST_NAME[i]}!"
299 cmd_line=${cmd_line}" -n ${raiddisks} ${MD_DEVS[i]}"
305 # construct_mdadm_rm_cmdline index
307 # Construct the remove operation command line of mdadm for ${MD_NAME[index]}
308 construct_mdadm_rm_cmdline() {
313 # Deactivate the MD array, releasing all resources
314 mdadm_cmd="${MDADM} -S ${MD_NAME[i]}"
316 if [ -n "${MD_DEVS[i]}" ]; then
317 # Remove the "missing" devices from the component devices
318 real_devs=`echo ${MD_DEVS[i]} | sed 's/missing//g'`
319 # Over-written the superblock with zeros
320 mdadm_cmd=${mdadm_cmd}" && ${MDADM} --zero-superblock ${real_devs}"
327 # construct_mdadm_cmdline host_name
329 # Construct the command line of mdadm to be run in $host_name
330 construct_mdadm_cmdline() {
333 local mdadm_stop_cmd mdadm_cmd
337 # Construct command line
338 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
341 if [ "${host_name}" = "${HOST_NAME[i]}" ]; then
342 case "${OP_MODE[i]}" in
344 # Check the status of the MD array
345 md_is_active ${host_name} ${MD_NAME[i]}
347 if [ "$rc" -eq "2" ]; then
349 elif [ "$rc" -eq "0" ]; then
351 echo -n "`basename $0`: ${MD_NAME[i]} is active on"\
352 "${host_name}, go ahead to deactivate it and create"\
353 "the new array? [y/n]:"
355 if [ "${OK}" = "n" ]; then
356 echo "`basename $0`: ${MD_NAME[i]} on host"\
357 "${host_name} remains as it is."
361 # Construct the remove command line
362 mdadm_stop_cmd=$(construct_mdadm_rm_cmdline ${i})
365 # Construct the create command line
366 mdadm_cmd=$(construct_mdadm_create_cmdline ${i})
367 if [ ${PIPESTATUS[0]} -ne 0 ]; then
368 echo >&2 "${mdadm_cmd}"
372 [ -n "${mdadm_stop_cmd}" ] && mdadm_cmd=${mdadm_stop_cmd}" && "${mdadm_cmd}
375 if [ -z "${MD_NAME[i]}" ]; then
377 echo -n "`basename $0`: Do you really want to remove"\
378 "all the MD devices in the host ${HOST_NAME[i]}? [y/n]:"
380 if [ "${OK}" = "n" ]; then
381 echo "`basename $0`: MD devices on host"\
382 "${HOST_NAME[i]} remain as they are."
386 # Construct the teardown command line
387 mdadm_cmd="(cat /proc/mdstat | egrep \"^md[[:digit:]]\" |"
388 mdadm_cmd=${mdadm_cmd}" while read md rest; do ${MDADM} -S /dev/\$md; done)"
390 # Construct the remove command line
391 mdadm_cmd=$(construct_mdadm_rm_cmdline ${i})
396 mdadm_cmd="${MDADM} ${OP_MODE[i]} ${MD_NAME[i]} ${OP_OPTS[i]} ${MD_DEVS[i]}"
400 if [ -z "${MDADM_CMDLINE}" ]; then
401 MDADM_CMDLINE=${mdadm_cmd}
403 MDADM_CMDLINE=${MDADM_CMDLINE}" && "${mdadm_cmd}
411 # config_md_devs host_name
413 # Run remote command to configure MD devices in $host_name
417 # Construct mdadm command line
418 if ! construct_mdadm_cmdline ${host_name}; then
422 if [ -z "${MDADM_CMDLINE}" ]; then
423 verbose_output "There are no MD devices on host ${host_name}"\
424 "needed to be configured."
428 # Run remote command to configure MD devices in $host_name
429 verbose_output "Configuring MD devices in host ${host_name}..."
430 verbose_output "Configure command line is: \"${MDADM_CMDLINE}\""
431 REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${MDADM_CMDLINE}\""
432 ${REMOTE} ${host_name} "${MDADM_CMDLINE}" >&2 &
433 REMOTE_PID[pid_num]=$!
440 # Run remote command to configure all the MD devices specified in the csv file
443 declare -i idx=0 # Index of NODE_NAME array
447 # Initialize the NODE_NAME array
450 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
451 host_name=${HOST_NAME[i]}
452 configured_host ${host_name} && continue
454 NODE_NAME[idx]=${host_name}
457 # Run remote command to configure MD devices in $host_name
458 if ! config_md_devs ${host_name}; then
463 if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then
464 verbose_output "There are no MD devices to be configured."
468 # Wait for the exit status of the background remote command
469 verbose_output "Waiting for the return of the remote command..."
471 for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
472 wait ${REMOTE_PID[${pid_num}]}
473 if [ ${PIPESTATUS[0]} -ne 0 ]; then
474 echo >&2 "`basename $0`: config_md() error: Failed"\
475 "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
480 if ${failed_status}; then
484 verbose_output "All the MD devices are configured successfully!"
490 if ! check_file $1; then
494 # Get the list of nodes to be operated on
495 NODES_TO_USE=$(get_nodelist)
496 [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1
498 # Check the node list
499 check_nodelist ${NODES_TO_USE} || exit 1
501 # Get all the MD device items from the csv file
502 if ! get_md_items ${CSV_FILE}; then
506 # Configure the MD devices