#!/usr/bin/perl
#
# Copyright (c) 2017 SUSE LLC.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################

use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;
use YAML qw/LoadFile/;

BEGIN {
  unshift @::INC, '/usr/lib/obs/server';
}


if (! caller) {

  my $ret = eval {
    my $mcoe = Monitoring::Check::OBS::Events->new();
    $mcoe->getopt();
    $mcoe->get_config();
    $mcoe->check_dir_list();
    $mcoe->check_results();
  };

  if (! defined $ret) {
     print "UNKNOWN - $@";
     exit 3;
  }
}

package Monitoring::Check::OBS::Events;
our $VERSION = '0.0.1';
use strict;
use warnings;

use Getopt::Long qw/GetOptionsFromArray/;
use Data::Dumper;
use YAML qw/LoadFile/;
use Carp;

use BSConfiguration;

# ATTRIBUTES
sub critical     { return shift->_attribute(@_) } ## no critic (Subroutines::RequireArgUnpacking)
sub warning      { return shift->_attribute(@_) } ## no critic (Subroutines::RequireArgUnpacking)
sub config       { return shift->_attribute(@_) } ## no critic (Subroutines::RequireArgUnpacking)
sub config_file  { return shift->_attribute(@_) } ## no critic (Subroutines::RequireArgUnpacking)
sub results      { return shift->_attribute(@_) } ## no critic (Subroutines::RequireArgUnpacking)

# METHODS
sub new {
  my ($class,@args) = @_;
  my $self  =
    {
      critical    => 0,
      warning     => 0,
      config_file => '/etc/monitoring-plugins/check_obs_events.yml',
      results     => [],
      failed         => [],
      @args
    };
  bless $self, $class;
  return $self;
}

sub _attribute {
  my $self   = shift;
  my $value  = shift;
  my @caller = caller 1;
  my $attr   = $caller[3];
  $attr      =~ s/.*::([^:]*)$/$1/smx;
  if (defined $value) { $self->{$attr} = $value };
  return $self->{$attr};
}

sub failed {
  my ($self) = @_;
  $self->{failed} = [];
  @{$self->{failed}} = grep { $_->{state} > 0 } @{$self->results};
  @{$self->{failed}} = sort { $b->{state} <=> $a->{state} } @{$self->{failed}};
  return $self->{failed};
}

sub get_failed_max_state {
  my ($self) = @_;
  return $self->{failed}->[0]->{state} || 0;
}

sub getopt {
  my ($self) = @_;

  if (! GetOptionsFromArray(\@ARGV, 'warning|w=i' => \$self->{warning}, 'critical|c=i' => \$self->{critical}, 'config=s' => \$self->{config_file} )) {
    my $bn = $0;
    $bn =~ s{.*/(.*)$}{$1}smx;
    print "Usage: $bn <-w warning> <-c critical> <--dir events_subdir>\n";
    die "Error: Wrong arguments!\n";
  }

  return $self;
}

sub get_config {
  my ($self) = @_;
  my $cfile = $self->config_file;
  die "Config file '$cfile' not found!\n" if (! -f $cfile);
  return $self->config(LoadFile($cfile));
}

sub check_dir_list {
  my ($self) = @_;
  # cleanup results
  $self->results([]);
  my $cfg = $self->config;
  foreach my $dir (@{$cfg->{directories_to_check} || []}) {
	  $self->check_dir($dir);
  }
  return $self->failed();
}

sub check_dir {
  my ($self, $dir) = @_;

  my $events_dir = "$BSConfig::bsdir/events";

  my $critical      = (defined($dir->{critical})) ? $dir->{critical} : $self->critical;
  my $warning       = (defined($dir->{warning})) ? $dir->{warning} : $self->warning;
  my $my_events_dir = "$events_dir/$dir->{dir}";
  my $state=0;
  my $diff=0;

  opendir my $dh, $my_events_dir or die "Error  while opening '$my_events_dir': $!\n";
  my @all_files = readdir $dh;
  closedir $dh;
  my @events = grep { ! m/^[.]/smx } @all_files;
  my $ev     = scalar @events;

  if ($ev >= $warning)  { $state = 1; $diff = $ev - $warning  }
  if ($ev >= $critical) { $state = 2; $diff = $ev - $critical }

  push @{$self->results},
    {
      state    => $state,
      perfdata => "'$dir->{dir}'=$ev;$warning;$critical",
      diff     => $diff,
      name     => $dir->{dir},
      events   => $ev
    }
  ;

  return;
}

sub check_results {
  my ($self) = @_;
  my $state = $self->get_failed_max_state();

  if ($state) {
    print $self->format_output_fail($state);
    exit $state
  } else {
    print $self->format_output_ok();
    exit 0;
  }
}

sub format_output_fail {
  my ($self, $state) = @_;
  my $sname = { 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN'};
  croak('State "'.($state || q{}).'" not known') if ! $state;
  my $out="$sname->{$state} - ";

  for my $res (sort { $b->{state} <=> $a->{state} or $b->{diff} <=> $a->{diff} } @{$self->results} ) {
    next if ! $res->{state};
    $out = "$out $res->{name} ($res->{state}/$res->{events}/$res->{diff})";
  }

  return "$out | ".$self->get_perfdata;
}

sub format_output_ok {
  my ($self) = @_;
  return 'OK - all eventdirs checked | '.$self->get_perfdata."\n";

}

sub get_perfdata {
  my ($self) = @_;
  my @perfdata;
  for my $res (@{$self->{results}}) {
     push @perfdata, $res->{perfdata};
  }
  return join '; ', @perfdata;
}
1;
