File block-dmmd of Package xen.15298

#! /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
openSUSE Build Service is sponsored by