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 error_output "Missing csv file!"
171 # check_lvm_item index
173 # Check the items required for managing LVM device ${LVM_NAME[index]}
176 if [ $# -eq 0 ]; then
177 error_output "check_lvm_item():"\
185 if [ -z "${HOST_NAME[i]}" ]; then
186 error_output "check_lvm_item():"\
187 "hostname item has null value!"
191 # Check LVM device name
192 if [ -z "${LVM_NAME[i]}" ] \
193 && [ "${LINE_MARKER[i]}" != "${LV_MARKER}" -a "${OP_MODE[i]}" != "remove" ]
195 error_output "check_lvm_item():"\
196 "LVM component name item has null value!"
200 # Check the operation mode
201 if [ -n "${OP_MODE[i]}" ] \
202 && [ "${OP_MODE[i]}" != "create" -a "${OP_MODE[i]}" != "remove" ]
204 error_output "check_lvm_item():"\
205 "Invalid operation mode item - \"${OP_MODE[i]}\"!"
209 # Check items required by create mode
210 if [ -z "${OP_MODE[i]}" -o "${OP_MODE[i]}" = "create" ]; then
211 if [ "${LINE_MARKER[i]}" = "${VG_MARKER}" -a -z "${SIXTH_ITEM[i]}" ]
213 error_output "check_lvm_item():"\
214 "pv paths item of vg ${LVM_NAME[i]} has null value!"
218 if [ "${LINE_MARKER[i]}" = "${LV_MARKER}" ]; then
219 if [ -z "${SIXTH_ITEM[i]}" ]; then
220 error_output "check_lvm_item():"\
221 "lv size item has null value!"
225 if [ -z "${SEVENTH_ITEM[i]}" ]; then
226 error_output "check_lvm_item():"\
227 "vg name item has null value!"
236 # get_lvm_items csv_file
238 # Get all the LVM device items in the $csv_file and do some checks.
241 if [ $# -eq 0 ]; then
242 error_output "get_lvm_items(): Missing csv file!"
247 local LINE line_marker
249 declare -i line_num=0
252 while read -r LINE; do
255 # Skip the comment line
256 [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue
258 # Skip the non-LVM line
259 line_marker=$(echo ${LINE} | cut -d, -f 2)
260 [ "${line_marker}" != "${PV_MARKER}" ] \
261 && [ "${line_marker}" != "${VG_MARKER}" ] \
262 && [ "${line_marker}" != "${LV_MARKER}" ] && continue
264 # Skip the host which is not specified in the host list
265 if ! ${USE_ALLNODES}; then
266 hostname=$(echo ${LINE} | cut -d, -f 1)
267 ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue
270 # Parse the config line into CONFIG_ITEM
271 if ! parse_line "$LINE"; then
275 HOST_NAME[idx]=${CONFIG_ITEM[0]}
276 LINE_MARKER[idx]=${CONFIG_ITEM[1]}
277 LVM_NAME[idx]=${CONFIG_ITEM[2]}
278 OP_MODE[idx]=${CONFIG_ITEM[3]}
279 OP_OPTS[idx]=${CONFIG_ITEM[4]}
280 SIXTH_ITEM[idx]=${CONFIG_ITEM[5]}
281 SEVENTH_ITEM[idx]=${CONFIG_ITEM[6]}
283 # Check some required items
284 if ! check_lvm_item $idx; then
285 error_output "check_lvm_item():"\
286 "Occurred on line ${line_num} in ${CSV_FILE}."
296 # construct_lvm_create_cmdline index
298 # Construct the creation command line for ${LVM_NAME[index]}
299 construct_lvm_create_cmdline() {
303 case "${LINE_MARKER[i]}" in
305 lvm_cmd="pvcreate -ff -y ${OP_OPTS[i]} ${LVM_NAME[i]}"
308 lvm_cmd="vgcreate ${OP_OPTS[i]} ${LVM_NAME[i]} ${SIXTH_ITEM[i]}"
311 if [ -z "${LVM_NAME[i]}" ]; then
312 lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}"
314 lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} -n ${LVM_NAME[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}"
323 # cmdline_rm_LVs vg_name
325 # Construct command line to remove all the LVs on $vg_name.
326 # If $vg_name is null, then remove all the LVs in the host.
331 lvm_rm_cmd="vgchange -a n ${vg_name} &&"
332 lvm_rm_cmd=${lvm_rm_cmd}" vgdisplay -v ${vg_name} | grep \"LV Name\" | awk '{print \$3}' |"
333 lvm_rm_cmd=${lvm_rm_cmd}" while read lv; do lvremove -f \$lv; done"
339 # cmdline_rm_LV lv_path
341 # Construct command line to remove LV $lv_path
346 lvm_rm_cmd="lvchange -a n ${lv_path} && lvremove -f ${lv_path}"
352 # cmdline_rm_VG vg_name
354 # Construct command line to remove VG $vg_name
359 # Remove all the LVs on this VG
360 lvm_rm_cmd=$(cmdline_rm_LVs ${vg_name})
363 lvm_rm_cmd=${lvm_rm_cmd}" && vgremove ${vg_name}"
370 # Construct command line to remove all the VGs in the host
374 # Remove all the LVs in the host
375 lvm_rm_cmd=$(cmdline_rm_LVs)
377 # Remove all the VGs in the host
378 lvm_rm_cmd=${lvm_rm_cmd}" && vgdisplay | grep \"VG Name\" | awk '{print \$3}' |"
379 lvm_rm_cmd=${lvm_rm_cmd}" while read vg; do vgremove \$vg; done"
387 # Construct command line to remove all the PVs in the host
391 # Remove all the LVs and VGs in the host
392 lvm_rm_cmd=$(cmdline_rm_VGs)
394 # Remove all the PVs in the host
395 lvm_rm_cmd=${lvm_rm_cmd}" && pvdisplay | grep \"PV Name\" | awk '{print \$3}' |"
396 lvm_rm_cmd=${lvm_rm_cmd}" while read pv; do pvremove -ff -y \$pv; done"
402 # construct_lvm_teardown_cmdline index
404 # Construct the teardown command line for LVM devices in ${HOST_NAME[index]}
405 construct_lvm_teardown_cmdline() {
409 case "${LINE_MARKER[i]}" in
411 lvm_rm_cmd=$(cmdline_rm_LVs ${SEVENTH_ITEM[i]})
414 # Remove all the VGs in the host
415 lvm_rm_cmd=$(cmdline_rm_VGs)
418 # Remove all the PVs in the host
419 lvm_rm_cmd=$(cmdline_rm_PVs)
427 # construct_lvm_rm_cmdline index
429 # Construct the remove command line for LVM device ${LVM_NAME[index]}
430 construct_lvm_rm_cmdline() {
434 case "${LINE_MARKER[i]}" in
436 lvm_rm_cmd=$(cmdline_rm_LV ${LVM_NAME[i]})
439 lvm_rm_cmd=$(cmdline_rm_VG ${LVM_NAME[i]})
442 lvm_rm_cmd="pvremove -ff -y ${LVM_NAME[i]}"
450 # construct_lvm_cmdline host_name
452 # Construct the command line of LVM utilities to be run in the $host_name
453 construct_lvm_cmdline() {
459 # Construct command line
460 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
462 if [ "${host_name}" = "${HOST_NAME[i]}" ]; then
463 case "${OP_MODE[i]}" in
465 # Construct the create command line
466 lvm_cmd=$(construct_lvm_create_cmdline ${i})
469 if [ -z "${LVM_NAME[i]}" ]; then
470 # Construct the teardown command line
471 lvm_cmd=$(construct_lvm_teardown_cmdline ${i})
472 else # Remove instead of teardown
473 # Construct the remove command line
474 lvm_cmd=$(construct_lvm_rm_cmdline ${i})
478 error_output "construct_lvm_cmdline():"\
479 "Invalid operation mode - \"${OP_MODE[i]}\"!"
484 if [ -z "${LVM_CMDLINE}" ]; then
485 LVM_CMDLINE=${lvm_cmd}
487 LVM_CMDLINE=${LVM_CMDLINE}" && "${lvm_cmd}
495 # config_lvm_devs host_name
497 # Run remote command to configure LVM devices in $host_name
501 # Construct the LVM utilities command line
502 if ! construct_lvm_cmdline ${host_name}; then
506 if [ -z "${LVM_CMDLINE}" ]; then
507 verbose_output "There are no LVM devices on host ${host_name}"\
508 "needed to be configured."
512 # Run remote command to configure LVM devices in $host_name
513 verbose_output "Configuring LVM devices in host ${host_name}..."
514 verbose_output "Configure command line is: \"${LVM_CMDLINE}\""
515 REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${LVM_CMDLINE}\""
516 $REMOTE $host_name "export PATH=\$PATH:/sbin:/usr/sbin; $LVM_CMDLINE" &
517 REMOTE_PID[pid_num]=$!
523 # Run remote command to configure all the LVM devices specified
527 declare -i idx=0 # Index of NODE_NAME array
531 # Initialize the NODE_NAME array
534 for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do
535 host_name=${HOST_NAME[i]}
536 configured_host ${host_name} && continue
538 NODE_NAME[idx]=${host_name}
541 # Run remote command to configure LVM devices in $host_name
542 if ! config_lvm_devs ${host_name}; then
547 if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then
548 verbose_output "There are no LVM devices to be configured."
552 # Wait for the exit status of the background remote command
553 verbose_output "Waiting for the return of the remote command..."
555 for ((pid_num = 0; pid_num < ${#REMOTE_PID[@]}; pid_num++)); do
556 wait ${REMOTE_PID[${pid_num}]}
557 if [ ${PIPESTATUS[0]} -ne 0 ]; then
558 error_output "config_lvm(): Failed"\
559 "to execute \"${REMOTE_CMD[${pid_num}]}\"!"
564 if ${failed_status}; then
568 verbose_output "All the LVM devices are configured successfully!"
574 check_file $CSV_FILE || exit ${PIPESTATUS[0]}
576 # Get the list of nodes to be operated on
577 NODES_TO_USE=$(get_nodelist) || error_exit ${PIPESTATUS[0]} "$NODES_TO_USE"
579 # Check the node list
580 check_nodelist ${NODES_TO_USE} || exit 1
582 # Get all the LVM device items from the csv file
583 if ! get_lvm_items ${CSV_FILE}; then
587 # Configure the LVM devices
588 if ! config_lvm; then