Remi Fasol wrote:
> 
> hi Joshua,
> 
> is this your recommended setup when using Apache::ASP
> or is this for mod_perl in general?
> 

mod_perl in general.

> if it's for Apache::ASP, do you have a sample CPU
> limit script and/or watchdog?
> 

Check out Apache::Resource, the CPU limiting feature
is well documented there.  You need to install
also BSD::Resource on your system.

My watch dog code is a bit of a mess, but it uses LWP::*, 
or lwp-request to query the server, test for expected 
output, and if it takes too long restart the server.  If 
too many subsequent restarts fail to work, and apache is 
still not responding in a timely manner, send an 
email to the administrator.

Some code is posted below that you can adapt for you use.

--Joshua
_________________________________________________________________
Joshua Chamas                           Chamas Enterprises Inc.
NODEWORKS >> free web link monitoring   Huntington Beach, CA  USA 
http://www.nodeworks.com                1-714-625-4051

## main watchdog, you can add other testing modules, I test
## web server, database, dns, smtp with this.

use Util;
use My::UserAgent;
# use LWP::Debug qw(+);

my $ua = new My::UserAgent;
my $self = new Util::Monitor;

$self->add_run(bless
               {
                   'name' => 'proxy homepages test',

                   # make sure both the secure and non-secure pages are working
                   'test' => sub {
                       my $http_doc = $ua->Request('http://www.nodeworks.com');
                       my $https_doc = $ua->Request('https://www.nodeworks.com');
                       ($http_doc->{success} && $https_doc->{success}); },

                   # restart www server gracefully, works when server isn't started
                   # all too
                   'fix'  => sub {
                       $self->log("attempting restart of www server");
                       `/usr/local/apache/sbin/apachectl graceful`; },

                   # allow for downtime of one - two minutes before sending page
                   # it may take a minute to reboot server when the machine is busy
                   'period' => 30,
                   'max_tests' => 3,
                   'timeout' => 30,
               }, Util::Monitor::Run
               );

$self->monitor;

## from Util.pm

package Util::Monitor;
@ISA = qw(Util);
use Class::Struct;
use File::Basename;
use Carp qw(confess cluck carp);
use HTTP::Date;
use Net::SMTP;
use Net::Config;
use Data::Dumper;
use Tie::CPHash;
use File::Basename;
use Time::localtime;

@Mandatory = ('name', 'test', 'period');
$MaxSleep = 60;
$DefaultMaxTests = 3;

unless(keys %Util::Monitor::Run::) {
    struct(Util::Monitor::Run => {
        'name' => "\$", # string describing test
        'test' => "\$", # CODE
        'fix'  => "\$", # CODE
        'period' => "\$", # time in seconds to iterate
        'max_tests' => "\$", # number of times before erroring
        'num_tests' => "\$", # number of times before erroring
        'last' => "\$", # time last ran
        'timeout' => "\$", # timeout for test
    });
}

sub new {
    chdir(File::Basename::dirname($0)) || die("can't change to dir for $0");
    my $self = bless { runs => [] };
    $self->write_pidfile;
    $self;
}

# add runs before monitoring
sub add_run {
    my($self, $run) = @_;
    die("no run") unless $run;
    die("run is not well defined") unless (@Mandatory == grep(defined $run->{$_}, 
@Mandatory));

    $run->max_tests || $run->max_tests($DefaultMaxTests);
    $run->last(0);
    $run->num_tests(0);
    push(@{$self->{runs}}, $run);
}

# main code to loop over
sub monitor {
    my $self = shift;
    while($self->alive) {
        $self->do_runs;
        $self->sleep;
    }
}

sub sleep {
    my $self = shift;
    my $next_time = time() + $MaxSleep;
    for(@{$self->{runs}}) {
        my $run_time = $_->period + $_->last;
        if($run_time < $next_time) {
            $next_time = $run_time;
        }
    }
    my $sleep_time = $next_time - time;
    if($sleep_time > 0) {
        $self->log("sleeping $sleep_time");
        sleep($sleep_time);
    } else {
        $sleep_time = 0;
    }

    $sleep_time;
}

sub do_runs {
    my $self = shift;
    @{$self->{runs}} > 0 or die("no runs to do");

    my $run;
    for $run (@{$self->{runs}}) {
        my $run_time = $run->period + $run->last;
        next unless ($run_time <= time);
        $self->log("doing run name ".$run->name);
        $self->do_run($run);
        $run->last(time);
    }
}

sub do_run {
    my($self, $run) = @_;

    my $name = $run->name;
    my $start = time();
    my $result = $self->try($run->test, $run->timeout);
    my $total = time - $start;
    $self->log("time for $name: $total");

    if($result) {
        # test succeeded
        $self->log("test success for $name, result $result");
        if($run->num_tests) {
            $self->sendmail({
                Subject => $run->name . " fixed",
                Body => "failed times before fixing: ".$run->num_tests
                });
        }
        $run->num_tests(0);
        $result;
    } else {
        $run->num_tests($run->num_tests + 1);
        $self->log("test fail for $name");
        if($run->num_tests >= $run->max_tests) {
            $self->log("tests for $name failed, sending crit");
            $self->critical("$name failed");
            $run->num_tests(0);
        }

        if($run->fix) {
            if($self->try($run->fix, $run->timeout)) {
                $self->log("fix for $name success");
            } else {
                $self->log("fix for $name failed");
            }
        }
        0;
        $run->last(time);
    }
}

sub alive {
    my $self = shift;
    $$ eq $self->read_pidfile;
}

sub pidfile {
    my($self, $name) = @_;
    $name ||= basename($0);
    $name =~ s/.[^.]+$//s;
    $name .= "_pid";
    $name;
}

# pid_file is the name_pid file for the running process
sub read_pidfile {
    my($self, $name) = @_;
    my $pidfile = $self->pidfile($name);
    local *PID;
    open(PID, $pidfile) || return '';
    my $data = <PID>;
    close PID;
    $data || '';
}

sub write_pidfile {
    my($self, $name) = @_;
    my $pidfile = $self->pidfile($name);
    local *PID;
    open(PID, ">$pidfile") || die("can't write $pidfile");
    print PID $$;
    close PID;
    1;
}

sub sendmail {
    my($self, $mail, %args) = @_;
    my($smtp, @to, $server);
    my($rv) = 1;

    tie %mail, 'Tie::CPHash';
    %mail = %{$mail};
    $mail = \%mail;

    $mail->{To}   ||= $AdminEmail;
    $mail->{From} ||= $SystemEmail;

    confess("not enough args to send mail")
        unless ($mail->{to} && $mail->{body} && $mail->{subject} && $mail->{from});

    # connect to server
    $args{Timeout} ||= $SmtpTimeout;
    if(exists $mail->{Debug}) {
        $args{Debug} = $mail->{Debug};
        delete $mail->{Debug};
    }
    $smtp = Net::SMTP->new(%args);
    $smtp || return(0);

    @to = (ref $mail->{to}) ? @{$mail->{to}} : ($mail->{to});

    $smtp->mail($mail->{from});
    $smtp->to(@to);

    my($data);
    my $body = $mail->{body};
    delete $mail->{body};

    my %done;
    for('Subject', 'From', 'Reply-To', 'Organization', 'To', keys %$mail) {
        next unless $mail->{$_};
        next if $done{lc($_)}++;
        my $add = ref($mail->{$_}) ? join(", ", @{$mail->{$_}}) : $mail->{$_};
        $data .= "$_: $add\n";
    }
    $data .= "\n" . $body;

    $smtp->data($data) || ($rv = 0);
    $smtp->quit();

    $rv;
}

Reply via email to