package OSLV::Monitor;

use 5.006;
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
use Time::HiRes  qw(gettimeofday);

=head1 NAME

OSLV::Monitor - OS level virtualization monitoring extend for LibreNMS.

=head1 VERSION

Version 1.0.4

=cut

our $VERSION = '1.0.4';

=head1 SYNOPSIS

Quick summary of what the module does.

Perhaps a little code snippet.

    use OSLV::Monitor;

    my $monitor = OSLV::Monitor->new();

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

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

    print $data."\n";


=head2 METHODS

Inits the object.

One option is taken and that is a hash ref.

    # init with the cbsd backend
    my $monitor->new(backend=>'FreeBSD');

The keys are list as below.

    - backend :: The name of the backend to use. If undef, it is choosen based on $^O
        freebsd :: FreeBSD
        linux :: cgroups

    - base_dir :: The dir to use for any caches as well as output.
        Default :: /var/cache/oslv_monitor

    - include :: An array of regexps to match names against for seeing if they should
            be included or not. By default everything is return.
        - Default :: ['^.*$']

    - exclude :: An array of regexps to exclude. By default this array is empty. It is
            processed after the include array.
        - Default :: []

    - time_divider :: What to pass to the backend for the time_divider for that if needed.
        Default :: 1000000

=cut

sub new {
	my ( $blank, %opts ) = @_;

	if ( !defined( $opts{time_divider} ) ) {
		$opts{time_divider} = 1000000;
	} else {
		if ( !looks_like_number( $opts{time_divider} ) ) {
			die('time_divider is not a number');
		}
	}

	if ( !defined( $opts{backend} ) ) {
		$opts{backend} = 'FreeBSD';
		if ( $^O eq 'freebsd' ) {
			$opts{backend} = 'FreeBSD';
		} elsif ( $^O eq 'linux' ) {
			$opts{backend} = 'cgroups';
		}
	}

	if ( !defined( $opts{include} ) ) {
		my @include = ('^.+$');
		$opts{include} = \@include;
	} else {
		if ( !defined( $opts{include}[0] ) ) {
			$opts{include}[0] = '^.+$';
		}

		my $int = 0;
		while ( defined( $opts{include}[$int] ) ) {
			if ( ref( $opts{include}[$int] ) ne '' ) {
				die( 'ref for $opts{include}[' . $int . '] is ' . ref( $opts{include}[$int] ) . ' and not ""' );
			}
			$int++;
		}
	} ## end else [ if ( !defined( $opts{include} ) ) ]

	if ( !defined( $opts{exclude} ) ) {
		my @exclude;
		$opts{exclude} = \@exclude;
	} else {
		my $int = 0;
		while ( defined( $opts{exclude}[$int] ) ) {
			if ( ref( $opts{exclude}[$int] ) ne '' ) {
				die( 'ref for $opts{exclude}[' . $int . '] is ' . ref( $opts{exclude}[$int] ) . ' and not ""' );
			}
			$int++;
		}
	}

	if ( !defined( $opts{base_dir} ) ) {
		$opts{base_dir} = '/var/cache/oslv_monitor';
	}

	if ( !-d $opts{base_dir} ) {
		mkdir( $opts{base_dir} ) || die( $opts{base_dir} . ' was not a directory and could not be created' );
	}

	my $self = {
		time_divider => $opts{time_divider},
		version      => 1,
		backend      => $opts{backend},
		base_dir     => $opts{base_dir},
		include      => $opts{include},
		exclude      => $opts{exclude},
	};
	bless $self;

	return $self;
} ## end sub new

=head2 load

This loads the specified backend.

    eval{ $monitor->load; };
    if ( $@ ){
        print "Failed to load the backend... ".$@;
    }

=cut

sub load {
	my $self = $_[0];

	my $loaded = 0;

	my $backend_test;
	my $usable;
	my $test_string = '
use OSLV::Monitor::Backends::' . $self->{backend} . ';
$backend_test=OSLV::Monitor::Backends::'
		. $self->{backend} . '->new(base_dir=>$self->{base_dir}, obj=>$self, time_divider=>$self->{time_divider});
$usable=$backend_test->usable;
';
	if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
		warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': test string "' . $test_string . '"' );
	}
	eval($test_string);

	if ($usable) {
		$self->{backend_mod} = $backend_test;
		$loaded = 1;
	} else {
		die( 'Failed to load backend... ' . $@ );
	}

	return $loaded;
} ## end sub load

=head2 run

Runs the poller backend and report the results.

If nothing is nothing is loaded, load will be called.

    my $status=$monitor->run;

=cut

sub run {
	my $self = $_[0];

	if ( !defined( $self->{backend_mod} ) ) {
		return {
			version     => $self->{version},
			data        => {},
			error       => 1,
			errorString => 'No module loaded',
		};
	}

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

	my $to_return_data;
	#	eval { $to_return_data = $self->{backend_mod}->run };
	$to_return_data = $self->{backend_mod}->run;
	if ($@) {
		return {
			version     => $self->{version},
			data        => {},
			error       => 1,
			errorString => 'Failed to run backend... ' . $@,
		};
	}

	$to_return_data->{backend} = $self->{backend};

	return {
		version     => $self->{version},
		data        => $to_return_data,
		error       => 0,
		errorString => ''
	};
} ## end sub run

sub include {
	my $self = $_[0];
	my $name = $_[1];

	# any of these means it is not meaningfully usable or an error
	if ( !defined($name) ) {
		if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
			warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': include name is undef returning 0' );
		}
		return 0;
	} elsif ( ref($name) ne '' ) {
		if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
			warn(     'DEBUG, '
					. join( '.', gettimeofday )
					. ': include name ref is not "" but "'
					. ref($name)
					. '" returning 0' );
		}
		return 0;
	} elsif ( $name eq '' ) {
		if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
			warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': include name is "" returning 0' );
		}
		return 0;
	}

	if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
		warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': include name being tested is "' . $name . '"' );
	}

	# look for mathcing includes
	foreach my $item ( @{ $self->{include} } ) {
		# check if it matches
		if ( $name =~ /$item/ ) {
			if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
				warn(     'DEBUG, '
						. join( '.', gettimeofday )
						. ': include name matched include /'
						. $item
						. '/ testing for exlcudes now' );
			}
			# if we got a match check for excludes
			foreach my $item ( @{ $self->{exclude} } ) {
				if ( $name =~ /$item/ ) {
					return 0;
				} elsif ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
					warn(     'DEBUG, '
							. join( '.', gettimeofday )
							. ': include name did not match exclude /'
							. $item
							. '/' );
				}
			} ## end foreach my $item ( @{ $self->{exclude} } )
			# if we get here it should means a include matched and no excludes matched
			if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
				warn(     'DEBUG, '
						. join( '.', gettimeofday )
						. ': include name matched a include and no exlcudes returning 1' );
			}
			return 1;
		} elsif ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
			warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': include name did not match include /' . $item . '/' );
		}

	} ## end foreach my $item ( @{ $self->{include} } )

	if ( $ENV{'OSLV_MONITOR_DEBUG'} ) {
		warn( 'DEBUG, ' . join( '.', gettimeofday ) . ': include name did not match any includes' );
	}

	# if we get here it should mean no includes matched
	return 0;
} ## end sub include

=head1 AUTHOR

Zane C. Bowers-Hadley, C<< <vvelox at vvelox.net> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-oslv-monitor at rt.cpan.org>, or through
the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=OSLV-Monitor>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc OSLV::Monitor


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker (report bugs here)

L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=OSLV-Monitor>

=item * CPAN Ratings

L<https://cpanratings.perl.org/d/OSLV-Monitor>

=item * Search CPAN

L<https://metacpan.org/release/OSLV-Monitor>

=back


=head1 ACKNOWLEDGEMENTS


=head1 LICENSE AND COPYRIGHT

This software is Copyright (c) 2024 by Zane C. Bowers-Hadley.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)


=cut

1;    # End of OSLV::Monitor
