#!/bin/bash # # vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4: # # lc_lvm - configure Linux LVM devices from a csv file # ################################################################################ # Usage usage() { cat >&2 < This script is used to configure Linux LVM 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 LVM component (PV, VG, LV) to be configured in a Lustre cluster EOF exit 1 } # Samples sample() { cat <&2 "`basename $0`: Missing csv file!" usage fi # check_lvm_item index # # Check the items required for managing LVM device ${LVM_NAME[index]} check_lvm_item() { # Check argument if [ $# -eq 0 ]; then echo >&2 "`basename $0`: check_lvm_item() error:"\ "Missing argument!" return 1 fi declare -i i=$1 # Check hostname if [ -z "${HOST_NAME[i]}" ]; then echo >&2 "`basename $0`: check_lvm_item() error:"\ "hostname item has null value!" return 1 fi # Check LVM device name if [ -z "${LVM_NAME[i]}" ] \ && [ "${LINE_MARKER[i]}" != "${LV_MARKER}" -a "${OP_MODE[i]}" != "remove" ] then echo >&2 "`basename $0`: check_lvm_item() error:"\ "LVM component name item has null value!" return 1 fi # Check the operation mode if [ -n "${OP_MODE[i]}" ] \ && [ "${OP_MODE[i]}" != "create" -a "${OP_MODE[i]}" != "remove" ] then echo >&2 "`basename $0`: check_lvm_item() error:"\ "Invalid operation mode item - \"${OP_MODE[i]}\"!" return 1 fi # Check items required by create mode if [ -z "${OP_MODE[i]}" -o "${OP_MODE[i]}" = "create" ]; then if [ "${LINE_MARKER[i]}" = "${VG_MARKER}" -a -z "${SIXTH_ITEM[i]}" ] then echo >&2 "`basename $0`: check_lvm_item() error:"\ "pv paths item of vg ${LVM_NAME[i]} has null value!" return 1 fi if [ "${LINE_MARKER[i]}" = "${LV_MARKER}" ]; then if [ -z "${SIXTH_ITEM[i]}" ]; then echo >&2 "`basename $0`: check_lvm_item() error:"\ "lv size item has null value!" return 1 fi if [ -z "${SEVENTH_ITEM[i]}" ]; then echo >&2 "`basename $0`: check_lvm_item() error:"\ "vg name item has null value!" return 1 fi fi fi return 0 } # get_lvm_items csv_file # # Get all the LVM device items in the $csv_file and do some checks. get_lvm_items() { # Check argument if [ $# -eq 0 ]; then echo >&2 "`basename $0`: get_lvm_items() error: Missing csv file!" return 1 fi CSV_FILE=$1 local LINE line_marker local hostname declare -i line_num=0 declare -i idx=0 while read -r LINE; do let "line_num += 1" # Skip the comment line [ -z "`echo \"${LINE}\" | egrep -v \"([[:space:]]|^)#\"`" ] && continue # Skip the non-LVM line line_marker=$(echo ${LINE} | cut -d, -f 2) [ "${line_marker}" != "${PV_MARKER}" ] \ && [ "${line_marker}" != "${VG_MARKER}" ] \ && [ "${line_marker}" != "${LV_MARKER}" ] && continue # Skip the host which is not specified in the host list if ! ${USE_ALLNODES}; then hostname=$(echo ${LINE} | cut -d, -f 1) ! host_in_hostlist ${hostname} ${NODES_TO_USE} && continue fi # Parse the config line into CONFIG_ITEM if ! parse_line "$LINE"; then return 1 fi HOST_NAME[idx]=${CONFIG_ITEM[0]} LINE_MARKER[idx]=${CONFIG_ITEM[1]} LVM_NAME[idx]=${CONFIG_ITEM[2]} OP_MODE[idx]=${CONFIG_ITEM[3]} OP_OPTS[idx]=${CONFIG_ITEM[4]} SIXTH_ITEM[idx]=${CONFIG_ITEM[5]} SEVENTH_ITEM[idx]=${CONFIG_ITEM[6]} # Check some required items if ! check_lvm_item $idx; then echo >&2 "`basename $0`: check_lvm_item() error:"\ "Occurred on line ${line_num} in ${CSV_FILE}." return 1 fi let "idx += 1" done < ${CSV_FILE} return 0 } # construct_lvm_create_cmdline index # # Construct the creation command line for ${LVM_NAME[index]} construct_lvm_create_cmdline() { declare -i i=$1 local lvm_cmd case "${LINE_MARKER[i]}" in "${PV_MARKER}") lvm_cmd="pvcreate -ff -y ${OP_OPTS[i]} ${LVM_NAME[i]}" ;; "${VG_MARKER}") lvm_cmd="vgcreate ${OP_OPTS[i]} ${LVM_NAME[i]} ${SIXTH_ITEM[i]}" ;; "${LV_MARKER}") if [ -z "${LVM_NAME[i]}" ]; then lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}" else lvm_cmd="lvcreate -L ${SIXTH_ITEM[i]} -n ${LVM_NAME[i]} ${OP_OPTS[i]} ${SEVENTH_ITEM[i]}" fi ;; esac echo ${lvm_cmd} return 0 } # cmdline_rm_LVs vg_name # # Construct command line to remove all the LVs on $vg_name. # If $vg_name is null, then remove all the LVs in the host. cmdline_rm_LVs() { local vg_name=$1 local lvm_rm_cmd lvm_rm_cmd="vgchange -a n ${vg_name} &&" lvm_rm_cmd=${lvm_rm_cmd}" vgdisplay -v ${vg_name} | grep \"LV Name\" | awk '{print \$3}' |" lvm_rm_cmd=${lvm_rm_cmd}" while read lv; do lvremove -f \$lv; done" echo ${lvm_rm_cmd} return 0 } # cmdline_rm_LV lv_path # # Construct command line to remove LV $lv_path cmdline_rm_LV() { local lv_path=$1 local lvm_rm_cmd lvm_rm_cmd="lvchange -a n ${lv_path} && lvremove -f ${lv_path}" echo ${lvm_rm_cmd} return 0 } # cmdline_rm_VG vg_name # # Construct command line to remove VG $vg_name cmdline_rm_VG() { local vg_name=$1 local lvm_rm_cmd # Remove all the LVs on this VG lvm_rm_cmd=$(cmdline_rm_LVs ${vg_name}) # Remove this VG lvm_rm_cmd=${lvm_rm_cmd}" && vgremove ${vg_name}" echo ${lvm_rm_cmd} return 0 } # cmdline_rm_VGs # # Construct command line to remove all the VGs in the host cmdline_rm_VGs() { local lvm_rm_cmd # Remove all the LVs in the host lvm_rm_cmd=$(cmdline_rm_LVs) # Remove all the VGs in the host lvm_rm_cmd=${lvm_rm_cmd}" && vgdisplay | grep \"VG Name\" | awk '{print \$3}' |" lvm_rm_cmd=${lvm_rm_cmd}" while read vg; do vgremove \$vg; done" echo ${lvm_rm_cmd} return 0 } # cmdline_rm_PVs # # Construct command line to remove all the PVs in the host cmdline_rm_PVs() { local lvm_rm_cmd # Remove all the LVs and VGs in the host lvm_rm_cmd=$(cmdline_rm_VGs) # Remove all the PVs in the host lvm_rm_cmd=${lvm_rm_cmd}" && pvdisplay | grep \"PV Name\" | awk '{print \$3}' |" lvm_rm_cmd=${lvm_rm_cmd}" while read pv; do pvremove -ff -y \$pv; done" echo ${lvm_rm_cmd} return 0 } # construct_lvm_teardown_cmdline index # # Construct the teardown command line for LVM devices in ${HOST_NAME[index]} construct_lvm_teardown_cmdline() { declare -i i=$1 local lvm_rm_cmd case "${LINE_MARKER[i]}" in "${LV_MARKER}") lvm_rm_cmd=$(cmdline_rm_LVs ${SEVENTH_ITEM[i]}) ;; "${VG_MARKER}") # Remove all the VGs in the host lvm_rm_cmd=$(cmdline_rm_VGs) ;; "${PV_MARKER}") # Remove all the PVs in the host lvm_rm_cmd=$(cmdline_rm_PVs) ;; esac echo ${lvm_rm_cmd} return 0 } # construct_lvm_rm_cmdline index # # Construct the remove command line for LVM device ${LVM_NAME[index]} construct_lvm_rm_cmdline() { declare -i i=$1 local lvm_rm_cmd case "${LINE_MARKER[i]}" in "${LV_MARKER}") lvm_rm_cmd=$(cmdline_rm_LV ${LVM_NAME[i]}) ;; "${VG_MARKER}") lvm_rm_cmd=$(cmdline_rm_VG ${LVM_NAME[i]}) ;; "${PV_MARKER}") lvm_rm_cmd="pvremove -ff -y ${LVM_NAME[i]}" ;; esac echo ${lvm_rm_cmd} return 0 } # construct_lvm_cmdline host_name # # Construct the command line of LVM utilities to be run in the $host_name construct_lvm_cmdline() { LVM_CMDLINE= local host_name=$1 local lvm_cmd declare -i i # Construct command line for ((i = 0; i < ${#HOST_NAME[@]}; i++)); do lvm_cmd= if [ "${host_name}" = "${HOST_NAME[i]}" ]; then case "${OP_MODE[i]}" in "" | create) # Construct the create command line lvm_cmd=$(construct_lvm_create_cmdline ${i}) ;; remove) if [ -z "${LVM_NAME[i]}" ]; then # Construct the teardown command line lvm_cmd=$(construct_lvm_teardown_cmdline ${i}) else # Remove instead of teardown # Construct the remove command line lvm_cmd=$(construct_lvm_rm_cmdline ${i}) fi ;; *) echo >&2 "`basename $0`: construct_lvm_cmdline() error:"\ "Invalid operation mode - \"${OP_MODE[i]}\"!" return 1 ;; esac if [ -z "${LVM_CMDLINE}" ]; then LVM_CMDLINE=${lvm_cmd} else LVM_CMDLINE=${LVM_CMDLINE}" && "${lvm_cmd} fi fi done return 0 } # config_lvm_devs host_name # # Run remote command to configure LVM devices in $host_name config_lvm_devs() { local host_name=$1 # Construct the LVM utilities command line if ! construct_lvm_cmdline ${host_name}; then return 1 fi if [ -z "${LVM_CMDLINE}" ]; then verbose_output "There are no LVM devices on host ${host_name}"\ "needed to be configured." return 0 fi # Run remote command to configure LVM devices in $host_name verbose_output "Configuring LVM devices in host ${host_name}..." verbose_output "Configure command line is: \"${LVM_CMDLINE}\"" REMOTE_CMD[pid_num]="${REMOTE} ${host_name} \"${LVM_CMDLINE}\"" ${REMOTE} ${host_name} "(${EXPORT_PATH} ${LVM_CMDLINE})" >&2 & REMOTE_PID[pid_num]=$! let "pid_num += 1" return 0 } # Run remote command to configure all the LVM devices specified # in the csv file config_lvm() { 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 LVM devices in $host_name if ! config_lvm_devs ${host_name}; then return 1 fi done if [ ${#HOST_NAME[@]} -eq 0 -o ${#REMOTE_PID[@]} -eq 0 ]; then verbose_output "There are no LVM 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 echo >&2 "`basename $0`: config_lvm() error: Failed"\ "to execute \"${REMOTE_CMD[${pid_num}]}\"!" failed_status=true fi done if ${failed_status}; then return 1 fi verbose_output "All the LVM devices are configured successfully!" return 0 } # Main flow # Check the csv file if ! check_file $1; then exit 1 fi # Get the list of nodes to be operated on NODES_TO_USE=$(get_nodelist) [ ${PIPESTATUS[0]} -ne 0 ] && echo >&2 "${NODES_TO_USE}" && exit 1 # Check the node list check_nodelist ${NODES_TO_USE} || exit 1 # Get all the LVM device items from the csv file if ! get_lvm_items ${CSV_FILE}; then exit 1 fi # Configure the LVM devices if ! config_lvm; then exit 1 fi exit 0