385 lines
9.3 KiB
Bash
385 lines
9.3 KiB
Bash
#! /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
|