#!/bin/sh -e

# Documentation: at the end, suitable for pod2man

# Configuration. All of this can be overridden from the environment.

## The default size for a new data block. Override with SIZE.
DEFSIZE=16G

## The default RAID level. Override with LEVEL.
DEFLEVEL=6

# don't change the next line
if test -z "$DATA" ; then

## The next three variables need to be unique, i.e. no other devices
## on your system can be named that way.

## the name of your large VG.
DATA=daten

## the name of the participating RAID volumes.
## Extended with three-digit numbers.
RAID=vg_

## the name of the volume groups for the participating disks.
## Extended however you like, typically with letters.
DISKVG=md_


# probably best not to change anything below this line


SIZE=${SIZE:-$DEFSIZE}
LEVEL=${LEVEL:-$DEFLEVEL}

elif test -z "$RAID" ; then
	echo >&2 "You need to set RAID to the name of the participating RAID volumes."
	exit 1
elif test -z "$DISKVG" ; then
	echo >&2 "You need to set DISKVG to the name of the VGs for the participating disk."
	exit 1
elif test -z "$SIZE" ; then
	echo >&2 "SIZE not set, using $DEFSIZE."
	SIZE=$DEFSIZE
elif test -z "$LEVEL" ; then
	echo >&2 "LEVEL not set, using $DEFLEVEL."
	LEVEL=$DEFLEVEL
fi

trap "echo 'ERROR'; exit 1" 0 1 2 15

if grep -qs resync /proc/mdstat ; then
	echo >&2 "Another RAID is syncing. Waiting."
	while grep -qs resync /proc/mdstat ; do sleep 10 ; done
fi
if test ! -b /dev/${RAID}000 ; then
	echo >&2 ""
	echo >&2 "# No staged RAID setup found. Do this to create one,"
	echo >&2 "# replacing /dev/sdXY with your destination partitions."
	mx=000
	for d in a b c d e f g h ; do
		echo >&2 "pvcreate /dev/sd${d}3"
		echo >&2 "vgcreate $DISKVG$d /dev/sd${d}3"
		lvcreate -L $SIZE -n v_$mx ${DISKVG}$d
	done
	echo >&2 "mdadm --create -l$LEVEL -n8 --name $RAID$mx -e1.1 --auto md /dev/$RAID$mx /dev/${DISKVG}*/v_$mx"
	echo >&2 "pvcreate /dev/$RAID$mx"
	echo >&2 "vgcreate $DATA /dev/$RAID$mx"
	if ! grep -qs auto=md /etc/udev/rules.d/85-mdadm.rules; then
		echo >&2 "# Verify that udev's mdadm call, somewhere in"
		echo >&2 "# /etc/udev/rules.d/, contains the '--auto=md argument."
		echo >&2 "# If not, add it, then run 'update-initramfs'."
	fi
	
	trap "" 0 1 2 15
	exit 1
fi

LOCK=/var/lock/new_vg
shlock $LOCK
trap "rm -f $LOCK; echo 'ERROR'; exit 1" 0 1 2 15

mx=$(cd /dev; ls -d ${RAID}*|sort -r|head -1|sed -e "s/^$RAID//" -e 's/^0*//')
if test -z "$mx" ; then
	echo >&2 "Oops, no staged RAID setup found after all?"
	exit 1
fi
mx=$(( $mx + 1 ))

if [ $mx -lt 10 ] ; then
	mx=00$mx
elif [ $mx -lt 100 ] ; then
	mx=0$mx
fi

for d in $(cd /dev; ls -d ${DISKVG}*) ; do
	lvcreate -L $SIZE -n v_$mx $d
done
mdadm --create -l$LEVEL -n$(ls /dev/${RAID}*/v_$mx | wc -l) --name $RAID$mx -e1.1 --auto md /dev/$RAID$mx /dev/${RAID}*/v_$mx
pvcreate -M 2 --metadatacopies 2 /dev/$RAID$mx

mdadm -D --brief /dev/$RAID$mx | sed -e 's/level=.* //' >> /etc/mdadm/mdadm.conf
#ARRAY /dev/md115 level=raid6 num-devices=8 name=vg_009 UUID=3d2da0cd:d07d948e:90736125:d38917c0
#ARRAY /dev/md115 UUID=3d2da0cd:d07d948e:90736125:d38917c0

vgextend $DATA /dev/$RAID$mx

trap "rm -f $LOCK; exit 0" 0 1 2 15
exit 0

cat <<'END' >/dev/null

=head1 SYNOPSIS

B<new_vg> -- extend a multi-RAIDed volume group

=head1 Structure

A multi-RAID volume group is a VG which consists of several small RAID
volumes, all of hich live on the same set of disks.

The purpose of this structure is to break up large disks into smaller chunks.
That way, read errors somewhere in one area of a disk do not drop the whole
disk from an array. This may be necessary because:

=over 4

=item

You're afraid that the subsequent re-sync will find errors on another
disk or two (depending on whether you use RAID5 or RAID6), thereby
obliterating your whole RAID setup.

Resyncing a whole disk takes too long, esp. if you can only do it at night,
and leaves your data at risk.

You know that your disks are imperfect, but you need cheap reliable
backup storage.

=back

As disks are assumed to be unreliable, RAID6 is definitely recommended,
as no other scheme supports recovery when any two random disks fail.

=head2 Layout

Each of your disks will contain a unique volume group with a number of
logical volumes on it.

Each of these logical volumes, with its siblings on the other disks, is
part of a small(ish) RAID6.

These small RAID6 volumes are part of a large volume group.

You allocate partitions from this large volume group for your data.

If there's a disk error, hopefully only one of those small partitions
will fail. If that happens, allocate a new logical volume on that disk,
and add it to the degraded RAID6.

Yes, this does mean that F</proc/mdstat> will become rather large.
Deal with it.

=head2 System start-up

Modern Linux systems should re-assemble this structure automagically;
you can even store the root file system on it.
(But not the boot partition. Use a separate RAID1 for that, if necessary.)

The RAID device names used when creating this structure are usually ignored
afetr rebooting. Add '--auto=md' to C<udev>'s C<mdadm> command if you'd
like to fix that. (It's not strictly necessary, however.) The command
is typically stored in F</etc/udev/rules.d/85-mdadm.rules>; after you
change that file, you need to run C<update-initramfs>.

=head1 USAGE

This script extends an existing multi-RAID volume group with another
block of data.

If such a volume group doesn't yet exist, it emits template statements
for creating them.

It is not called with any arguments. If necessary, you can override
some of its attributes using environment variables.

=over 4

=item SIZE

The size of a new RAID6 block. The default is 16 GBytes; assuming RAID6 and
eight disks, this will extend your volume group by almost 100 GBytes.

=item LEVEL

The RAID level. 6 is definitely recommended. 5 might work if you're very
confident that your disks are good. 0 works if you're afraid that any of
your disks might fail at any time.

=item DATA

The name of your large volume group. This is the name of
that volume group that's extended when you call this script.

=item RAID

The initial part of the device name of the participating RAID volumes.
A three-digit number is added to this name.

The default is C<vg_>; you should keep the underscore for clarity.

=item DISKVG

The name of the volume groups for the participating disks.
These VGs are allocated when you create this setup, so the complete
name is whatever you like. It's recommended B<not> to use drive letters
because the system may decide to re-order them when you reboot.

The default is C<md_>; you should keep the underscore for clarity.

=head1 SEE ALSO

L<mdadm(8)>, L<lvm(8)>.

=back

=cut
END

