Category: AIX
Author/Contact Info Bowie J. Poag <bpoag@comcast.net>
Description: MoveCeiling is an AIX Perl script that monitors filesystem growth, and attempts to ensure a given filesystem doesn't run out of space by increasing the amount of free space ahead of the demand for additional storage. MoveCeiling also keeps a timestamped log of it's actions (located in /tmp/moveceiling.log) for later reference.
#!/usr/bin/perl
#
# MoveCeiling v0.2 written 061206:1122 by BJP
#
# MoveCeiling will watch and automatically increase the size of an AIX
+ filesystem
# given user-specified parameters.
#
# Usage: moveceiling <ceiling in MB> <filesystem> <increment size> <sl
+eep time> <space to preserve> <notify counter> <email address>
# Example: moveceiling 500 /tmp 64 30 3200 5 foo@bar.com
# English: Keep 500MB free in /tmp. If you have to add space, add it i
+n 64MB chunks. Check out the situation every 30 seconds. Preserve 320
+0MB in the volume group. Remind me every 5 times you have to increase
+ space, and send that reminder to foo@bar.com
#

use Mail::Sendmail;

$ceilingSize=$ARGV[0];
$targetFS=$ARGV[1];
$incrementSize=$ARGV[2];
$sleepTime=$ARGV[3];
$preserveAmount=$ARGV[4];
$notifyCounter=$ARGV[5];
$mailRecipient=$ARGV[6];
$mailSender=$ENV{"USER"}."\@"."localhost";

if ($#ARGV!=6)
{
        showUsage();
        exit();
}


spinUpRoutine();
mainRoutine();
spinDownRoutine();


sub showUsage()
{

        print "\n MoveCeiling's job is to watch a filesystem, and incr
+ease it's size if the\n";
        print " amount of free space in that filesystem falls below th
+reshold. MoveCeiling\n";
        print " is much smarter than it used to be, but you should sti
+ll use it with caution. \n";
        print "\n";
        print "      Usage: moveceiling <ceiling in MB> <filesystem> <
+increment size> <sleep time> <space to preserve> <notify counter> <em
+ail address>\n";
        print "    Example: moveceiling 500 /tmp 64 30 5 foo\@bar.com\
+n";
        print " In English: Every 30 seconds, check to see if /tmp has
+ at least 500MB free. \n";
        print "             If it sinks below 500MB, add another 64MB.
+ For every 5 times I \n";
        print "             add more space, i'll send a status message
+ to foo\@bar.com.\n";
        print "\n\n";
        exit();
}


sub spinUpRoutine()
{
        print "\nMoveCeiling: I will make sure $targetFS has at least 
+$ceilingSize MB free.";
        print "\nMoveCeiling: If I have to increase it's size, I will 
+do so in $incrementSize MB increments.";
        print "\nMoveCeiling: Every $notifyCounter times I do so, i'll
+ drop a note to $mailRecipient.";
        print "\nMoveCeiling: I'll stop adding storage if the volume g
+roup has less than $preserveAmount MB remaining.";
        print "\nMoveCeiling: Starting up..\n";

        ## Check to see if the filesystem actually exists...

        @dfContents=split(/\s+/,`df -m | grep " $targetFS\$"`);

        if(length($targetFS)==length($dfContents[6]))
        {
                print "MoveCeiling: Examining $dfContents[6]..\n";
        }


        else
        {
                print "MoveCeiling: Uh-oh.. Can't find the filesystem 
+you specified ($targetFS)..!  Exiting.\n\n";
                exit();
        }

        ## So far so good. Pull up the VG info for this filesystem..

        $dfContents[0]=~/\/dev\//;
        $targetLV=$';
        @lslvContents=split(/\s+/,`lslv $targetLV|head -n1` );
        $targetVG=$lslvContents[5];

        print "MoveCeiling: $targetFS belongs to logical volume $targe
+tLV inside $targetVG.\n";

        @lsvgDump=split(/\s+/,`lsvg -L $targetVG | head -n4 | tail -n1
+`);
        $lsvgDump[6]=~/\(/;
        $targetVGSpaceRemaining=$';

        print "MoveCeiling: $targetVG has $targetVGSpaceRemaining MB r
+emaining, of which $preserveAmount MB will be preserved.\n\n";


}

sub mainRoutine()
{
        while(true)
        {
                @dfContents=split(/\s+/,`df -m | grep " $targetFS\$"`)
+;
                chomp($dateString=`date`);

                print "MoveCeiling: $dfContents[2] MB remaining in $ta
+rgetFS as of $dateString.\n";

                @lsvgDump=split(/\s+/,`lsvg -L $targetVG | head -n4 | 
+tail -n1`);
                $lsvgDump[6]=~/\(/;
                $targetVGSpaceRemaining=$';

                if($dfContents[2] < $ceilingSize) # Tricky logic time.
+.
                {
                        if($targetVGSpaceRemaining >= ($preserveAmount
++$incrementSize))
                        {
                                print "MoveCeiling: Less than $ceiling
+Size MB remaining! Adding $incrementSize MB to $targetFS..\n";
                                system("echo `date` -- MoveCeiling: Le
+ss than $ceilingSize MB remaining! Adding $incrementSize MB to $targe
+tFS.. >>/tmp/moveceiling.log");
                                `chfs -a size=+"$incrementSize"M $targ
+etFS`;
                                $incrementCounter++;

                                if($incrementCounter==$notifyCounter)
                                {
                                        sendNotification();
                                        $incrementCounter=0;
                                }
                        }

                        else
                        {
                                system("echo `date` -- MoveCeiling: Pr
+eserve limit reached. Committing suicide. $targetFS last seen with $d
+fContents[2] MB free, $targetVGSpaceRemaiing MB remaining in $targetV
+G.  >>/tmp/moveceiling.log");

                                print "MoveCeiling: Increasing $target
+FS by $incrementSize MB would eat into the preserved area! \n";
                                print "MoveCeiling: Preparing suicide 
+note to $mailRecipient..\n";
                                sendSuicideNote();
                                print "MoveCeiling: My hands are tied!
+ I'm sorry!\n";
                                spinDownRoutine();
                                exit();
                        }
                }

                else
                {
                        print "MoveCeiling: More than $ceilingSize MB 
+remaining..No increase needed.\n";
                }

                print "MoveCeiling: $targetVGSpaceRemaining MB remaini
+ng in $targetVG. Sleeping for $sleepTime seconds..\n\n";
                sleep($sleepTime);
        }
}


sub sendSuicideNote()
{

        %mail=( To => $mailRecipient,
                From => $mailSender,
                Subject => "MoveCeiling Suicide Notice",
                Message => "Preserve limit reached -- Data loss may be
+ imminent. $targetFS last seen with $dfContents[2] MB free, $targetVG
+SpaceRemaining MB remaining in $targetVG. );

                sendmail(%mail);

        print "MoveCeiling: Suicide note sent.\n";

}

sub sendNotification()
{
        print "MoveCeiling: Sending a friendly reminder to $mailRecipi
+ent..\n";

        %mail=( To => $mailRecipient,
                From => $mailSender,
                Subject => "MoveCeiling Update",
                Message => "$targetFS still growing. Now at $dfContent
+s[2] MB in size with $targetVGSpaceRemaining MB remaining in $targetV
+G." );

                sendmail(%mail);

        print "MoveCeiling: Mail sent.\n";
}

sub spinDownRoutine()
{
        print "MoveCeiling: Spinning down..\n\n";
}
Replies are listed 'Best First'.
Re: MoveCeiling - monitor filesystem growth
by jwkrahn (Abbot) on Jun 16, 2006 at 02:59 UTC
    You should include the warnings and strict pragmas in your code to help you locate mistakes. After I corrected the error on line 159 I compiled with warnings enabled which produced these messages:
    Unquoted string "true" may clash with future reserved word at 555648.p +l line 98. Bareword found in conditional at 555648.pl line 146. main::showUsage() called too early to check prototype at 555648.pl lin +e 27. main::spinUpRoutine() called too early to check prototype at 555648.pl + line 32. main::mainRoutine() called too early to check prototype at 555648.pl l +ine 33. main::spinDownRoutine() called too early to check prototype at 555648. +pl line 34. Name "main::targetVGSpaceRemaiing" used only once: possible typo at 55 +5648.pl line 127.
    if ($#ARGV!=6)
    Your program requires seven arguments so why are you obfuscating that fact?
    if ( @ARGV != 7 )
    All of your subroutines except spinDownRoutine() are only called once. Subroutines are very handy if you have to encapsulate data and code that has to be used multiple times however you are neither encapsulating the data in the subroutine nor using them more than once so why put everything in subroutines?

      Hey!

      With regard to $#ARGV versus @ARGV... I was working with indexes at that point in the code, and didn't think to switch gears mentally and make the check look more readable. You are correct, it is a little obfuscated to an outside observer, since element 0 is the beginning of the argument list in ARGV versus 1 with 0 being the command itself. Thats what $0 is for, as you probably know. Good catch, btw.

      Blocking things off into subroutines right off the bat is sort of a style quirk of mine. For strict procedural things, you're right, it's sort of pointless...But it helps if I have to revisit the code later and make changes, which is a frequent occurence here where I work. It's kind of funny, we have sort of a generic "grandfather" script we use over here as sort of a template for "watch this, do this, and notify this person" tasks...This script wasn't derived from it, specifically, but I can think of at least half a dozen scripts we use that were, and it came in handy to have things already compartmentalized like that. spinDownRoutine() is a good example. Everything I write has a spinUp/Down routine. Sometimes they come in handy, other times they're just withered limbs.

      As for the typo, that's an artifact of the cut/paste process to get it posted here on PerlMonks. :)

      Cheers,
      Bowie