#!/usr/bin/perl
#desc{    calm -- sleeps until the system is relatively calm.   }
#======================================================================
#  The way I often use it:
#    some_huge_app & ;  calm && another_huge_app &
#======================================================================
# Time-stamp: "2008-10-11 17:55:58 ADT"
# -*-coding:latin-1;-*-             « sburke@pobox.com »                    |
use constant DEBUG => 0; use strict; use warnings; $| = 1;
#======================================================================

my $Trial_Period = $ARGV[0] || 300; # max seconds.  300 = 5 minutes
my $Min_Samples = 3;
my $Time_Between_Samples = 1;

# max percentage of usage percentages that count as "calm":
my    $Total_Proc_Calm_Max = 50;
my  $Total_IOWait_Calm_Max = 25;

#End of configurables
#======================================================================

my $Heading_Checker = qr{
  ^
  \s* ([\:0-9]+ # time
  \s+  \w+)     # am/pm or whatever
  \s+  CPU
  \s+  %user
  \s+  %nice
  \s+  %sys
  \s+  %iowait
  \s+  %irq
  \s+  %soft
  \s+  %steal
  \s+  %idle
  \s+  intr/s
}smx;

#Linux 2.6.15-51-386 (..........)        05/06/2008
#
#12:40:42 AM  CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s
#12:40:43 AM  all    6.93    0.00    0.00    0.00    0.00    0.00    0.00   93.07    294.06
#12:40:44 AM  all    6.93    0.00    1.98    0.00    0.00    0.00    0.00   91.09    311.88
#Average:     all    6.93    0.00    0.99    0.00    0.00    0.00    0.00   92.08    302.97

my $Fields_Extractor = qr{
  ^
  \s* Average:
  \s+  (\w+)      #CPU name
  \s+  ([\.0-9]+) #%user
  \s+   [\.0-9]+  #%nice
  \s+  ([\.0-9]+) #%sys
  \s+  ([\.0-9]+) #%iowait
  \s+   [\.0-9]+  #%irq
  \s+   [\.0-9]+  #%soft
  \s+   [\.0-9]+  #%steal
  \s+   [\.0-9]+  #%idle
  \s+   [\.0-9]+  #intr/s
}smx;

my $Max_Time = $Trial_Period + $^T;

my $Command_V = "mpstat -V 2>&1";
my $Command   = "mpstat $Time_Between_Samples $Min_Samples";

main();
exit;
#======================================================================

sub main {
  check_prog_avail();
  wait_for_calm();
}

sub check_prog_avail {
  my $output = qx[$Command_V];
  unless( defined $output and $output =~ m/\d/) {
    die "mpstat isn't available on your system.\n  Aborting";
  }
  DEBUG > 10 and print "$Command_V => {{$output}}\n";
  return 1;
}

sub wait_for_calm {
  my( $now, $line, $total_proc, $iowait, $now_from_heading );

  while(1) {
    $now = time();
    if( $now >= $Max_Time ) {
      die join '', "It's ", scalar(localtime($now)),
       " and I've been waiting for calm since ", scalar(localtime($^T)),
       " (" , $now - $^T, " seconds).  So I'm aborting.\n ";
    }

    chomp($line = qx[$Command]);

    die "I can't find the expected header line in here:\n$line\nAborting"
     unless $line =~ $Heading_Checker;
    $now_from_heading = $1;

    DEBUG > 5 and print "$Command =>\n $line\n";

    if( $line =~ $Fields_Extractor ) {
      print "I expected the CPU name to be 'all', not: $1"
       unless $1 eq 'all';
      $iowait = $4;
      $total_proc = ($2 + $3);
      if($total_proc > $Total_Proc_Calm_Max) {
        if(DEBUG > 2) {
          print " $now_from_heading: Busy because proc $total_proc% > $Total_Proc_Calm_Max%\n";
        }
        next;
      }
      if($iowait >  $Total_IOWait_Calm_Max) {
        if(DEBUG > 2) {
          print " $now_from_heading: Busy because iowait $iowait% > $Total_IOWait_Calm_Max%\n";
        }
        next;
      }

      DEBUG and print " [Calm now (cpu:$total_proc, io:$iowait).  Exiting okay after ", time() - $^T, " seconds.]\n";
      last;
    } else {
      die "But \"$line\" is in an unexpected format\nAborting"
    }
  }
  return 1;
}

__END__

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See http://www.perl.com/perl/misc/Artistic.html

=head1 AUTHOR

Sean M. Burke, E<lt>sburke@cpan.orgE<gt>

