#!perl

use JSON qw(decode_json encode_json);
use strict;
use warnings;
use Getopt::Long       qw(GetOptions);
use OSLV::Monitor      ();
use MIME::Base64       qw(encode_base64);
use IO::Compress::Gzip qw(gzip);
use File::Slurp        qw(read_file write_file);
use Pod::Usage         qw(pod2usage);
use Time::HiRes        qw(gettimeofday);

=head1 NAME

oslv_monitor - LibreNMS style JSON SNMP extend for OS level virtualization monitoring

=head1 VERSION

1.0.4

=cut

our $VERSION = '1.0.4';

=head1 SYNOPSIS

oslv_monitor [B<-c>] [B<-f> <config file>] [B<-b> <base dir>] [B<-q>] [B<-t> <time divider>]

=head1 DESCRIPTION

For cron...

    */5 * * * * /usr/local/bin/oslv_monitor -q

For snmpd...

    extend oslv_monitor /bin/cat /var/cache/oslv_monitor/snmp

Debugging info can be printed via OSLV_MONITOR_DEBUG=1.

=head1 FLAGS

=head2 -c

Compress the output using gzip and base64 encoded so it
can be transmitted via SNMP with out issue.

/var/cache/oslv_monitor/snmp will always be available and compressed.

=head2 -f <file>

The config file to use.

Default :: /usr/local/etc/oslv_monitor.json

=head2 -q

Do not print the results.

=head2 -t <time divider>

What to use for the time divider.

=head1 CONFIG FILE

The following keys are used in the JSON config file.

    - include :: A array of regular expressions to include.
        Default :: ["^.*$"]

    - exclude :: A array of regular expressions to exlclude.
        Default :: undef

    - backend :: Override the the backend and automatically choose it.

    - time_divider :: Override the time_divider value. The default value varies
        per backend and if it is needed.

Time divider notes.

    - cgroups :: While the default for usec to sec conversion should be 1000000,
              some settings report the value in nanoseconds, requiring 1000000000.
        Default :: 1000000

    - FreeBSD :: not used

By Defaults the backends are as below.

    FreeBSD: FreeBSD
    Linux: cgroups

Default would be like this.

    {
        "include": ["^.*$"]
    }

=cut

sub main::VERSION_MESSAGE {
	print "oslv_monitor v. $VERSION\n";
}

sub main::HELP_MESSAGE {
	pod2usage( -exitval => 255, -verbose => 2, -output => \*STDOUT, );
}

# get the commandline options
my $help     = 0;
my $version  = 0;
my $base_dir = '/var/cache/oslv_monitor';
my $quiet;
my $backend;
my $exclude;
my $include;
my $time_divider;
my $config_file = '/usr/local/etc/oslv_monitor.json';
Getopt::Long::Configure('no_ignore_case');
Getopt::Long::Configure('bundling');
GetOptions(
	't=s'     => \$time_divider,
	'version' => \$version,
	'v'       => \$version,
	'help'    => \$help,
	'h'       => \$help,
	'q'       => \$quiet,
	'f=s'     => \$config_file,
	'b=s'     => \$base_dir,
);

if ($version) {
	&main::VERSION_MESSAGE;
	exit 1;
}

if ($help) {
	&main::HELP_MESSAGE;
	exit 1;
}

if ( -f $config_file ) {
	if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
		warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': reading in config file "' . $config_file . '"' );
	}
	eval {
		my $raw_config    = read_file($config_file);
		my $parsed_config = decode_json($raw_config);
		if ( !defined($backend) && defined( $parsed_config->{backend} ) && ref( $parsed_config->{backend} ) eq '' )
		{
			$backend = $parsed_config->{backend};
		}
		if (  !defined($time_divider)
			&& defined( $parsed_config->{time_divider} )
			&& ref( $parsed_config->{time_divider} ) eq '' )
		{
			$time_divider = $parsed_config->{time_divider};
		}
		if ( defined( $parsed_config->{include} ) && ref( $parsed_config->{include} ) eq 'ARRAY' ) {
			$include = $parsed_config->{include};
		}
		if ( defined( $parsed_config->{exclude} ) && ref( $parsed_config->{exclude} ) eq 'ARRAY' ) {
			$exclude = $parsed_config->{exclude};
		}
	};
	if ($@) {
		die( 'Failed to read the config "' . $config_file . '" ... ' . $@ );
	}
} ## end if ( -f $config_file )

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': initing module' );
}

my $monitor = OSLV::Monitor->new(
	backend      => $backend,
	base_dir     => $base_dir,
	include      => $include,
	exclude      => $exclude,
	time_divider => $time_divider
);

if ( !-d $base_dir ) {
	if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
		warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': creating "' . $base_dir . '"' );
	}
	mkdir($base_dir) || die( 'Unable to mkdir "' . $base_dir . '"' );
}

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': calling load' );
}

eval { $monitor->load; };
if ($@) {
	print encode_json( { version => 1, data => {}, error => 1, errorString => 'load failed... ' . $@ } ) . "\n";
	exit 1;
}

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': calling run' );
}

my $data = encode_json( $monitor->run );

if ( !$quiet ) {
	print $data. "\n";
}

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': writing out "' . $base_dir . '/json"' );
}

write_file( $base_dir . '/json', $data . "\n" );

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': writing out "' . $base_dir . '/json"' );
}

my $toReturnCompressed;
gzip \$data => \$toReturnCompressed;
my $compressed = encode_base64($toReturnCompressed);
$compressed =~ s/\n//g;
$compressed = $compressed . "\n";

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': writing out "' . $base_dir . '/snmp"' );
}

write_file( $base_dir . '/snmp', $compressed );

if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
	warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': done' );
}

exit 0;
