3 # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
5 # lc_lvm - configure Linux LVM devices from a csv file
7 ################################################################################
13 Usage: `basename $0` [options] <csv file>
15 This script is used to configure Linux LVM 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 LVM component
28 (PV, VG, LV) to be configured in a Lustre cluster
38 This script is used to configure Linux LVM devices in a Lustre cluster
41 LVM is a Logical Volume Manager for the Linux operating system. The
42 three-level components of it are PV (Physical Volume), VG (Volume Group)
43 and LV (Logical Volume).
45 Each line marked with "PV" in the csv file represents one or more PVs.
47 hostname,PV,pv names,operation mode,options
49 hostname hostname of the node in the cluster
51 pv names devices or loopback files to be initialized for later
52 use by LVM or to be wiped the label, e.g. /dev/sda
53 Multiple devices or files are separated by space or by
54 using shell expansions, e.g. "/dev/sd{a,b,c}"
55 operation mode create or remove, default is create
56 options a "catchall" for other pvcreate/pvremove options
59 Each line marked with "VG" in the csv file represents one VG.
61 hostname,VG,vg name,operation mode,options,pv paths
63 hostname hostname of the node in the cluster
65 vg name name of the volume group, e.g. ost_vg
66 operation mode create or remove, default is create
67 options a "catchall" for other vgcreate/vgremove options
69 pv paths physical volumes to construct this VG, required by
71 Multiple PVs are separated by space or by using
72 shell expansions, e.g. "/dev/sd[k-m]1"
74 Each line marked with "LV" in the csv file represents one LV.
76 hostname,LV,lv name,operation mode,options,lv size,vg name
78 hostname hostname of the node in the cluster
80 lv name name of the logical volume to be created (optional)
81 or path of the logical volume to be removed (required
83 operation mode create or remove, default is create
84 options a "catchall" for other lvcreate/lvremove options
86 lv size size [kKmMgGtT] to be allocated for the new LV
87 Default unit is megabytes.
88 vg name name of the VG in which the new LV will be created
90 Items left blank will be set to defaults.
93 -------------------------------------------------------
94 # MD/LVM devices on mgsnode
95 # Remove the LVM devices in the mgsnode
96 mgsnode,LV,/dev/mgs_vg/mdt1,remove
97 mgsnode,LV,/dev/mgs_vg/mdt2,remove
98 mgsnode,VG,mgs_vg,remove
99 mgsnode,PV,"/dev/sd{a,b}1",remove
101 # Create MD device in the mgsnode
102 mgsnode,MD,/dev/md0,,-q,1,/dev/sda1 /dev/sdb1
105 # MD/LVM devices on ostnode
106 # Create MD and LVM devices in the ostnode
107 ostnode,MD,/dev/md0,,-q -c 128,5,"/dev/sd{a,b,c}"
108 ostnode,MD,/dev/md1,,-q -c 128,5,"/dev/sd{d,e,f}"
110 ostnode,PV,/dev/md0 /dev/md1
111 ostnode,VG,ost_vg,,-s 32M,"/dev/md{0,1}"
112 ostnode,LV,ost0,,-i 2 -I 128,300G,ost_vg
113 ostnode,LV,ost1,,-i 2 -I 128,300G,ost_vg
114 -------------------------------------------------------
120 # Get the library of functions
121 . @scriptlibdir@/lc_common
123 #***************************** Global variables *****************************#
124 # All the LVM device items in the csv file
125 declare -a HOST_NAME LINE_MARKER LVM_NAME OP_MODE OP_OPTS SIXTH_ITEM SEVENTH_ITEM
127 # Variables related to background executions
128 declare -a REMOTE_CMD
129 declare -a REMOTE_PID
134 # Get and check the positional parameters
135 while getopts "aw:x:hv" OPTION; do
138 [ -z "${SPECIFIED_NODELIST}" ] && [ -z "${EXCLUDED_NODELIST}" ] \
143 SPECIFIED_NODELIST=$OPTARG
147 EXCLUDED_NODELIST=$OPTARG
160 # Toss out the parameters we've already processed
161 shift `expr $OPTIND - 1`
163 # Here we expect the csv file
164 if [ $# -eq 0 ]; then
165 echo >&2 "`basename $0`: Missing csv file!"
169 # check_lvm_item index
171 # Check the items required for managing LVM device ${LVM_NAME[index]}
174 if [ $# -eq 0 ]; then
175 echo >&2 "`basename $0`: check_lvm_item() error:"\
183 if [ -z "${HOST_NAME[i]}" ]; then
184 echo >&2 "`basename $0`: check_lvm_item() error:"\
185 "hostname item has null value!"
189 # Check LVM device name
190 if [ -z "${LVM_NAME[i]}" ] \
191 && [ "${LINE_MARKER[i]}" != "${LV_MARKER}" -a "${OP_MODE[i]}" != "remove" ]
193 echo >&2 "`basename $0`: check_lvm_item() error:"\
194 "LVM component name item has null value!"
198 # Check the operation mode
199 if [ -n "${OP_MODE[i]}" ] \
200 && [ "${OP_MODE[i]}" != "create" -a "${OP_MODE[i]}" != "remove" ]
202 echo >&2 "`basename $0`: check_lvm_item() error:"\
203 "Invalid operation mode item - \"${OP_MODE[i]}\"!"
207 # Check items required by create mode
208 if [ -z "${OP_MODE[i]}" -o "${OP_MODE[i]}" = "create" ]; then
209 if [ "${LINE_MARKER[i]}" = "${VG_MARKER}" -a -z "${SIXTH_ITEM[i]}" ]
211 echo >&2 "`basename $0`: check_lvm_item() error:"\
212 "pv paths item of vg ${LVM_NAME[i]} has null value!"
216 if [ "${LINE_MARKER[i]}" = "${LV_MARKER}" ]; then
217 if [ -z "${SIXTH_ITEM[i]}" ]; then
218 echo >&2 "`basename $0`: check_lvm_item() error:"\
219 "lv size item has null value!"
223 if [ -z "${SEVENTH_ITEM[i]}" ]; then
224 echo >&2 "`basename $0`: check_lvm_item() error:"\
225 "vg name item has null value!"
234 # get_lvm_items csv_file
236 # Get all the LVM device items in the $csv_file and do some checks.
239 if [ $# -eq 0 ]; then
240 echo >&2 "`basename $0`: get_lvm_items() error: Missing csv file!"
245 local LINE line_marker
247 declare -i line_num=0
250 while read -r LINE; do
253 # Skip the comment line
254 [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue
256 # Skip the non-LVM line
257 line_marker=$(echo ${LINE} | cut -d, -f 2)
258 [ "${line_marker}" != "${PV_MARKER}" ] \
259 && [ "${line_marker}" != "${VG_MARKER}" ] \
260 && [ "${line_marker}" != "${LV_MARKER}" ] && continue
262 # Skip the host which is not specified in the host list
263 if ! ${USE_ALLNODES}; then
264 hostname=$(echo ${LINE} | cut -d, -f 1)
265 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
268 # Parse the config line into CONFIG_ITEM
269 if ! parse_line "$LINE"; then
273 HOST_NAME[idx]=${CONFIG_ITEM[0]}
274 LINE_MARKER[idx]=${CONFIG_ITEM[1]}
275 LVM_NAME[idx]=${CONFIG_ITEM[2]}
276 OP_MODE[idx]=${CONFIG_ITEM[3]}
277 OP_OPTS[idx]=${CONFIG_ITEM[4]}
278 SIXTH_ITEM[idx]=${CONFIG_ITEM[5]}
279 SEVENTH_ITEM[idx]=${CONFIG_ITEM[6]}
281 # Check some required items
282 if ! check_lvm_item $idx; then
283 echo >&2 "`basename $0`: check_lvm_item() error:"\
284 "Occurred on line ${line_num} in ${CSV_FILE}."
294 # construct_lvm_create_cmdline index
296 # Construct the creation command line for ${LVM_NAME[index]}
297 construct_lvm_create_cmdline() {
301 case "${LINE_MARKER[i]}" in
303 lvm_cmd="pvcreate -ff -y ${OP_OPTS[i]} ${LVM_NAME[i]}"
306 lvm_cmd="vgcreate ${OP_OPTS[i]} ${LVM_NAME[i]} ${SIXTH_ITEM[i]}"
309 if [ -z "${LVM_NAME[i]}" ]; then
310 lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}"
312 lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} -n ${LVM_NAME[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}"
321 # cmdline_rm_LVs vg_name
323 # Construct command line to remove all the LVs on $vg_name.
324 # If $vg_name is null, then remove all the LVs in the host.
329 lvm_rm_cmd="vgchange -a n ${vg_name} &&"
330 lvm_rm_cmd=${lvm_rm_cmd}" vgdisplay -v ${vg_name} | grep \"LV Name\" | awk '{print \$3}' |"
331 lvm_rm_cmd=${lvm_rm_cmd}" while read lv; do lvremove -f \$lv; done"
337 # cmdline_rm_LV lv_path
339 # Construct command line to remove LV $lv_path
344 lvm_rm_cmd="lvchange -a n ${lv_path} && lvremove -f ${lv_path}"
350 # cmdline_rm_VG vg_name
352 # Construct command line to remove VG $vg_name
357 # Remove all the LVs on this VG
358 lvm_rm_cmd=$(cmdline_rm_LVs ${vg_name})
361 lvm_rm_cmd=${lvm_rm_cmd}" && vgremove ${vg_name}"
368 # Construct command line to remove all the VGs in the host
372 # Remove all the LVs in the host
373 lvm_rm_cmd=$(cmdline_rm_LVs)
375 # Remove all the VGs in the host
376 lvm_rm_cmd=${lvm_rm_cmd}" && vgdisplay | grep \"VG Name\" | awk '{print \$3}' |"
377 lvm_rm_cmd=${lvm_rm_cmd}" while read vg; do vgremove \$vg; done"
385 # Construct command line to remove all the PVs in the host
389 # Remove all the LVs and VGs in the host
390 lvm_rm_cmd=$(cmdline_rm_VGs)
392 # Remove all the PVs in the host
393 lvm_rm_cmd=${lvm_rm_cmd}" && pvdisplay | grep \"PV Name\" | awk '{print \$3}' |"
394 lvm_rm_cmd=${lvm_rm_cmd}" while read pv; do pvremove -ff -y \$pv; done"
400 # construct_lvm_teardown_cmdline index
402 # Construct the teardown command line for LVM devices in ${HOST_NAME[index]}
403 construct_lvm_teardown_cmdline() {
407 case "${LINE_MARKER[i]}" in
409 lvm_rm_cmd=$(cmdline_rm_LVs ${SEVENTH_ITEM[i]})
412 # Remove all the VGs in the host
413 lvm_rm_cmd=$(cmdline_rm_VGs)
416 # Remove all the PVs in the host
417 lvm_rm_cmd=$(cmdline_rm_PVs)
425 # construct_lvm_rm_cmdline index
427 # Construct the remove command line for LVM device ${LVM_NAME[index]}
428 construct_lvm_rm_cmdline() {
432 case "${LINE_MARKER[i]}" in
434 lvm_rm_cmd=$(cmdline_rm_LV ${LVM_NAME[i]})
437 lvm_rm_cmd=$(cmdline_rm_VG ${LVM_NAME[i]})
440 lvm_rm_cmd="pvremove -ff -y ${LVM_NAME[i]}"
448 # construct_lvm_cmdline host_name
450 # Construct the command line of LVM utilities to be run in the $host_name
451 construct_lvm_cmdline() {
457 # Construct command line
458 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
460 if [ "${host_name}" = "${HOST_NAME[i]}" ]; then
461 case "${OP_MODE[i]}" in
463 # Construct the create command line
464 lvm_cmd=$(construct_lvm_create_cmdline ${i})
467 if [ -z "${LVM_NAME[i]}" ]; then
468 # Construct the teardown command line
469 lvm_cmd=$(construct_lvm_teardown_cmdline ${i})
470 else # Remove instead of teardown
471 # Construct the remove command line
472 lvm_cmd=$(construct_lvm_rm_cmdline ${i})
476 echo >&2 "`basename $0`: construct_lvm_cmdline() error:"\
477 "Invalid operation mode - \"${OP_MODE[i]}\"!"
482 if [ -z "${LVM_CMDLINE}" ]; then
483 LVM_CMDLINE=${lvm_cmd}
485 LVM_CMDLINE=${LVM_CMDLINE}" && "${lvm_cmd}
493 # config_lvm_devs host_name
495 # Run remote command to configure LVM devices in $host_name
499 # Construct the LVM utilities command line
500 if ! construct_lvm_cmdline ${host_name}; then
504 if [ -z "${LVM_CMDLINE}" ]; then
505 verbose_output "There are no LVM devices on host ${host_name}"\
506 "needed to be configured."
510 # Run remote command to configure LVM devices in $host_name
511 verbose_output "Configuring LVM devices in host ${host_name}..."
512 verbose_output "Configure command line is: \"${LVM_CMDLINE}\""
513 REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${LVM_CMDLINE}\""
514 ${REMOTE} ${host_name} "(${EXPORT_PATH} ${LVM_CMDLINE})" >&2 &
515 REMOTE_PID[pid_num]=$!
521 # Run remote command to configure all the LVM devices specified
525 declare -i idx=0 # Index of NODE_NAME array
529 # Initialize the NODE_NAME array
532 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
533 host_name=${HOST_NAME[i]}
534 configured_host ${host_name} && continue
536 NODE_NAME[idx]=${host_name}
539 # Run remote command to configure LVM devices in $host_name
540 if ! config_lvm_devs ${host_name}; then
545 if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then
546 verbose_output "There are no LVM devices to be configured."
550 # Wait for the exit status of the background remote command
551 verbose_output "Waiting for the return of the remote command..."
553 for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
554 wait ${REMOTE_PID[${pid_num}]}
555 if [ ${PIPESTATUS[0]} -ne 0 ]; then
556 echo >&2 "`basename $0`: config_lvm() error: Failed"\
557 "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
562 if ${failed_status}; then
566 verbose_output "All the LVM devices are configured successfully!"
572 if ! check_file $1; then
576 # Get the list of nodes to be operated on
577 NODES_TO_USE=$(get_nodelist)
578 [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1
580 # Check the node list
581 check_nodelist ${NODES_TO_USE} || exit 1
583 # Get all the LVM device items from the csv file
584 if ! get_lvm_items ${CSV_FILE}; then
588 # Configure the LVM devices
589 if ! config_lvm; then