Re: How to automatically add a nbd to a raid1?

Michael Rendell <[email protected]> Fri, 19 Dec 2008 11:16:31 -0330
Newsgroups gmane.linux.enbd.general
Message-ID <[email protected]>
On Thursday 18 December 2008 20:03, Peter Breuer wrote:
...
> More obviously, the interaction between pwprog and the init script needs
> considerable thinking about.  At the moment you're disabling pwprog (as
> run by mdadm --monitor) while the init script runs.  The init script
> effectively runs for quite a while since it starts daemon scripts which
> hang around waiting for the other end of a connection to come up before
> starting the corresponding daemons.
>
> I'm a little concerned that there may be a multiplicity of waiting init
> script slave daemon scripts, and that they may compete for or confuse
> the status of the lockout file that lets mdadm start working, or simply
> compete with or confuse the pwprog script run by mdadm --monitor.


Yes - the disable/enable should be outside the while loop that
reads the config file; it can be moved to the top of the start/stop case:
  case $do_what in
  start|stop)
    if [ X"$to_what" = X -o X"$to_what" = Xclient ] && [ X"$to_which" = X ]
    then
      # Allow/prevent pwprog (run by mdmonitor) from acting.
      [ X"$do_what" = Xstop ] && enbd-pwprog disable
      [ X"$do_what" = Xstart ] && enbd-pwprog enable
    fi
    (
      if [ "$SERVER_CONFIG" != "$CLIENT_CONFIG" ]; then


>
> There's only one pwprog disable file. It will be removed by the init
> script running start, which allows pwprog to run thereafter.  It will
> be established by the init script running stop, which turns off
> the pwprog run from mdadm. The latter is clearly right. Is the start
> action right?
>
> If the init script can't start all its daemons at once, that's when it
> starts its watchers. Hopefully mdadm won't know about the missing enbd
> devices at this point, so it won't ever tell pwprog to try and recover
> enbd devices (which don't yet exist) into raid arrays. That saves
> pwprog from competing with the init script. But it leaves a hole ..
> the init script watchers will start the enbd connections. Then nothing
> .. mdadm won't act. One needs to use the udev notice or the init script
> itself to put the new enbd devices into raid arrays. After that one can
> leave everything to pwprog.
>
> So I don't see there's anything wrong with your approach.  Nevertheless,
> I feel bad about something ..  it seems to me that there ought to be one
> lockout file for pwprog per enbd device, not one lockout file full stop.
> But I don't know why I feel that.  Perhaps it'll come to me ..  tomorrow
> I have to be on a train.


Also felt that it could be done on a device by device level - means
the enable/disable would go back inside the while loop.  Can make
the needed changes to the pwprog and init scripts if you'd like.
If one wanted a "disable all" option for pwprog, it would mean pwprog
would have to parse the config file (straight forward, but means the
parsing is done in two places).


> ...

> It seems to me that you are basically looking for a mechanism to stop
> pwprog acting in opposition to an orderly shutdown procedure.
> 
> It's not a bad idea to have a control file .. but it feels clumsy.
> 
> On the plus side it is a control mechanism that can be used in all
> circumstances. 
> 
> On the minus side, the intended use is very init-sequence specific, so
> why go the file route?
> 
> One could simply shut md down before shutting enbd down.  Then there
> would be no interference from mdadm/pwprog when enbd shutdown goes on
> its killing spree...  not so?

It may be that not all md devices are using nd devices, so stopping them
(especially if the root file system is on one) may not be possible.
Could try stopping only those that use nd devices, but again, it may
be that the file system is needed (no reason why the root file system
couldn't be mirrored to another machine), so the best one could do is
fail/remove the nd partition, but that would trigger the pwprog...

As it happens, have written a script that does stop (or fail/remove) md
devices with the intent that it be run before the "init/enbd stop" script
when shutting down :-)  It does the "pwprog disable" / enable to avoid the
above problem.  Wasn't sure if this was generally useful (doesn't deal with
other potential uses of enbd devices, such as in lvm volumes), so didn't send
it before.  Makes sense to put the general disable/enable in this script,
but also makes sense to put individual device disable/enabling in the init
script (since it is designed to be able to start/stop individual devices).
Have included the script at the end.


> I would have alternatively suggested for consideration (not terribly
> seriously) that pwprog suicide when runlevel is going to 0 or 6!  It can
> ask /sbin/runlevel.  Or it can examine the init links directly to figure
> out if enbd is going up or down in this runlevel transition, in general.

Yes - this is possible; means pwprog would have to know the details
of the init system (something that seems to vary a bit: Debian style
vs Redhat style vs Gentoo style vs ...).  Is nice to have such things
confined to the init script itself!


Best wishes,
Michael

/etc/rc.d/init.d/ script to cleanly shutdown md devices before enbd clients
go away:

#!/bin/sh
#
# enbd-md	look for md (raid) devices using enbd devices and unmount
#		the file systems and stop the raids in preparation for
#		stopping the enbd service.
#
# chkconfig: 345 75 25
# description: Prepare for the shutting down of enbd (myTel).

# Source function library.
. /etc/rc.d/init.d/functions

prog=enbd-md

verbose()
{
    echo $0: "$@"
    :
}

logError()
{
    echo "$0:" "$@" 1>&2
    logger -p daemon.error -t "$prog" -- "$@"
}

findMDs()
{
    awk '/^md[0-9][0-9]* : (active|inactive) (raid[0-9]) / {
	    md = $1;
	    level = $4;
	    for (i = 5; i < NF; ++i) {
		dev = $i;
		sub(/\[.*/, "", dev);
		isFailed = $i ~ /\(F\)/ ? "failed" : "ok";
		if (dev ~ /^nd[a-p]$/) {
		    print md, level, isFailed, dev;
		}
	    }
	}' /proc/mdstat
}

umountFSOnDev()
{
    typeset dev=$1
    if grep "^[ 	]*$dev[ 	]" /proc/mounts > /dev/null; then
	typeset sig=TERM cnt=0
	for cnt in 0 1 2; do
	    verbose "umountFSOnDev: Attempting to umount $dev"
	    if umount $dev; then
		verbose "umountFSOnDev: umount $dev succeeded"
		return 0
	    fi
	    verbose "umountFSOnDev: trying to signal ($sig) processes using $dev"
	    /sbin/fuser -k -m -$sig $dev
	    sleep 4
	    sig=KILL
	done
	# failed to unmount
	return 1
    else
	verbose "umountFSOnDev: nothing mounted on $dev"
	# not mounted
	return 0
    fi
}

doStart()
{
    echo -n "Starting $prog: "
    touch /var/lock/subsys/$prog
    # Allow pwprog to work again
    enbd-pwprog enable
    echo
    return 0
}

doStop()
{
    typeset mds=`findMDs`
    typeset md level nd
    typeset anyErrors=0

    echo -n "Stopping $prog: "

    # Toast any running pwprog processes and prevent new ones
    enbd-pwprog disable

    set -- $mds
    while [ $# -gt 0 ]; do
	md=/dev/$1
	level=$2
	isFailed=$3
	nd=/dev/$4
	shift 4 || {
	    logError "doStop: internal error: invalid md list (n args: $*)"
	    exit 1
	}
	verbose "doStop: checking $level on $md which uses $nd"
	if [ X"$level" = Xraid1 ] ; then
	    if umountFSOnDev $md; then
		verbose "doStop: $md: attempting to stop raid"
		if mdadm --stop $md; then
		    verbose "doStop: $md: stop succeeded"
		    continue
		fi
		logError "doStop: $md: unable to stop raid"
	    fi
	    verbose "doStop: $md: attempting to fail $nd"
	    if [ X"$isFailed" != Xfailed ] && ! mdadm --fail $md $nd; then
		logError "doStop: $md: unable to mark $nd as failed"
		anyErrors=1
	    else
		verbose "doStop: $md: fail succeeded"
		sleep 2
	    fi
	    verbose "doStop: $md: attempting to remove $nd"
	    if ! mdadm --remove $md $nd; then
		logError "doStop: $md: unable to remove $nd"
		anyErrors=1
	    else
		verbose "doStop: $md: remove succeeded"
	    fi
	else
	    logError "doStop: can't handle level $level (for $md, $nd)"
	    anyErrors=1
	    continue
	fi
    done
    rm -f /var/lock/subsys/$prog
    echo
    return $anyErrors
}

exitCode=0
case "$1" in
  start)
	doStart
	exitCode=$?
        ;;
  stop)
	doStop
	exitCode=$?
        ;;
  *)
        echo "Usage: enbd-md {start|stop}"
        exitCode=1
esac

exit $exitCode