Automatically growing LVM partitions as the data in them expands

We all know that the IT equivalent of Parkinson's law is that data expands to fill slightly more than the space available.

I like using Logical Volume Management for data partitions, in order to make efficient use of disk space, with convenient facilities to add another disk, migrate partitions, and adjust the size of partitions as data fills them.

I created the following script which can be run from cron (I do it every 5 or 10 minutes) to scan each volume group for logical volumes which are currently mounted, and check how full the (presumed ext2/3/4) contained file system is. If the file system is more than 95% full, the partition and the file system are grown by 10%; if the file system is between 90% and 95% full, they're grown by 5%. Anything under 90% is left as it is.

In this way, provided you have sufficient spare capacity in your volume group, you can keep writing data to the file systems in the logical volumes, and never reach 100% with all the inconvenience that entails. This script makes use of the automatic console/SSH/cron detection mechanism to determine the default debug level. You can override it by specifying the debug level as the first parameter on the command line.

Note: This is indeed a very useful automated facility, but just in case something goes ballistic and starts eating up disk space faster than you can keep an eye on it, do bear in mind that once you've sorted out the runaway process and deleted whatever it created, shrinking the file system and its containing partition can take a very long time. It works, no problem, but it's slow.

dynamic.lvm.sh
#!/bin/bash

PATH=$PATH:/sbin

[ "$TERM" = "dumb" -a ! -t 0 ] && run=cron || run=local
[ "`ps --no-headers o comm $PPID`" = "sshd" ] && run=SSH

debug=0
[ "$run" = "SSH" ] && debug=1
[ "$run" = "local" ] && debug=2

recipient=username

# Add increment1% if we're above threshold1%, but add increment2% if we're above threshold2% full

threshold1=85
increment1=5

threshold2=90
increment2=10

[ -n "$1" ] && debug=$1

# Scan for all Volume Groups

vgs --noheadings | tr -s ' ' | cut -d' ' -f2 | while read vg
do
  [ "$debug" -gt "0" ] && echo "Checking Volume Group $vg"
# Scan for Logical Volumes in each Group
  lvs --noheadings $vg | tr -s ' ' | cut -d' ' -f2 | while read lv
  do
    [ "$debug" -gt "0" ] && echo "Checking Logical Volume $lv"
# Check if LV is mounted
    percent=`df -h | grep "/dev/mapper/$vg-$lv " | sed 's/.* \([0-9]\+\)%.*/\1/'`
    if [ -n "$percent" ]
# Yes we have a mounted LV
    then
      [ "$debug" -gt "0" ] && echo "Logical Volume $vg/$lv is mounted and is $percent% full"
# Is it more than threshold1% full?
      if [ "$percent" -gt "$threshold1" ]
      then
# Add increment1% if we're above threshold1%, but add increment2% if we're above threshold2% full
        incr=$increment1
        [ "$percent" -gt "$threshold2" ] && incr=$increment2
        if ps ax | grep -v grep | grep -q "resize2fs /dev/mapper/$vg-$lv "
        then
          echo "resize operation already running" | mail -s "Would increase /dev/$vg/$lv by $incr%" $recipient
        else
          ( time ( df -h | grep "/dev/mapper/$vg-$lv "; echo; lvextend -l +$incr%LV -r /dev/$vg/$lv 2>&1; df -h | grep "/dev/mapper/$vg-$lv "; echo; vgs ) ) |& mail -s "Increasing /dev/$vg/$lv by $incr%" $recipient
        fi
      else
        [ "$debug" -gt "0" ] && echo "Partition is ${percent}% full" | mail -s "/dev/$vg/$lv does not need extending at present." $recipient
      fi
    else
      [ "$debug" -gt "0" ] && echo "Logical Volume $vg/$lv is not mounted"
    fi
  done
done

Substitute the user (or external email address) you want to receive the notification emails for username in the above script.


Go up
Return to main index.