#!/bin/sh . /usr/share/debconf/confmodule # # convert the return values from "xx (yy)" => "xx" # convert_return() { local IFS="," RET="" for part in $1; do part=${part%% *} if [ -z "$RET" ]; then RET="$part" else RET="$RET, $part" fi done } # # Convert common terms for disk sizes into something LVM understands. # e.g. "200 Gb" -> "200G" # human2lvm() { echo "$1" | tr -d " " | sed -e 's/\([kmgtKMGT]\)[bB]/\1/' } # # return extra informations (like size, current lv) for the # volume group # addinfos_pv() { cmdout=`pvdisplay "$1" 2>&1` echo "$cmdout" | grep -q 'is a new physical volume' if [ $? -eq 0 ]; then RET2=`echo "$cmdout" | sed -e 's/.*of \(.*\)$/Size: \1/'` RET="${RET2}" return fi echo "$cmdout" | grep -q '^pvdisplay' if [ $? -eq 0 ]; then RET="unknown" return fi RET="" VG=`echo "$cmdout" | grep '^[ ]*VG Name' | \ sed -e 's/^[ ]*VG Name \+//'` [ "$VG" != "" ] && RET="VG: ${VG}/ " RET2=`echo "$cmdout" | grep '^[ ]*PV Size' | \ sed -e 's/^[ ]*PV Size \+\(.*\)/Size: \1/' | \ cut -d "[" -f 1 | cut -d "/" -f 1 | sed -e 's/ $//'` RET="${RET}${RET2}" } # # return the free space of a volume group # getfree_vg() { cmdout=`vgdisplay "$1" 2>&1` echo "$cmdout" | grep '^[ ]*Free PE' | sed -e 's,^.*/ ,,' | sed -e 's/ \+$//' } # # return the free space of a volume group (in physical extents [PE]) # getfreepe_vg() { cmdout=`vgdisplay "$1" 2>&1` echo "$cmdout" | grep '^[ ]*Free PE' | sed -e 's/^.*Size \+//' | sed -e 's/ \+.*//' } # # return the size of a volume group # getsize_vg() { cmdout=`vgdisplay "$1" 2>&1` echo "$cmdout" | grep '^[ ]*VG Size' | sed -e 's/^[ ]*VG Size \+//' } # # return extra informations (like size, current lv) for the # volume group # addinfos_vg() { cmdout=`vgdisplay "$1" 2>&1` RET2="Free: `getfree_vg "$1"`" RET="${RET2}" RET2="Size: `getsize_vg "$1"`" RET="${RET}/ ${RET2}" RET2=`echo "$cmdout" | grep '^[ ]*Cur LV' | \ sed -e 's/^[ ]*Cur LV \+\(.*\)/LVs: \1/'` RET="${RET}/ ${RET2}" RET2=`echo "$cmdout" | grep '^[ ]*Cur PV' | \ sed -e 's/^[ ]*Cur PV \+\(.*\)/PVs: \1/'` RET="${RET}/ ${RET2}" } # # return extra informations (like size, fs type) for the # logical volume # addinfos_lv() { cmdout=`lvdisplay "$1" 2>&1` RET2=`echo "$cmdout" | grep '^[ ]*LV Size' | \ sed -e 's/^[ ]*LV Size \+\(.*\)/Size: \1/'` RET="${RET2}" cmdout=`parted $1 print | grep '^1' | \ sed -e 's/ \+/ /g' | cut -d " " -f 4` [ -z "$cmdout" ] && cmdout="unknown" RET="${RET}/ FS: ${cmdout}" cmdout=`grep "^$1" /proc/mounts | cut -d " " -f2 | \ sed -e 's,/target,,'` [ -z "$cmdout" ] && cmdout="n/a" RET="${RET}/ Mount: ${cmdout}" } # get all available physical volumes enum_pvs() { # Use partconf's find-partitions to find partitions # marked as lvm volumes. /usr/lib/partconf/find-partitions --ignore-fstype 2>/dev/null | \ grep "[[:space:]]LVM[[:space:]]" | cut -f1 } # get all unused available physical volumes # in this case all partitions with 0x8e, # or all other non-lvm devices from /proc/partitions # get_pvs() { PARTITIONS="" for i in $(enum_pvs); do # skip already assigned found=no for pv in $(vgdisplay -v | grep "[ ]*PV Name" | sed -e "s/ \+PV Name \+//"); do [ "$(readlink -f "$pv")" = "$(readlink -f "$i")" ] && found=yes done [ "$found" = "yes" ] && continue addinfos_pv "$i" i=`printf "%-15s (%s)" "$i" "$RET"` if [ -z "$PARTITIONS" ]; then PARTITIONS="$i" else PARTITIONS="${PARTITIONS},$i" fi done } # # list all activated volume groups # list_vgs() { echo `vgdisplay | grep '^[ ]*VG Name' | sed -e 's/.*[[:space:]]\+\(.*\)$/\1/' | sort` } # # check if any VGs have free space # any_free_vgs() { for i in $(list_vgs); do [ "$(getfree_vg "$i")" = "0" ] || return 0 done return 1 } # # get all activated volume groups # get_vgs() { arg="$1" GROUPS="" for i in $(list_vgs); do if [ "$arg" = "free" -a "$(getfree_vg "$i")" = "0" ]; then continue fi if [ "$arg" = "with-lv" ]; then get_vglvs "$i" [ "$LVS" = "" ] && continue fi addinfos_vg "$i" i=`printf "%-15s (%s)" "$i" "$RET"` if [ -z "$GROUPS" ]; then GROUPS="$i" else GROUPS="$GROUPS, $i" fi done } # # get all physical volumes from a volume group # get_vgpvs() { PARTITIONS="" for i in `vgdisplay -v $1 | grep '^[ ]*PV Name' | sed -e 's,.*/dev,/dev,' | cut -d " " -f 1 | sort`; do addinfos_pv "$i" i=`printf "%-15s (%s)" "$i" "$RET"` if [ -z "$PARTITIONS" ]; then PARTITIONS="$i" else PARTITIONS="$PARTITIONS, $i" fi done } # # get all logical volumes from a volume group # get_vglvs() { local i LVS="" for i in `vgdisplay -v $1 | grep '^[ ]*LV Name' | sed -e 's,.*/\(.*\),\1,' | sort`; do addinfos_lv "/dev/$1/$i" i=`printf "%-15s (%s)" "$i" "$RET"` if [ -z "$LVS" ]; then LVS="$i" else LVS="$LVS, $i" fi done } # # this is the VG main-menu # vg_mainmenu() { while [ 1 ]; do db_set lvmcfg/vgmenu "false" db_input critical lvmcfg/vgmenu db_go db_get lvmcfg/vgmenu log-output -t lvmcfg vgscan case "$RET" in Create*) vg_create ;; Delete*) vg_delete ;; Extend*) vg_extend ;; Reduce*) vg_reduce ;; *) break ;; esac # If we have only created VGs so far, then check if any PVs # are still free; if not, leave the menu and default to # creating a LV. if [ "$FIRST" = "yes" ]; then if [ "$VGRET" = "create" ]; then get_pvs if [ -z "$PARTITIONS" ]; then OVERRIDE=no db_set lvmcfg/mainmenu "Modify logical volumes (LV)" break fi else FIRST=no fi fi done } # # create a new volume group # vg_create() { # searching for available partitions (0x8e) get_pvs if [ -z "$PARTITIONS" ]; then db_set lvmcfg/nopartitions "false" db_input high lvmcfg/nopartitions db_go return fi db_subst lvmcfg/vgcreate_parts PARTITIONS $PARTITIONS db_set lvmcfg/vgcreate_parts "false" db_input critical lvmcfg/vgcreate_parts db_go db_get lvmcfg/vgcreate_parts [ "$RET" = "false" ] && return convert_return "$RET" PARTITIONS="$RET" if [ -z "$RET" ]; then db_set lvmcfg/vgcreate_nosel "false" db_input high lvmcfg/vgcreate_nosel db_go return fi db_set lvmcfg/vgcreate_name "" db_input critical lvmcfg/vgcreate_name db_go [ $? -eq 30 ] && return db_get lvmcfg/vgcreate_name NAME="$RET" # check whether a name for the vg has been given if [ "$NAME" = "" ]; then db_set lvmcfg/vgcreate_nonamegiven "false" db_input high lvmcfg/vgcreate_nonamegiven db_go return fi # check, if the vg name is already in use vgdisplay -v | grep '^[ ]*VG Name' | sed -e 's/ *VG Name *//' | grep -q "^$NAME$" if [ $? -eq 0 ]; then db_set lvmcfg/vgcreate_nameused "false" db_input high lvmcfg/vgcreate_nameused db_go return fi # check if the vg name overlaps with an existing device # FIXME: d-i uses devfs while Debian doesn't, so ideally we # would also check against a regular /dev tree. if [ -e "/dev/$NAME" ]; then db_set lvmcfg/vgcreate_devnameused "false" db_input high lvmcfg/vgcreate_devnameused db_go return fi for p in `echo "$PARTITIONS" | sed -e 's/,/ /g'`; do log-output -t lvmcfg pvcreate -ff -y $p done log-output -t lvmcfg vgcreate "$NAME" `echo "$PARTITIONS" | sed -e 's/,/ /g'` } # # delete a volume group # vg_delete() { get_vgs if [ -z "$GROUPS" ]; then db_set lvmcfg/vgdelete_novg "false" db_input high lvmcfg/vgdelete_novg db_go return fi db_subst lvmcfg/vgdelete_names GROUPS "${GROUPS}, Leave" db_set lvmcfg/vgdelete_names "false" db_input critical lvmcfg/vgdelete_names db_go db_get lvmcfg/vgdelete_names [ "$RET" = "Leave" -o "$RET" = "false" ] && return VG=`echo "$RET" | cut -d " " -f1` # confirm message db_subst lvmcfg/vgdelete_confirm VG $VG db_set lvmcfg/vgdelete_confirm "false" db_input critical lvmcfg/vgdelete_confirm db_go db_get lvmcfg/vgdelete_confirm [ "$RET" != "true" ] && return log-output -t lvmcfg vgchange -a n $VG && \ log-output -t lvmcfg vgremove $VG [ $? -eq 0 ] && return # reactivate if deleting was failed log-output -t lvmcfg vgchange -a y $VG db_set lvmcfg/vgdelete_error "false" db_input high lvmcfg/vgdelete_error db_go } # # allow expanding of existing volume groups # vg_extend() { # searching for available partitions (0x8e) get_pvs if [ -z "$PARTITIONS" ]; then db_set lvmcfg/nopartitions "false" db_input high lvmcfg/nopartitions db_go return fi get_vgs if [ -z "$GROUPS" ]; then db_set lvmcfg/vgextend_novg "false" db_input high lvmcfg/vgextend_novg db_go return fi db_subst lvmcfg/vgextend_names GROUPS "${GROUPS}, Leave" db_set lvmcfg/vgextend_names "false" db_input critical lvmcfg/vgextend_names db_go db_get lvmcfg/vgextend_names [ "$RET" = "Leave" -o "$RET" = "false" ] && return VG=`echo "$RET" | cut -d " " -f1` db_subst lvmcfg/vgextend_parts PARTITIONS $PARTITIONS db_set lvmcfg/vgextend_parts "false" db_input critical lvmcfg/vgextend_parts db_go db_get lvmcfg/vgextend_parts convert_return "$RET" PARTITIONS="$RET" if [ -z "$RET" ]; then db_set lvmcfg/vgextend_nosel "false" db_input high lvmcfg/vgextend_nosel db_go return fi for p in `echo "$PARTITIONS" | sed -e 's/,/ /g'`; do log-output -t lvmcfg pvcreate -ff -y $p && \ log-output -t lvmcfg vgextend $VG $p if [ $? -ne 0 ]; then # on error db_subst lvmcfg/vgextend_error PARTITION $p db_subst lvmcfg/vgextend_error VG $VG db_set lvmcfg/vgextend_error "false" db_input high lvmcfg/vgextend_error db_go return fi done } # # allow reducing the volume groups # vg_reduce() { get_vgs if [ -z "$GROUPS" ]; then db_set lvmcfg/vgreduce_novg "false" db_input high lvmcfg/vgreduce_novg db_go return fi db_subst lvmcfg/vgreduce_names GROUPS "${GROUPS}, Leave" db_set lvmcfg/vgreduce_names "false" db_input critical lvmcfg/vgreduce_names db_go db_get lvmcfg/vgreduce_names [ "$RET" = "Leave" -o "$RET" = "false" ] && return VG=`echo "$RET" | cut -d " " -f1` # check, if the vg has more then one pv's set -- `vgdisplay $VG | grep '^[ ]*Cur PV'` if [ $3 -le 1 ]; then db_subst lvmcfg/vgreduce_minpv VG $VG db_set lvmcfg/vgreduce_minpv "false" db_input high lvmcfg/vgreduce_minpv db_go return fi get_vgpvs "$VG" db_subst lvmcfg/vgreduce_parts PARTITIONS $PARTITIONS db_set lvmcfg/vgreduce_parts "false" db_input critical lvmcfg/vgreduce_parts db_go db_get lvmcfg/vgreduce_parts PARTITIONS=`echo "$RET" | cut -d " " -f1` log-output -t lvmcfg vgreduce $VG $PARTITIONS if [ $? -ne 0 ]; then db_subst lvmcfg/vgreduce_error VG $VG db_subst lvmcfg/vgreduce_error PARTITION $PARTITIONS db_set lvmcfg/vgreduce_error "false" db_input high lvmcfg/vgreduce_error db_go return fi } # # this is the LV main-menu # lv_mainmenu() { FIRST=yes while [ 1 ]; do db_set lvmcfg/lvmenu "false" db_input critical lvmcfg/lvmenu db_go db_get lvmcfg/lvmenu log-output -t lvmcfg vgscan case "$RET" in Create*) lv_create ;; Delete*) lv_delete ;; Extend*) lv_expand ;; Reduce*) lv_reduce ;; *) break ;; esac # Leave the menu and default to "Leave" if there is no # free space anymore after setting up LVs. if [ "$FIRST" = "yes" ]; then if [ "$LVRET" = "create" ]; then if ! $(any_free_vgs) ; then OVERRIDE=no db_set lvmcfg/mainmenu "Leave" break fi else FIRST=no fi fi done } # # create a new logical volume # lv_create() { get_vgs if [ -z "$GROUPS" ]; then db_set lvmcfg/lvcreate_novg "false" db_input high lvmcfg/lvcreate_novg db_go return fi get_vgs "free" if [ -z "$GROUPS" ]; then db_set lvmcfg/lvcreate_nofreevg "false" db_input high lvmcfg/lvcreate_nofreevg db_go return fi db_set lvmcfg/lvcreate_name "" db_input critical lvmcfg/lvcreate_name db_go [ $? -eq 30 ] && return db_get lvmcfg/lvcreate_name NAME="$RET" # check whether a name for the lv has been given if [ "$NAME" = "" ]; then db_set lvmcfg/lvcreate_nonamegiven "false" db_input high lvmcfg/lvcreate_nonamegiven db_go return fi db_subst lvmcfg/lvcreate_vgnames GROUPS "${GROUPS}, Leave" db_set lvmcfg/lvcreate_vgnames "false" db_input critical lvmcfg/lvcreate_vgnames db_go db_get lvmcfg/lvcreate_vgnames [ "$RET" = "Leave" -o "$RET" = "false" ] && return VG=`echo "$RET" | cut -d " " -f1` # make sure, the name isn't already in use vgdisplay -v $VG | grep '^[ ]*LV Name' | grep -q "${VG}/${NAME}$" if [ $? -eq 0 ]; then db_subst lvmcfg/lvcreate_exists LV "$NAME" db_subst lvmcfg/lvcreate_exists VG $VG db_set lvmcfg/lvcreate_exists "false" db_input high lvmcfg/lvcreate_exists db_go return fi MAX_SIZE=$(human2lvm "$(getfree_vg "$VG")") db_set lvmcfg/lvcreate_size "$MAX_SIZE" db_fset lvmcfg/lvcreate_size seen false db_input critical lvmcfg/lvcreate_size db_go db_get lvmcfg/lvcreate_size SIZE=$(human2lvm "$RET") [ -z "$RET" ] && return # If the maximum free space should be used for the new LV, use the # number of physical extents (PEs) because the size given by LVM # might not be accurate, resulting in an error because the VG is # not big enough (see #250594). if [ "$MAX_SIZE" = "$SIZE" ]; then log-output -t lvmcfg lvcreate -l$(getfreepe_vg "$VG") -n "$NAME" $VG else log-output -t lvmcfg lvcreate -L${SIZE} -n "$NAME" $VG fi if [ $? -ne 0 ]; then db_subst lvmcfg/lvcreate_error VG $VG db_subst lvmcfg/lvcreate_error LV $NAME db_subst lvmcfg/lvcreate_error SIZE $SIZE db_set lvmcfg/lvcreate_error "false" db_input high lvmcfg/lvcreate_error db_go return fi } # # create a new logical volume # lv_delete() { get_vgs "with-lv" if [ -z "$GROUPS" ]; then db_set lvmcfg/lvdelete_novg "false" db_input high lvmcfg/lvdelete_novg db_go return fi db_subst lvmcfg/lvdelete_vgnames GROUPS "${GROUPS}, Leave" db_set lvmcfg/lvdelete_vgnames "false" db_input critical lvmcfg/lvdelete_vgnames db_go db_get lvmcfg/lvdelete_vgnames [ "$RET" = "Leave" -o "$RET" = "false" ] && return VG=`echo "$RET" | cut -d " " -f1` get_vglvs "$VG" if [ -z "$LVS" ]; then db_set lvmcfg/lvdelete_nolv "false" db_input high lvmcfg/lvdelete_nolv db_go return fi db_subst lvmcfg/lvdelete_lvnames VG "$VG" db_subst lvmcfg/lvdelete_lvnames LVS "${LVS}, Leave" db_set lvmcfg/lvdelete_lvnames "false" db_input critical lvmcfg/lvdelete_lvnames db_go db_get lvmcfg/lvdelete_lvnames [ "$RET" = "Leave" -o "$RET" = "false" ] && return LV=`echo "$RET" | cut -d " " -f1` log-output -t lvmcfg lvremove -f /dev/${VG}/${LV} if [ $? -ne 0 ]; then db_subst lvmcfg/lvdelete_error VG $VG db_subst lvmcfg/lvdelete_error LV $LV db_set lvmcfg/lvdelete_error "false" db_input high lvmcfg/lvdelete_error db_go return fi } # # *** main stuff *** # # load required kernel modules depmod -a 1>/dev/null 2>&1 modprobe dm-mod >/dev/null 2>&1 modprobe lvm-mod >/dev/null 2>&1 # make sure that lvm is available if ! grep -q "[0-9] device-mapper$" /proc/misc ; then db_set lvmcfg/nolvm "false" db_input high lvmcfg/nolvm db_go exit 0 fi # scan for logical volumes log-output -t lvmcfg pvscan log-output -t lvmcfg vgscan # try to activate old volume groups set -- `vgdisplay -v | grep 'NOT \(active\|available\)' | wc -l` if [ $1 -gt 0 -a ! -f /var/cache/lvmcfg/first ]; then db_subst lvmcfg/activevg COUNT $1 db_set lvmcfg/activevg "false" db_input critical lvmcfg/activevg db_go db_get lvmcfg/activevg [ "$RET" = "true" ] && log-output -t lvmcfg vgchange -a y fi # ask only the first time mkdir -p /var/cache/lvmcfg && touch /var/cache/lvmcfg/first FIRST=yes # The following variable determines whether the lvmcfg/mainmenu is set # to false. At various places in this script, OVERRIDE=no is set in # order to set a default. This is useful to have LV as default when # all VGs have been set up, and Leave the default after all LVs have # been allocated. OVERRIDE=yes # main-loop while [ 1 ]; do [ "$OVERRIDE" = "yes" ] && db_set lvmcfg/mainmenu "false" db_input critical lvmcfg/mainmenu db_go db_get lvmcfg/mainmenu MAINRET=`echo "$RET" | sed -e 's,.*(\(.*\)).*,\1,'` OVERRIDE=yes case "$MAINRET" in "PV") # currently unused #pv_mainmenu ;; "VG") vg_mainmenu ;; "LV") lv_mainmenu ;; *) break # finished ;; esac done # install lvm tools in /target if needed set -- `vgdisplay -v | grep -i "Logical volume ---" | wc -l` [ $1 -gt 0 ] && apt-install lvm2 exit 0