Taking Nagios::Plugin::WWW::Mechanize for a ride

Posted on 20/05/

I love Nagios::Plugin because it simplifies (and beautifies) the plugins for Nagios that I write. Ton Voon released Nagios::Plugin::WWW::Mechanize some time ago... A really good helper for writing plugins that have to do web-browsing related things...

I used the module some time ago to write a couple of simple plugins, but these days I've been confronted with some trickier plugins, and Nagios::Plugin::WWW::Mechanize has been able to make me smile, because it has helped, and hasn't got in the way with the trickier bits.

So let me expose the tricky stuff that Nagios::Plugin::WWW::Mechanize let me do

Reading the headers of the response
Since the module let's you access the raw "mech" object, you can access the headers:

$np->mech->response->headers->as_string

Handling Gzipped content
WWW::Mechanize will announce that it accepts data encoded in gzip format... but when the server returns a gzipped body it will do nothing about it! It just returns the gzipped stream of bytes in the body of the response. So if you where pretending to do something with $np->content you get lot's of garbage.
So you start to wander around CPAN and the interweb, and you find: WWW::Mechanize::GZip. It looks promising, but you think: there is no way Nagios::Plugin::WWW::Mechanize will integrate... Well... You're wrong:

my $np = Nagios::Plugin::WWW::Mechanize->new(
  'mech' => WWW::Mechanize::GZip->new(autocheck => 0)
);
Ton left in your salvation... you can pass Nagios::Plugin::WWW::Mechanize an already constructed mech object!

Making Nagios::Plugin::WWW::Mechanize go through a proxy
And last, but not least... the plugin I was developing had to request a URL (f. ex. http://www.example.com/a/url) from another server (not the one that www.example.com resolves to). I found a pretty nasty solution browsing though perlmonks at first:

use LWP::UserAgent;
use LWP::Protocol::http;
push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, "PeerAddr" => "IP_OF_THE_WEB_SERVER");

I wouldn't recommend this way to trick WWW::Mechanize (which is an LWP::UserAgent) to contact another server... (I'm just documenting it for maybe further use ;)).
I finally found an elegant solution: LWP let's you define proxies!
my $proxy = $np->opts->proxy;
if (defined $proxy){
  $np->mech->proxy(['http', 'https'], $proxy);
}
You can also make any plugin that uses Nagios::Plugin::WWW:Mechanize proxy through another server just defining an environment variable:
http_proxy='http://PROXY_IP/' ./check_something_with_n_p_www_mech -url http://www.example.com/

I did find a couple of things that can maybe be be addressed.

  • when ->get(URL) fails the module calls nagios_exit(CRITICAL) without giving you a choice to do anything with the failure.
    Even if you wrap the call to get around an eval {}, the script exits. And the contents of the request are outputted. The thing is: Maybe you want to do something on failure
    eval {
      $np->get('http://x.x.x.x/url');
    }
    if ($@) {
      $np->nagios_exit('CRITICAL', "HTTP Status " . $np->....->status);
    }
    

    or even
    eval {
      $np->get('http://x.x.x.x/url');
    }
    if ($@) {
      $np->get('http://y.y.y.y/url');
    }
    # absolutely nothing bad happened...
    
    To solve this in a back-compatible way, I propose that get only call nagios_exit if the calling code is not wrapped in an eval ($^S will inform of this), else die, so the user can catch the exception (http://perldoc.perl.org/perlvar.html).
  • the plugins identify themselves as WWW::Mechanize in the User Agent
    Why not make them identify as pluginname/version. I had to do this in my script, but it would be nice that they do it automatically.
  • I had to use Nagios::Plugin; use Nagios::Plugin::WWW::Mechanize; To call
    $np->nagios_exit(CRITICAL, 'An error has occurred');

    If you don't use Nagios::Plugin, you wont get constants CRITICAL, WARNING, etc. imported into your namespace. Maybe Nagios::Plugin::WWW::Mechanize should do that too.