package Nagios::WebTransact::Timed; use strict; use vars qw($VERSION @ISA) ; $VERSION = '0.06'; @ISA = qw(Nagios::WebTransact) ; use HTTP::Request::Common qw(GET POST) ; use HTTP::Cookies ; use LWP::UserAgent ; use Time::HiRes qw(gettimeofday tv_interval) ; use Carp ; use Nagios::WebTransact ; use constant FALSE => 0 ; use constant TRUE => ! FALSE ; use constant FAIL => FALSE ; use constant OK => TRUE ; # ie Normal Perl semantics. 'Success' is TRUE (1). # Caller must map this to Unix/Nagios return codes. use constant GET_TIME_THRESHOLD => 10 ; use constant FAIL_RATIO_PERCENT => 50 ; sub check { my ($self, $cgi_parm_vals_hr) = @_ ; my %defaults = ( cookies => TRUE, debug => TRUE, timeout => GET_TIME_THRESHOLD, agent => 'Mozilla/4.7', proxy => {}, fail_if_1 => FALSE, verbose => 1, download_images => FALSE, indent_level => 0, fail_ratio_percent => FAIL_RATIO_PERCENT ) ; # check semantics. # $fail_if_1 ? return FAIL if any URL fails # ! $fail_if_1 ? return FAIL if all URLs fail # (same as return OK if any URL ok) my %parms = (%defaults, @_) ; my $debug = $parms{debug} ; my $verbose = $parms{verbose} ; my $indent = $parms{indent_level} ; my $fail_ratio_percent = $parms{fail_ratio_percent} || FAIL_RATIO_PERCENT ; croak("Expecting fail_ratio_percent as a percentage (0-100%), got \$fail_ratio:_percent: $fail_ratio_percent\n") if $fail_ratio_percent < 0 or $fail_ratio_percent > 100 ; my $fail_ratio = $fail_ratio_percent / 100 ; my $timeout = $parms{timeout} ; croak("Expecting timeout as a natural number (0 ... not_too_big), got \$timeout: $timeout.\n") if $timeout < 0 ; my ($ua, %downloaded) ; keys %downloaded = 128 ; $ua = new LWP::UserAgent ; $ua->agent($parms{agent}) ; $ua->timeout($timeout) ; $ua->cookie_jar(HTTP::Cookies->new) if $parms{cookies} ; $ua->proxy(['http', 'ftp'] => $parms{proxy}{server}) if exists $parms{proxy}{server} ; my @urls = @{ $self->{urls} } ; my $Fault_Threshold = int( scalar @urls * $fail_ratio + 0.5 ) * $timeout ; my $check_time = 0 ; my @get_times = () ; foreach my $url_r ( @urls ) { my $req = $self->make_req( $url_r->{Method}, $url_r->{Url}, $url_r->{Qs_var}, $url_r->{Qs_fixed}, $cgi_parm_vals_hr ) ; $req->proxy_authorization_basic( $parms{proxy}{account}, $parms{proxy}{pass} ) if exists $parms{proxy}{account} ; print STDERR ' ' x $indent, '... ' ,$req->as_string, "\n" if $debug ; my $t0 = [gettimeofday] ; my $res = $ua->request($req) ; my $elapsed = tv_interval ($t0) ; my $rounded_elapsed = ( ($elapsed < GET_TIME_THRESHOLD and $res->is_success) ? sprintf('%3.2f', $elapsed ) : GET_TIME_THRESHOLD ) ; push @get_times, $rounded_elapsed ; $check_time += $rounded_elapsed ; print STDERR ' ' x $indent, '... ' , $res->as_string ,"\n" if $debug ; if ( $verbose ) { my $url_report = sprintf("%-95s%10s%-5.2f%-40s\n", substr(' ' x $indent . (! $indent ? '--getting ' : '') . $url_r->{Url}, 0, 95), , ' ' x 10, , $rounded_elapsed, , (! $indent ? 'Total check time: ' : ' image download time: ') . sprintf('%5.2f', $check_time)) ; print STDERR $url_report ; } unless ( $check_time <= $Fault_Threshold ) { my $i = 0 ; foreach (@urls) { $get_times[$i] = GET_TIME_THRESHOLD if not defined $get_times[$i] ; $i++ ; } return (FAIL, 'Transaction failed. Timeout', \@get_times) ; } if ( $parms{download_images} ) { my ($image_dl_ok, $image_dl_msg, $image_get_times_ar, $number_imgs_dl ) = &download_images($res, \%parms, \%downloaded) ; return (FAIL, $image_dl_msg) unless $image_dl_ok ; $self->{number_of_images_downloaded} += $number_imgs_dl ; $get_times[-1] += $_ foreach @$image_get_times_ar ; # &download_images() will call check() which returns here the list of image download times in @$image_get_times_ar. # Each elt in this list is added to the last html download time ($get_times[-1]) leaving @get_times containing # the total download time for the page (downloaded sequentially and without heed to 'if modified' headers). printf "%137s%5.2f\n", 'Total page download time: ', $get_times[-1] if $verbose ; } } return (OK, 'Transaction completed Ok.', \@get_times) ; } sub download_images { my ($res, $parms_hr, $downloaded_hr) = @_ ; require HTML::LinkExtor ; require URI::URL ; URI::URL->import(qw(url)) ; my @imgs = () ; my $cb = sub { my($tag, %attr) = @_; return if $tag ne 'img'; # we only look closer at push(@imgs, $attr{src}); } ; my $p = HTML::LinkExtor->new($cb) ; $p->parse($res->as_string) ; my $base = $res->base; my @imgs_abs = grep ! $downloaded_hr->{$_}++, map { my $x = url($_, $base)->abs; } @imgs; my @img_urls = map { Method => 'GET', Url => $_->as_string, Qs_var => [], Qs_fixed => [], Exp => '.', Exp_Fault => 'NeverInAnImage' }, @imgs_abs ; # url() returns an array ref containing the abs url and the base. if ( my $number_of_images_not_already_downloaded =scalar @img_urls ) { # If there are no images that have not been downloaded, then don't try to call ->check([]) since it will return FAIL. my $img_trx = __PACKAGE__->new(\@img_urls) ; my %image_dl_parms = (%$parms_hr, fail_if_1 => FALSE, download_images => FALSE, indent_level => 1) ; return ( $img_trx->check({}, %image_dl_parms), $number_of_images_not_already_downloaded ) ; } else { return (OK, 'Downloaded all __zero__ images found in ' . $res->base, [], 0) ; } } 1 ; __END__ # Below is the stub of documentation for your module. You better edit it! =head1 NAME Nagios::WebTransact::Timed - An object that provides a check method (usually called by a Nagios service check) to determine if a sequence of URLs can be got inside a time threshold, returning the times for each. =head1 SYNOPSIS use Nagios::WebTransact::Timed; # Constructors $web_trx = Nagios::WebTransact::Timed->new(\@url_set); =head1 DESCRIPTION WebTransact::Timed is a subclass of WebTransact that checks web performance by downloading a sequence of URLs. The check is successfull if no more than B of the URLs fail ie a URL is downloaded inside the timeout period with a successfull HTTP return code and no indications of invalid content. Note that unlike WebTransact, this object only returns FAIL if all URLs fail or timeout. =head1 CONSTRUCTOR =over 4 =item Nagios::WebTransact::Timed->new(ref_to_array_of_hash_refs) E<10> This is the constructor for a new Nagios::WebTransact object. C is a reference to an array of records (anon hash refs) in the format :- { Method => HEAD|GET|POST, Url => 'http://foo/bar', Qs_fixed => [cgi_var_name_1 => val1, ... ] NB that now square brackets refer to a Perl array ref Qs_var => [cgi_var_name_1 => val_at_run_time], Exp => blah, Exp_Fault=> blurb } Exp and Exp_Fault are normal Perl patterns without pattern match delimiters. Most often they are strings. =item B is the pattern that when matched against the respose to the URL (in the same hash) indicates success. =item B is the pattern that indicates the response is a failure. If these patterns contain parentheses eg 'match a lot (.*)', then the match is saved for use by Qs_var. Note that there should be only B pattern per element of the Exp list. Nested patterns ( C ) will not work as expected. Qs_fixed and Qs_var are used to generate a query string. =item B contains the name value pairs that are known at compile time whereas =item B contains placeholders for values that are not known until run time. =back In both cases, the format of these fields is a reference to an array containing alternating CGI variable names and values eg \(name1, v1, name2, v2, ...) produces a query string name1=v1&name2=v2&.. =head1 METHODS Unless otherwise stated all methods return either a I or I value, with I meaning that the check of the web transaction was a success. I is a zero (0). =over 4 =item check( CGI_VALUES, OPTIONS ) Performs a check of the Web transaction by getting the sequence or URLs specified in the constructor argument. are passed in a hash like fashion, using key and value pairs. Possible options other than those specified by the super class are B specifies a timeout different to the default (10 seconds) for each URL. When a URL Bt be fetched, it is recorded as having taken B<10> (ten) seconds. B specifies that the check will return immediately (with a failure) if the proportion of failures (ie if HTTP::Response::is_success says it is or a timeout) as a percentage, is greater than this threshold. eg if fail_ratio_percent is 100, fetching all the URls must fail before the check returns false. B is meant for CLI use (or in a CGI). It reports the time taken for each URL on standard B. B is meant for CLI use (or in a CGI). It reports the time taken to download each of the images found in the page provided that image has not been downloaded by the Nagios::WebTransact object session. Download time is displayed on standard B. check returns a boolean indication of success and a reference to an array containing the time taken for each URL. If a URL cannot be download (invalid content, HTTP failure or timeout), the time is marked as 10. =back =head1 EXAMPLE see check_inter_perf.pl in t directory. =head1 BUGS =over 4 =item 1 Timeout is B and applies independently to image download and HTML - if you ask for S, the timeout is applied to the images and the HTML separately effectively doubling the timeout. =item 2 A more flexible approach may be for this module to decorate the super class, =item 3 Having to supply the list of URLs to the constructor is strange. =back =head1 AUTHOR S Hopcroft, Stanley.Hopcroft@IPAustralia.Gov.AU =head1 SEE ALSO perl(1). Nagios::WebTransact Nagios http://www.Nagios.ORG =cut