#!/bin/sh . /usr/share/debconf/confmodule # Translate device from /proc/mdstat (mdX) to device node md_devnode() { local num=$(echo $1 | sed -e "s/^md//") # Also handle the case where the first does not exist if [ -b /dev/md/$num ]; then echo /dev/md/$num elif [ -b /dev/md$num ]; then echo /dev/md$num else return 1 fi } md_get_level() { echo $(mdadm -Q --detail $1 | grep "Raid Level" | sed "s/.*: //") } md_get_devices() { DEVICES="" for DEVICE in $(grep ^md /proc/mdstat | \ sed -e 's/^\(md.*\) : .*/\1/'); do MDDEV=$(md_devnode $DEVICE) || return 1 TYPE=$(md_get_level $MDDEV) DEVICES="${DEVICES:+$DEVICES, }${DEVICE}_$TYPE" done } md_delete_verify() { DEVICE=$(echo "$1" | sed -e "s/^\(md.*\)_.*/\1/") DEVICES=$(grep "^$DEVICE[ :]" /proc/mdstat | \ sed -e "s/^.*active \(.*\)/\1/; s/raid[0-9]* //") MDDEV=$(md_devnode $DEVICE) || return 1 TYPE=$(md_get_level $MDDEV) db_set mdcfg/deleteverify false db_subst mdcfg/deleteverify DEVICE "/dev/$DEVICE" db_subst mdcfg/deleteverify TYPE "$TYPE" db_subst mdcfg/deleteverify DEVICES "$DEVICES" db_input critical mdcfg/deleteverify db_go db_get mdcfg/deleteverify case $RET in true) # Stop the MD device and zero the superblock # of all the component devices DEVICES=$(mdadm -Q --detail $MDDEV | \ grep -E "^[[:space:]]*[0-9].*(active|spare)" | \ sed -e 's/.* //') logger -t mdcfg "Removing $MDDEV ($DEVICES)" log-output -t mdcfg mdadm --stop $MDDEV || return 1 for DEV in "$DEVICES"; do log-output -t mdcfg \ mdadm --zero-superblock --force $DEV || return 1 done ;; esac return 0 } md_delete() { md_get_devices if [ -z "$DEVICES" ]; then db_set mdcfg/delete_no_md false db_input high mdcfg/delete_no_md db_go return fi db_set mdcfg/deletemenu false db_subst mdcfg/deletemenu DEVICES "$DEVICES" db_input critical mdcfg/deletemenu db_go db_get mdcfg/deletemenu case $RET in md*) if ! md_delete_verify $RET; then db_input critical mdcfg/deletefailed db_go fi ;; esac } md_createmain() { db_set mdcfg/createmain false db_input critical mdcfg/createmain db_go if [ $? -ne 30 ]; then db_get mdcfg/createmain if [ "$RET" = Cancel ]; then return fi RAID_SEL="$RET" if ! get_partitions; then return fi case "$RAID_SEL" in RAID10|RAID6|RAID5|RAID1) md_create_array "$RAID_SEL" ;; RAID0) md_create_raid0 ;; *) return 1 ;; esac fi } # This will set PARTITIONS and NUM_PART global variables get_partitions() { PARTITIONS="" # Get a list of RAID partitions. This only works if there is no # filesystem on the partitions, which is fine by us. RAW_PARTITIONS=$(/usr/lib/partconf/find-partitions --ignore-fstype 2>/dev/null | \ grep "[[:space:]]RAID[[:space:]]" | cut -f1) # Convert it into a proper list form for a select question # (comma separated) NUM_PART=0 for i in $RAW_PARTITIONS; do DEV=$(echo $i | sed -e "s/\/dev\///") REALDEV=$(mapdevfs "$i") MAPPEDDEV=$(echo "$REALDEV" | sed -e "s/\/dev\///") if grep -Eq "($DEV|$MAPPEDDEV)" /proc/mdstat; then continue fi PARTITIONS="${PARTITIONS:+$PARTITIONS, }$REALDEV" NUM_PART=$(($NUM_PART + 1)) done if [ -z "$PARTITIONS" ]; then db_input critical mdcfg/noparts db_go return 1 fi return 0 } prune_partitions() { CHOSEN="$1" OLDIFS="$IFS" IFS=, NEW_PARTITIONS="" for i in $PARTITIONS; do found=0 for j in $CHOSEN; do if [ "$i" = "$j" ]; then found=1 fi done if [ $found -eq 0 ]; then NEW_PARTITIONS="${NEW_PARTITIONS:+$NEW_PARTITIONS,}$i" fi done IFS=$OLDIFS PARTITIONS=$NEW_PARTITIONS } md_create_raid0() { db_subst mdcfg/raid0devs PARTITIONS "$PARTITIONS" db_set mdcfg/raid0devs "" db_input critical mdcfg/raid0devs db_go if [ $? -eq 30 ]; then return; fi db_get mdcfg/raid0devs SELECTED=0 for i in $RET; do let SELECTED++ done prune_partitions "$RET" MD_NUM=$(grep ^md /proc/mdstat | \ sed -e 's/^md\(.*\) : active .*/\1/' | sort | tail -n1) if [ -z "$MD_NUM" ]; then MD_NUM=0 else let MD_NUM++ fi logger -t mdcfg "Number of devices in the RAID0 array md$MD_NUM: $SELECTED" RAID_DEVICES="$(echo $RET | sed -e 's/,//g')" log-output -t mdcfg \ mdadm --create /dev/md$MD_NUM --auto=yes --force -R -l raid0 \ -n $SELECTED $RAID_DEVICES } md_create_array(){ OK=0 case "$1" in RAID1) MIN_SIZE=2 ;; RAID5) MIN_SIZE=3 ;; RAID6) MIN_SIZE=4 ;; RAID10) MIN_SIZE=2 ;; *) return ;; esac LEVEL=${1#RAID} for i in devcount devs sparecount sparedevs; do db_subst mdcfg/raid$i LEVEL "$LEVEL" done db_subst mdcfg/raiddevcount MINIMUM "$MIN_SIZE" db_set mdcfg/raiddevcount "$MIN_SIZE" # Get the count of active devices while [ $OK -eq 0 ]; do db_input critical mdcfg/raiddevcount db_go if [ $? -eq 30 ]; then return fi # Figure out, if the user entered a number db_get mdcfg/raiddevcount RET=$(echo $RET | sed -e "s/[[:space:]]//g") if [ "$RET" ]; then let "OK=${RET}>0 && ${RET}<99" fi done db_set mdcfg/raidsparecount "0" OK=0 # Same procedure as above, but get the number of spare partitions # this time. # TODO: Make a general function for this kind of stuff while [ $OK -eq 0 ]; do db_input critical mdcfg/raidsparecount db_go if [ $? -eq 30 ]; then return fi db_get mdcfg/raidsparecount RET=$(echo $RET | sed -e "s/[[:space:]]//g") if [ "$RET" ]; then let "OK=${RET}>=0 && ${RET}<99" fi done db_get mdcfg/raiddevcount DEV_COUNT="$RET" if [ $LEVEL -ne 1 ]; then if [ $DEV_COUNT -lt $MIN_SIZE ]; then DEV_COUNT=$MIN_SIZE # Minimum number for the selected RAID level fi fi db_get mdcfg/raidsparecount SPARE_COUNT="$RET" REQUIRED=$(($DEV_COUNT + $SPARE_COUNT)) if [ $LEVEL -ne 1 ]; then if [ $REQUIRED -gt $NUM_PART ]; then db_subst mdcfg/notenoughparts NUM_PART "$NUM_PART" db_subst mdcfg/notenoughparts REQUIRED "$REQUIRED" db_input critical mdcfg/notenoughparts db_go mdcfg/notenoughparts return fi fi db_set mdcfg/raiddevs "" SELECTED=0 # Loop until the correct number of active devices has been selected for RAID 5, 6, and 10 # Loop until at least one device has been selected for RAID 1 until ([ $LEVEL -ne 1 ] && [ $SELECTED -eq $DEV_COUNT ]) || \ ([ $LEVEL -eq 1 ] && [ $SELECTED -gt 0 ] && [ $SELECTED -le $DEV_COUNT ]); do db_subst mdcfg/raiddevs COUNT "$DEV_COUNT" db_subst mdcfg/raiddevs PARTITIONS "$PARTITIONS" db_input critical mdcfg/raiddevs db_go if [ $? -eq 30 ]; then return fi db_get mdcfg/raiddevs SELECTED=0 for i in $RET; do DEVICE=$(echo $i | sed -e "s/,//") let SELECTED++ done done MISSING_DEVICES="" if [ $LEVEL -eq 1 ]; then # Add "missing" for as many devices as weren't selected while [ $SELECTED -lt $DEV_COUNT ]; do MISSING_DEVICES="$MISSING_DEVICES missing" let SELECTED++ done fi # Remove partitions selected in raiddevs from the PARTITION list db_get mdcfg/raiddevs prune_partitions "$RET" db_set mdcfg/raidsparedevs "" SELECTED=0 if [ $SPARE_COUNT -gt 0 ] && [ -n "$PARTITIONS" ]; then FIRST=1 # Loop until the correct number of devices has been selected. # That means any number less than or equal to the spare count. while [ $SELECTED -gt $SPARE_COUNT ] || [ $FIRST -eq 1 ]; do FIRST=0 db_subst mdcfg/raidsparedevs COUNT "$SPARE_COUNT" db_subst mdcfg/raidsparedevs PARTITIONS "$PARTITIONS" db_input critical mdcfg/raidsparedevs db_go if [ $? -eq 30 ]; then return fi db_get mdcfg/raidsparedevs SELECTED=0 for i in $RET; do DEVICE=$(echo $i | sed -e "s/,//") let SELECTED++ done done fi LAYOUT="" if [ $LEVEL -eq 10 ]; then OK=0 db_set mdcfg/raid10layout "n2" while [ $OK -eq 0 ]; do db_input low mdcfg/raid10layout db_go if [ $? -eq 30 ]; then return; fi db_get mdcfg/raid10layout if echo $RET | grep -Eq "^[nfo][0-9]{1,2}$" && \ [ $(echo $RET | sed s/.//) -le $DEV_COUNT ]; then OK=1 fi done LAYOUT="--layout=$RET" fi # The number of spares the user has selected NAMED_SPARES=$SELECTED db_get mdcfg/raiddevs RAID_DEVICES=$(echo $RET | sed -e "s/,//g") db_get mdcfg/raidsparedevs SPARE_DEVICES=$(echo $RET | sed -e "s/,//g") MISSING_SPARES="" COUNT=$NAMED_SPARES while [ $COUNT -lt $SPARE_COUNT ]; do MISSING_SPARES="$MISSING_SPARES missing" let COUNT++ done # Find the next available md-number MD_NUM=$(grep ^md /proc/mdstat | \ sed -e 's/^md\(.*\) : active .*/\1/' | sort | tail -n1) if [ -z "$MD_NUM" ]; then MD_NUM=0 else let MD_NUM++ fi logger -t mdcfg "Selected spare count: $NAMED_SPARES" logger -t mdcfg "Raid devices count: $DEV_COUNT" logger -t mdcfg "Spare devices count: $SPARE_COUNT" log-output -t mdcfg \ mdadm --create /dev/md$MD_NUM --auto=yes --force -R -l raid${LEVEL} $LAYOUT \ -n $DEV_COUNT -x $SPARE_COUNT $RAID_DEVICES \ $MISSING_DEVICES $SPARE_DEVICES $MISSING_SPARES } md_mainmenu() { while true; do db_set mdcfg/mainmenu false db_input critical mdcfg/mainmenu db_go if [ $? -eq 30 ]; then exit 30 fi db_get mdcfg/mainmenu case $RET in "Create MD device") md_createmain ;; "Delete MD device") md_delete ;; "Finish") break ;; esac done } ### Main of script ### # Load the modules and scan for MD devices if needed if ! [ -e /proc/mdstat ] || ! [ -d /dev/md ]; then # Try to load the necesarry modules. depmod -a >/dev/null 2>&1 modprobe md-mod >/dev/null 2>&1 # Make sure that we have md-support if [ ! -e /proc/mdstat ]; then db_set mdcfg/nomd false db_input high mdcfg/nomd db_go exit 0 fi # Speed up installation by de-priorizing (not stopping) array resync if [ -w /proc/sys/dev/raid/speed_limit_min ]; then echo 0 > /proc/sys/dev/raid/speed_limit_min fi # Try to detect MD devices, and start them # mdadm will fail if /dev/md does not already exist mkdir -p /dev/md log-output -t mdcfg --pass-stdout \ mdadm --examine --scan --config=partitions >/tmp/mdadm.conf log-output -t mdcfg \ mdadm --assemble --scan --run \ --config=/tmp/mdadm.conf --auto=yes fi # Force mdadm to be installed on the target system apt-install mdadm # We want the "go back" button #db_capb backup md_mainmenu exit 0