#!/usr/bin/perl -w
use strict; # $Id: alertgrid_nginx 54 2016-09-14 15:31:32Z abalama $


=head1 NAME

alertgrid_nginx - MonM alertgrid Nginx status getter (http_stub_status_module)

=head1 VERSION

Version 1.00

=head1 SYNOPSIS

    alertgrid_nginx [-dX] [-t SECS] [-x EXPIRES] [-q URI] [COMMAND] [ARGUMENTS]
    
    alertgrid_nginx [OPTIONS] clients [active accepted handled rejected requests]
    
    alertgrid_nginx [OPTIONS] socket [reading writing waiting]

    Type alertgrid_nginx -H for more information

=head1 DESCRIPTION

Nginx status getter (http_stub_status_module) for alertgrid system

=head2 COMMANDS

=over

=item B<clients>

Getting clients statistic only

=item B<socket>

Getting sockeet statistic only

=item B<default>

Getting all counts

=back

=head2 OPTIONS

=over

=item B<-d, --debug>

Enable debug mode

=item B<-X, --noxml>

Output all data "as is"

=item B<-t SECS, --timeout=SECS>

Timeout of requests in seconds (default = 5)

=item B<-x EXPIRES, --expires=EXPIRES>

Time of life data in special "expires"-format (+5m as default).

See L<MonM::AlertGrid/expire_calc>

=item B<-q URI, --query=URI>

Host (default = http://localhost/server-status)

=back

=head1 EXAMPLES

    alertgrid_nginx
    alertgrid_nginx clients
    alertgrid_nginx socket

=head1 DEPENDENCES

L<CTK::Util>

=head1 TODO

See C<TODO> file

=head1 AUTHOR

Serz Minus (Lepenkov Sergey) L<http://www.serzik.com> E<lt>minus@mail333.comE<gt>.

=head1 COPYRIGHT

Copyright (C) 1998-2015 D&D Corporation. All Rights Reserved

=head1 LICENSE

This program is distributed under the GNU GPL v3.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

See C<LICENSE> file

=cut

use LWP::Simple;
use Try::Tiny;
use XML::Simple;
use CTK::Util;

use Getopt::Long;
use Pod::Usage;
use constant {
        TIMEOUT     => 5,
        RETRIES     => 5,
        URIDEF      => 'http://localhost/server-status',
        COMMUNITY   => 'public',
        EXPIRES     => '+5m',
    };

use vars qw/$VERSION/;
$VERSION = '1.00';

BEGIN { sub exception{ print "ERROR"; die sprintf("[%s GMT] ERROR %s",scalar(gmtime(time())),(@_ ? @_ : ''))} }
Getopt::Long::Configure("bundling");
my %OPT;

GetOptions(\%OPT,
    "help|usage|h|?",
    "man|m|H",
    "debug|d",      # Debug mode: 0/1
    "noxml|X",      # Output as text
    
    "timeout|time|t=i",     # Timeout (default = 1000000)
    "expires|x=s",          # Expires (+5m as default)
    "query|uri|url|q=s",    # URI (default = http://localhost/server-status)
    
) || pod2usage(-exitval => 1, -verbose => 0);
pod2usage(-exitval => 0, -verbose => 0) if $OPT{help};
pod2usage(-exitval => 0, -verbose => 2) if $OPT{man};
my @args = @ARGV ? @ARGV : (); # Arguments
my $cmd = lc(shift(@args) || 'default');

my $debug   = $OPT{debug} || 0;
my $uri     = $OPT{query} || URIDEF;
my $timeout = $OPT{timeout} || TIMEOUT;
my $expires = $OPT{expires} || EXPIRES;

binmode STDOUT, ':raw:utf8'; # binmode STDOUT;
require Data::Dumper if $OPT{noxml};

$LWP::Simple::ua->timeout($timeout);

my $content = get($uri);
unless ($content) {
    print _xml_output(
            count => {
                name    => 'nginx::test',
                expires => [ $expires ],
                status  => [ 'ERROR' ],
                error   => { code    => 1, content => 
                        sprintf("Connect failed to %s", $uri),
                    },
                value   => { type    => 'STR', content => '', },
            },
    );
    exit 0;
}
my $test = {
            name    => 'nginx::test',
            expires => [ $expires ],
            status  => [ 'OK' ],
            error   => { code    => 0, content => '', },
            value   => { type    => 'STR', content => length($content), },
        };

my %stubstatus;
if ($content =~ /(\d+).+?(\d+).+?(\d+).+?(\d+).+?(\d+).+?(\d+).+?(\d+)/s) {
    %stubstatus = (
	    name        => 'stub',
            active      => $1,
            accepted    => $2,
            handled     => $3,
            requests    => $4,
            reading     => $5,
            writing     => $6,
            waiting     => $7,
            rejected    => $3-$2,
            ping        => time-$^T,
        );
} else {
    print _xml_output(
            count => {
                name    => 'nginx::status',
                expires => [ $expires ],
                status  => [ 'ERROR' ],
                error   => { code    => 1, content => 
                        sprintf("Can't parse data from %s", $uri),
                    },
                value   => { type => 'STR', content => $content, },
            },
    );
    exit 0;
}

if ($cmd eq 'clients') {
    delete @stubstatus{qw/reading writing waiting/};
    push @args, 'name';
    if (@args) {
        foreach my $v (keys %stubstatus) {
            delete $stubstatus{$v} unless grep {$v eq lc($_)} @args;
        }
    }

    if ($OPT{noxml}) {
        print Data::Dumper::Dumper(\%stubstatus);
    } else {
        print _xml_output(
                count => [$test,
                    {
                        name    => 'nginx::clients',
                        expires => [ $expires ],
                        status  => [ 'OK' ],
                        error   => { code    => 0, content => '', },
                        value   => { type    => 'TAB', record  =>  [\%stubstatus], },
                    },
                ],
            );
    }


} elsif ($cmd eq 'socket') {
    delete @stubstatus{qw/active accepted handled requests rejected/};
    push @args, 'name';
    if (@args) {
        foreach my $v (keys %stubstatus) {
            delete $stubstatus{$v} unless grep {$v eq lc($_)} @args;
        }
    }

    if ($OPT{noxml}) {
        print Data::Dumper::Dumper(\%stubstatus);
    } else {
        print _xml_output(
                count => [$test,
                    {
                        name    => 'nginx::socket',
                        expires => [ $expires ],
                        status  => [ 'OK' ],
                        error   => { code    => 0, content => '', },
                        value   => { type    => 'TAB', record  =>  [\%stubstatus], },
                    },
                ],
            );
    }

} elsif ($cmd eq 'default') {

    if ($OPT{noxml}) {
        print Data::Dumper::Dumper(\%stubstatus);
    } else {
        print _xml_output(
                count => [$test,
                    {
                        name    => 'nginx',
                        expires => [ $expires ],
                        status  => [ 'OK' ],
                        error   => { code    => 0, content => '', },
                        value   => { type    => 'TAB', record  =>  [\%stubstatus], },
                    },
                ],
            );
    }

} else {
    pod2usage(-exitval => 1, -verbose => 0);
}

exit 0;

sub _xml_output {
    my %xmldata = @_;
    my $xmlout = XMLout(\%xmldata,
            RootName => 'response', 
            XMLDecl  => '<?xml version="1.0" encoding="utf-8"?>',
            NoEscape => 1,
        );
    return $xmlout
}

1;
__END__

CI=`/usr/local/bin/lwp-request http://$ADDR/server-status | grep "Active connections" | awk '{print $3}'`
CO=`/usr/local/bin/lwp-request http://$ADDR/server-status | grep "Waiting" | awk '{print $6}'`

active      current number of active connections                              
accepted    number of connection accepted by nginx (since server start)        accept';
handled     number of connections handled by nginx ( = accepts - rejected )     ,    ,    ,    
rejected    number of connection rejected by nginx (since server start)         
requests    number of requests processed                                      .  keep-alive       
reading     current number of connections reading request                        .
writing     current number of connections writing response                       .
waiting     current number of connections on waiting state                  keep-alive       
ping        time of test's GET-request   

if ($cmd eq 'get') {
    my $param = shift(@args) || 'SNMPv2-MIB::sysDescr.0'; # .1.3.6.1.2.1.1.1.0
    my $obj = '';
    my $val = '';
    
    if ($strictm) {
        $obj = [$param];
    } elsif ($param =~ /^\.?(\d+\.)*\d+$/) {
        if ($param =~ /\.0$/) {
            $obj = [$param];
        } else {
            $obj = new SNMP::Varbind([$param,0]);
        }
    } else {
        $param .= '.0' unless ($param =~ /\.0$/);
        $obj = [$param];
    }
    $val = $snmp->get($obj);
    $val = '' unless defined $val;
    #exception printf("SNMP Error [%d]: %s\n", $snmp->{ErrorNum}, $snmp->{ErrorStr}) if $snmp->{ErrorNum};
    if ($OPT{noxml}) {
        print $val;
    } else {
        print STDOUT _xml_output(
                count => [
                    {
                        name    => $param,
                        expires => [ $expires ],
                        status  => [ $snmp->{ErrorNum} ? 'ERROR' : 'OK' ],
                        error   => {
                                code    => $snmp->{ErrorNum} ? $snmp->{ErrorNum} : 0,
                                content => $snmp->{ErrorStr} ? cdata($snmp->{ErrorStr}) : '',
                            },
                        value   => {
                                type    => ($val =~ /^[+\-]\d+$/) ? 'DIG' : 'STR',
                                content => cdata($val),
                            },
                    },
                    $test,
                ],
            );
    }

