diff options
Diffstat (limited to 'block-dmmd')
| -rw-r--r-- | block-dmmd | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/block-dmmd b/block-dmmd new file mode 100644 index 0000000..58534f8 --- /dev/null +++ b/block-dmmd @@ -0,0 +1,384 @@ +#! /bin/bash + +# Usage: block-dmmd [add args | remove args] +# +# the dmmd device syntax (in xl commands/configs) is something like: +# script=block-dmmd,md;/dev/md0;md;/dev/md1;lvm;/dev/vg1/lv1 +# or +# script=block-dmmd,lvm;/dev/vg1/lv1;lvm;/dev/vg1/lv2;md;/dev/md0 +# device pairs (type;dev) are processed in order, with the last device +# assigned to the VM +# +# Note - When using the libxl stack, the "script=block-dmmd" option +# is required. See man xl-disk-configuration(5) for more information. +# +# md devices can optionally: +# specify a config file through: +# md;/dev/md100(/var/xen/config/mdadm.conf) +# use an array name (mdadm -N option): +# md;My-MD-name;lvm;/dev/vg1/lv1 +# +# Completely expressive syntax should be similar to: +# "format=raw, vdev=xvdb, access=rw, script=block-dmmd, \ +# target=md;/dev/md0(/etc/mdadm.conf);lvm;/dev/vg1/lv1" +# +## +# History: +# 2017-07-10, mlatimer@suse.com: +# Modification to use syslog for progress messages by ldevulder@suse.com +# 2017-06-12, mlatimer@suse.com: +# Merge LVM improvements by loic.devulder@mpsa.com +# Document libxl "script=block-dmmd" syntax in examples +# Remove xm/xend references (e.g. parsed_timeout from xend-config.sxp) +# 2016-05-27, mlatimer@suse.com: +# Merge improvements by loic.devulder@mpsa.com. Highlights include: +# - Re-write and simplification to speed up the script! +# - Add some (useful) logging messages and comments +# Minor tweaks and logging improvements +# 2016-05-26, mlatimer@suse.com: +# Verify MD activation if mdadm returns 2 +# 2016-05-20, mlatimer@suse.com: +# Strip leading "dmmd:" if present in xenstore params value +# 2013-07-03, loic.devulder@mpsa.com: +# Partial rewrite of the script for supporting MD activation by name +# 2009-06-09, mh@novell.com: +# Emit debugging messages into a temporary file; if no longer needed, +# just comment the exec I/O redirection below +# Make variables used in functions local to avoid global overridings +# Use vgscan and vgchange where required +# Use the C locale to avoid dealing with localized messages +# Assign output from assembling an MD device to a variable to aid +# debugging + +# We do not want to deal with localized messages +# We use LC_ALL because LC_ALL superse LANG +# But we also use LANG because some applications may still use LANG... +export LC_ALL=C +export LANG=${LC_ALL} + +# Loading common libraries +. $(dirname $0)/block-common.sh + +# Constants +typeset -rx MDADM_BIN=/sbin/mdadm +typeset -rx LVCHANGE_BIN=/sbin/lvchange +typeset -rx PVSCAN_BIN=/sbin/pvscan +typeset -rx VGSCAN_BIN=/sbin/vgscan +typeset -rx VGCHANGE_BIN=/sbin/vgchange +typeset -rx CLVMD_BIN=/usr/sbin/clvmd +typeset -rx DATE_SEC="date +%s" + +# We check for errors ourselves +set +e + +function reload_clvm() +{ + # If we are in cluster mode + if ps -e | grep -q [c]lvmd 2>/dev/null; then + # Logging message + log info "Synchronizing cLVM..." + + # Synchronize cLVM + ${CLVMD_BIN} -R > /dev/null 2>&1 \ + || return 1 + fi + + return 0 +} + +function run_mdadm() +{ + local mdadm_cmd=$1 + local msg + local rc + + msg="$(${MDADM_BIN} ${mdadm_cmd} 2>&1)" + rc=$? + case "${msg}" in + *"has been started"* | *"already active"*) + return 0 + ;; + *"is already in use"*) + # Hmm, might be used by another device in this domU + # Leave it to upper layers to detect a real error + return 2 + ;; + *) + return ${rc} + ;; + esac + + # Normally we should not get here, but if this happens + # we have to return an error + return 1 +} + +function activate_md() +{ + # Make it explicitly local + local par=$1 + local cfg dev dev_path rc t mdadm_opts + + if [[ ${par} == ${par%%(*} ]]; then + # No configuration file specified + dev=${par} + cfg="" + else + dev=${par%%(*} + t=${par#*(} + cfg="-c ${t%%)*}" + fi + + # Looking for device name or aliase + if [[ ${dev:0:1} == / ]]; then + dev_path=${dev%/*} + mdadm_opts="" + else + dev_path=/dev/md + mdadm_opts="-s -N" + fi + + # Logging message + log info "Activating MD device ${dev}..." + + # Is MD device already active? + # We need to use full path name, aliase is not possible... + if [ -e ${dev_path}/${dev##*/} ]; then + ${MDADM_BIN} -Q -D ${dev_path}/${dev##*/} 2>/dev/null \ + | grep -iq state.*\:.*inactive || return 0 + fi + + # Activate MD device + run_mdadm "-A ${mdadm_opts} ${dev} ${cfg}" + rc=$? + + # A return code of 2 can indicate the array configuration was incorrect + if [[ ${rc} == 2 ]]; then + # Logging message + log info "Verifying MD device ${dev} activation..." + + # If the array is active, return 0, otherwise return an error + ${MDADM_BIN} -Q -D ${dev_path}/${dev##*/} &>/dev/null && return 0 \ + || return 1 + fi + + return ${rc} +} + +function deactivate_md() +{ + local par=$1 + local dev + + if [[ ${par} == ${par%%(*} ]]; then + # No configuration file specified + dev=${par} + else + dev=${par%%(*} + fi + + # Looking for device name or aliase + if [[ ${dev:0:1} == / ]]; then + dev_path=${dev%/*} + else + dev_path=/dev/md + fi + + # Logging message + log info "Deactivating MD device ${dev}..." + + # We need the device name only while deactivating + ${MDADM_BIN} -S ${dev_path}/${dev##*/} > /dev/null 2>&1 + + return $? +} + +function lvm_action() +{ + local action=$1 + local dev=$2 + local run_timeout=90 + local end_time + + # Logging message + log info "${action} LVM device ${dev}..." + + # Set end_time for the loop + (( end_time = $(${DATE_SEC}) + run_timeout )) + + while true; do + # Action depends of what the user asks + if [[ ${action} == activate ]]; then + # First scan for PVs and VGs + # We need this for using MD device as PV + ${PVSCAN_BIN} > /dev/null 2>&1 + + ${LVCHANGE_BIN} -aey ${dev} > /dev/null 2>&1 \ + && [[ -e ${dev} ]] \ + && return 0 + elif [[ ${action} == deactivate ]]; then + ${LVCHANGE_BIN} -aen ${dev} > /dev/null 2>&1 \ + && return 0 + + # If the LV is already deactivated we may be in an infinite loop + # So we need to test if the LV is still present + [[ -e ${dev} ]] || return 0 + fi + + # It seems that we had a problem during lvchange + # If we are in a cluster the problem may be due to a cLVM locking bug, + # so try to reload it + reload_clvm + + # If it takes too long we need to return an error + if (( $(${DATE_SEC}) >= end_time )); then + log err "Failed to ${action} $1 within ${run_timeout} seconds" + return 1 + fi + + # Briefly sleep before restarting the loop + sleep 0.1 + + done + + # Normally we should not get here, but if this happens + # we have to return an error + return 1 +} + +# Variables +typeset command=$1 +typeset BP=100 +typeset SP=${BP} +typeset VBD +typeset -a stack + +function push() +{ + local value="$1" + + [[ -n "${value}" ]] \ + && stack[$((--SP))]="${value}" + + return 0 +} + +function pop() +{ + [[ "${SP}" != "${BP}" ]] \ + && VBD=${stack[$((SP++))]} \ + || VBD="" + + return 0 +} + +function activate_dmmd() +{ + case "$1" in + "md") + activate_md $2 + return $? + ;; + "lvm") + lvm_action activate $2 + return $? + ;; + esac + + # Normally we should not get here, but if this happens + # we have to return an error + return 1 +} + +function deactivate_dmmd() +{ + case "$1" in + "md") + deactivate_md $2 + return $? + ;; + "lvm") + lvm_action deactivate $2 + return $? + ;; + esac + + # Normally we should not get here, but if this happens + # we have to return an error + return 1 +} + +function cleanup_stack() +{ + while true; do + pop + [[ -z "${VBD}" ]] && break + deactivate_dmmd ${VBD} + done +} + +function parse_par() +{ + # Make these vars explicitly local + local ac par rc s t + + ac=$1 + par="$2" + + par="${par};" + while true; do + t=${par%%;*} + + [[ -z "${t}" ]] && return 0 + par=${par#*;} + + s=${par%%;*} + [[ -z "${s}" ]] && return 1 + par=${par#*;} + + if [[ "${ac}" == "activate" ]]; then + activate_dmmd ${t} ${s} \ + || return 1 + fi + push "${t} ${s}" + done +} + +case "${command}" in + "add") + p=$(xenstore-read ${XENBUS_PATH}/params) || true + claim_lock "dmmd" + dmmd=${p#dmmd:} + + if ! parse_par activate "${dmmd}"; then + cleanup_stack + release_lock "dmmd" + exit 1 + fi + + lastparam=${dmmd##*;} + usedevice=${lastparam%(*} + xenstore-write ${XENBUS_PATH}/node "${usedevice}" + write_dev "${usedevice}" + release_lock "dmmd" + + exit 0 + ;; + + "remove") + p=$(xenstore-read ${XENBUS_PATH}/params) || true + claim_lock "dmmd" + dmmd=${p#dmmd:} + + parse_par noactivate "${dmmd}" + + cleanup_stack + release_lock "dmmd" + + exit 0 + ;; +esac + +# Normally we should not get here, but if this happens +# we have to return an error +return 1 |
