#!/usr/bin/perl
# -----------------------------------------------------------------------------
#  check_devices_status : Nagios plugin for Stratus devices health check
# -----------------------------------------------------------------------------
#  Created by: Andriy Fomenko
#  Authors: Alex Titov, Alex Tsibulnik, Andriy Fomenko
#  QA by:
#  Copyright: videoNEXT Federal, Inc., 2015
# -----------------------------------------------------------------------------

use strict;
use warnings;
use Encode;
use Nagios::Plugin;
use Nagios::Plugin::Getopt;

my $np = Nagios::Plugin->new( usage => "Usage: %s [ -v|--verbose ]" );
$np->getopts;

my $APL=$ENV{APL} || $np->nagios_exit( UNKNOWN, 'APL environment variable is not defined' );

use SKM::DB;
use Master::Conf;
use Node::Conf ":all";

$np->nagios_exit( OK, 'skipped on slave node' ) if ! am_I_master();

# Try connecting to db
my $dbh;
eval {
    $dbh = DBMaster({ PrintError=>0, RaiseError=>1, FetchHashKeyName=>'NAME_uc' });
};
$np->nagios_exit( CRITICAL, 'Database is DOWN' ) if $@;

my ($verbose_notes, $alert_notes, $critical_notes);

if(0) { # TODO: process "verbose" parameter here
    device_status();
}

camera_status();
analytics_status();

$np->nagios_exit( CRITICAL, "Devices are in CRITICAL state\nCritical notes:\n${critical_notes}Warnings:$alert_notes" ) if $critical_notes;
$np->nagios_exit( WARNING, "Device warnings detected: $alert_notes" ) if $alert_notes;
$np->nagios_exit( OK, 'Devices are running normally' );

# -----------------------------------------------------------------------------

sub device_status {
    $verbose_notes.="Relays/sensors status\n";
    my ($cams_total, $sens_total, $rel_total) = (0, 0, 0);
    my $raCams;
    eval {
        $raCams = $dbh->selectall_arrayref(
            "select oa.val, count(o.obj)
            from _objs o inner join _obj_attr oa on o.obj=oa.obj
            where o.otype='D' and o.subtype='C'
            and o.deleted=0 and oa.attr='STATUS'
            group by oa.val",
            { Slice => [] }
        );
        $cams_total += $_->[1] foreach (@$raCams);

        my $raSens = $dbh->selectrow_arrayref(
            "select count(*) from _objs where otype='D' and subtype='S' and deleted=0",
            { Slice => [] }
        );
        my $raRel = $dbh->selectrow_arrayref(
            "select count(*) from _objs where otype='D' and subtype='R' and deleted=0",
            { Slice => [] }
        );
        $sens_total += $raSens->[0];
        $rel_total += $raRel->[0];
    };
    $np->nagios_exit( CRITICAL, 'Can not read devices info from DB' ) if $@;
    $verbose_notes .= "Cameras: $cams_total total\n";
    if(0) { # TODO: process "verbose" parameter here
        # $verbose_notes.= ArrayTable(['Camera status', 'Count'], $raCams) if $raCams;
    }
    $verbose_notes.="Sensors: $sens_total total\nRelays: $rel_total total\n";
}

sub camera_status {
    $verbose_notes.="Cameras status\n";
    my $ra;
    eval {
        $ra = $dbh->selectall_hashref(
            "select o.obj,o.name,o.node_ip as uni,a1.val as state, a2.val as status "
            ." from _objs o, _obj_attr a1, _obj_attr a2 "
            ."where otype='D' and subtype='C' and  o.obj=a1.obj and a1.attr='ARCHSTATE'"
            ."  and o.obj=a2.obj and a2.attr='STATUS' and deleted=0",
            'OBJ',
            {Slice=>[]}
        );
    };
    $np->nagios_exit( CRITICAL, 'Can not read cameras info from DB' ) if $@;
    my $broken=0;
    foreach my $obj( keys %$ra) {
        $broken++ if $ra->{$obj}->{STATUS}=~/^(BROKEN|DOWN|RESET\-FAILURE)$/;
    }
    if(! $broken) {
        $verbose_notes.="No broken cameras\n";
    } 
    elsif($broken < 4) { # show all broken cameras
        foreach  my $obj( keys %$ra) {
            my $dev=$ra->{$obj};
            next unless $dev->{STATUS}=~/^(BROKEN|DOWN|RESET\-FAILURE)$/;
            my $note=encode("utf8", "Camera #$obj ").$dev->{NAME}.encode("utf8", " is BROKEN");
            $note = decode("utf8", $note);
            $alert_notes.="$note\n";
        }
    } else {
        my $note="System has $broken broken cameras";
        $alert_notes.="$note\n";
    }
}


sub analytics_status {
    $verbose_notes.="Analytics status\n";

    my $ra;
    eval {
        $ra = $dbh->selectall_hashref(
            "select o.obj,o.name,o.node_ip as uni,a1.val as state, a2.val as vae, a3.val as vae_stat"
            ." from _objs o, _obj_attr a1, _obj_attr a2, _obj_attr a3 "
            ."where otype='D' and subtype='C' and deleted=0 and  o.obj=a1.obj and a1.attr='ARCHSTATE'"
            ."  and  o.obj=a2.obj and a2.attr='VAE_ACTIVE' and o.obj=a3.obj and a3.attr='STAT_VAE'",
            'OBJ',
            {Slice=>[]}
        );
    };
    $np->nagios_exit( CRITICAL, 'Can not read analytics info from DB' ) if $@;

    foreach my $obj (keys %$ra) {
        next unless $ra->{$obj}{STATE} eq 'on' and $ra->{$obj}{VAE} eq 'yes';
        my $stat = $ra->{$obj}{VAE_STAT};
        next unless $stat;
        $stat = eval { decode_json $stat };
        next if $@ or ref($stat) ne 'HASH';
        foreach my $name (keys %$stat) {
            my $trk = $stat->{$name};
            my ($state,$act,$min,$max,$cur)=($trk->{state},$trk->{active},$trk->{min_fps},$trk->{max_fps},$trk->{cur_fps});
            next if not defined $state or not defined $min or not defined $max or not defined $cur;
            my $avg = $min + int( ($max-$min) / 2);
            $act = 'no' unless $act;
            next if ($act ne 'yes');
            if ($state eq 'off') {
                $alert_notes.="Analyzer '$name' for camera [$obj] '$ra->{$obj}{NAME}' was stopped\n";
                last;
            }
            elsif ($state eq 'on' and $cur < $avg) {
                $alert_notes.="Analyzer '$name' for camera [$obj] '$ra->{$obj}{NAME}' lacks performance\n";
                last;
            }
        }
    }
}
