mirror of
https://github.com/librenms/librenms-agent.git
synced 2024-05-09 09:54:52 +00:00
Merge pull request #512 from VVelox/poudriere
add extend for monitoring Poudriere
This commit is contained in:
Executable
+433
@@ -0,0 +1,433 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
=head1 NAME
|
||||
|
||||
poudriere - LibreNMS JSON style SNMP extend for monitoring Poudriere
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
0.0.1
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
poudriere [B<-w>] [B<-b>] [B<-o> <cache base>]
|
||||
|
||||
poudriere --help|-h
|
||||
|
||||
poudriere --version|-v
|
||||
|
||||
=head1 SNMPD CONFIG
|
||||
|
||||
extend poudriere /etc/snmp/extends/poudriere -b
|
||||
|
||||
or if using cron...
|
||||
|
||||
extend poudriere cat /var/cache/poudriere.json.snmp
|
||||
|
||||
=head1 FLAGS
|
||||
|
||||
=head2 -w
|
||||
|
||||
Write the results out.
|
||||
|
||||
=head2 -b
|
||||
|
||||
Print out the compressed data if GZip+Base64 is smaller.
|
||||
|
||||
=head2 -o <cache base>
|
||||
|
||||
Where to write the results to. Defaults to '/var/cache/poudriere.json',
|
||||
meaning it will be written out to the two locations.
|
||||
|
||||
/var/cache/poudriere.json
|
||||
/var/cache/poudriere.json.snmp
|
||||
|
||||
The later is for use with returning data for SNMP. Will be compressed
|
||||
if possible.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use File::Slurp;
|
||||
use MIME::Base64;
|
||||
use IO::Compress::Gzip qw(gzip $GzipError);
|
||||
use Pod::Usage;
|
||||
use JSON;
|
||||
|
||||
sub time_to_seconds {
|
||||
my $time = $_[0];
|
||||
|
||||
if ( !defined($time) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( $time =~ /^0\:[0-9]+\.[0-9]+$/ ) {
|
||||
$time =~ s/^0\://;
|
||||
return $time;
|
||||
} elsif ( $time =~ /^[0-9]+\:[0-9]+\.[0-9]+$/ ) {
|
||||
my $minutes = $time;
|
||||
$minutes =~ s/\:.*//;
|
||||
$time =~ s/.*\://;
|
||||
$time = ( $minutes * 60 ) + $time;
|
||||
return $time;
|
||||
} elsif ( $time =~ /^[0-9]+D\:[0-9]+\:[0-9]+\.[0-9]+$/ ) {
|
||||
my $days = $time;
|
||||
$days =~ s/D\:.*$//;
|
||||
my $minutes = $time;
|
||||
$minutes =~ s/^.*D\://;
|
||||
$minutes =~ s/\:.*//;
|
||||
$time = ( $days * 86400 ) + ( $minutes * 60 ) + $time;
|
||||
return $time;
|
||||
}
|
||||
|
||||
# return 0 for anything unknown
|
||||
return 0;
|
||||
} ## end sub time_to_seconds
|
||||
|
||||
#the version of returned data
|
||||
my $VERSION = 1;
|
||||
|
||||
# ensure sbin is in the path
|
||||
$ENV{PATH} = $ENV{PATH} . ':/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin';
|
||||
|
||||
my $pretty;
|
||||
my $cache_base = '/var/cache/poudriere.json';
|
||||
my $write;
|
||||
my $compress;
|
||||
my $version;
|
||||
my $help;
|
||||
GetOptions(
|
||||
'o=s' => \$cache_base,
|
||||
w => \$write,
|
||||
b => \$compress,
|
||||
v => \$version,
|
||||
version => \$version,
|
||||
h => \$help,
|
||||
help => \$help,
|
||||
);
|
||||
|
||||
if ($version) {
|
||||
pod2usage( -exitval => 255, -verbose => 99, -sections => qw(VERSION), -output => \*STDOUT, );
|
||||
exit 255;
|
||||
}
|
||||
|
||||
if ($help) {
|
||||
pod2usage( -exitval => 255, -verbose => 2, -output => \*STDOUT, );
|
||||
exit 255;
|
||||
}
|
||||
|
||||
#the data to return
|
||||
my $to_return = {
|
||||
'version' => $VERSION,
|
||||
'error' => '0',
|
||||
'errorString' => '',
|
||||
};
|
||||
my $data = {
|
||||
status => '',
|
||||
build_info => '',
|
||||
not_done => 0,
|
||||
stats => {
|
||||
'copy-on-write-faults' => 0,
|
||||
'cpu-time' => 0,
|
||||
'data-size' => 0,
|
||||
'elapsed-times' => 0,
|
||||
'involuntary-context-switches' => 0,
|
||||
'job-control-count' => 0,
|
||||
'major-faults' => 0,
|
||||
'minor-faults' => 0,
|
||||
'percent-cpu' => 0,
|
||||
'percent-memory' => 0,
|
||||
'read-blocks' => 0,
|
||||
'received-messages' => 0,
|
||||
'rss' => 0,
|
||||
'sent-messages' => 0,
|
||||
'stack-size' => 0,
|
||||
'swaps' => 0,
|
||||
'system-time' => 0,
|
||||
'text-size' => 0,
|
||||
'threads' => 0,
|
||||
'user-time' => 0,
|
||||
'voluntary-context-switches' => 0,
|
||||
'written-blocks' => 0,
|
||||
'QUEUE' => 0,
|
||||
'BUILT' => 0,
|
||||
'FAIL' => 0,
|
||||
'SKIP' => 0,
|
||||
'IGNORE' => 0,
|
||||
'FETCH' => 0,
|
||||
'REMAIN' => 0,
|
||||
'TIME' => 0,
|
||||
'check-sanity' => 0,
|
||||
'pkg-depends' => 0,
|
||||
'fetch-depends' => 0,
|
||||
'fetch checksum' => 0,
|
||||
'extract-depends' => 0,
|
||||
'extract' => 0,
|
||||
'patch-depends' => 0,
|
||||
'patch' => 0,
|
||||
'build-depends' => 0,
|
||||
'lib-depends' => 0,
|
||||
'configure' => 0,
|
||||
'build' => 0,
|
||||
'run-depends' => 0,
|
||||
'stage' => 0,
|
||||
'package' => 0,
|
||||
},
|
||||
jailANDportsANDset => {}
|
||||
};
|
||||
|
||||
my @ps_stats = (
|
||||
'copy-on-write-faults', 'cpu-time', 'data-size', 'elapsed-times',
|
||||
'involuntary-context-switches', 'job-control-count', 'major-faults', 'minor-faults',
|
||||
'percent-cpu', 'percent-memory', 'read-blocks', 'received-messages',
|
||||
'rss', 'sent-messages', 'stack-size', 'swaps',
|
||||
'system-time', 'text-size', 'threads', 'user-time',
|
||||
'voluntary-context-switches',
|
||||
);
|
||||
|
||||
my @poudriere_stats = ( 'QUEUE', 'BUILT', 'FAIL', 'SKIP', 'IGNORE', 'FETCH', 'REMAIN', 'TIME' );
|
||||
|
||||
###
|
||||
###
|
||||
### get basic info via calling poudriere status
|
||||
###
|
||||
###
|
||||
|
||||
my $status_raw = `poudriere -N status -f 2> /dev/null`;
|
||||
if ( $? == 0 ) {
|
||||
$data->{status} = $status_raw;
|
||||
$data->{build_info} = `poudriere -N status -f -b 2>&1`;
|
||||
|
||||
my @status_split = split( /\n/, $status_raw );
|
||||
my $status_split_int = 1;
|
||||
while ( defined( $status_split[$status_split_int] ) ) {
|
||||
|
||||
my $jls;
|
||||
eval { $jls = decode_json(`jls --libxo json`); };
|
||||
if ($@) {
|
||||
$jls = { 'jail-information' => { jail => [] } };
|
||||
}
|
||||
|
||||
my $found = {
|
||||
'copy-on-write-faults' => 0,
|
||||
'cpu-time' => 0,
|
||||
'data-size' => 0,
|
||||
'elapsed-times' => 0,
|
||||
'involuntary-context-switches' => 0,
|
||||
'job-control-count' => 0,
|
||||
'major-faults' => 0,
|
||||
'minor-faults' => 0,
|
||||
'percent-cpu' => 0,
|
||||
'percent-memory' => 0,
|
||||
'read-blocks' => 0,
|
||||
'received-messages' => 0,
|
||||
'rss' => 0,
|
||||
'sent-messages' => 0,
|
||||
'stack-size' => 0,
|
||||
'swaps' => 0,
|
||||
'system-time' => 0,
|
||||
'text-size' => 0,
|
||||
'threads' => 0,
|
||||
'user-time' => 0,
|
||||
'voluntary-context-switches' => 0,
|
||||
'check-sanity' => 0,
|
||||
'pkg-depends' => 0,
|
||||
'fetch-depends' => 0,
|
||||
'fetch checksum' => 0,
|
||||
'extract-depends' => 0,
|
||||
'extract' => 0,
|
||||
'patch-depends' => 0,
|
||||
'patch' => 0,
|
||||
'build-depends' => 0,
|
||||
'lib-depends' => 0,
|
||||
'configure' => 0,
|
||||
'build' => 0,
|
||||
'run-depends' => 0,
|
||||
'stage' => 0,
|
||||
'package' => 0,
|
||||
};
|
||||
(
|
||||
$found->{SET}, $found->{PORTS}, $found->{JAIL}, $found->{BUILD}, $found->{STATUS},
|
||||
$found->{QUEUE}, $found->{BUILT}, $found->{FAIL}, $found->{SKIP}, $found->{IGNORE},
|
||||
$found->{FETCH}, $found->{REMAIN}, $found->{TIME}, $found->{LOGS}
|
||||
) = split( / +/, $status_split[$status_split_int], 14 );
|
||||
|
||||
if ( $found->{STATUS} ne 'done' ) {
|
||||
$data->{not_done} = 1;
|
||||
}
|
||||
|
||||
my $jailANDportsANDset;
|
||||
if ( $found->{SET} eq '-' ) {
|
||||
$jailANDportsANDset = $found->{JAIL} . '-' . $found->{PORTS};
|
||||
} else {
|
||||
$jailANDportsANDset = $found->{JAIL} . '-' . $found->{PORTS} . '-' . $found->{SET};
|
||||
}
|
||||
|
||||
foreach my $item (@poudriere_stats) {
|
||||
if ( $item eq 'TIME' ) {
|
||||
$found->{$item} = time_to_seconds( $found->{$item} );
|
||||
}
|
||||
$data->{stats}{$item} += $found->{$item};
|
||||
}
|
||||
|
||||
##
|
||||
## find the jails
|
||||
##
|
||||
my @jails;
|
||||
my $jail_regex = '^' . $jailANDportsANDset . '-job-[0-9]+';
|
||||
my $jls_int = 0;
|
||||
while ( defined( $jls->{'jail-information'}{jail}[$jls_int] ) ) {
|
||||
if ( $jls->{'jail-information'}{jail}[$jls_int]{hostname} eq $jailANDportsANDset
|
||||
|| $jls->{'jail-information'}{jail}[$jls_int]{hostname} =~ /$jail_regex/ )
|
||||
{
|
||||
push( @jails, $jls->{'jail-information'}{jail}[$jls_int]{jid} );
|
||||
}
|
||||
$jls_int++;
|
||||
}
|
||||
|
||||
##
|
||||
## if we have found jails, grab the information via ps
|
||||
##
|
||||
if ( defined( $jails[0] ) ) {
|
||||
my $jails_string = join( ',', @jails );
|
||||
|
||||
my $ps;
|
||||
eval {
|
||||
$ps
|
||||
= decode_json(
|
||||
`ps -o 'jid %cpu %mem rss cow dsiz etimes inblk jobc majflt minflt msgrcv msgsnd nivcsw nlwp nsigs nswap nvcsw oublk ssiz systime time tsiz usertime' --libxo json -J $jails_string`
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
$ps = { 'process-information' => { process => [] } };
|
||||
}
|
||||
my $ps_int = 0;
|
||||
while ( defined( $ps->{'process-information'}{process}[$ps_int] ) ) {
|
||||
foreach my $item (@ps_stats) {
|
||||
if ( $item eq 'user-time' || $item eq 'cpu-time' || $item eq 'system-time' ) {
|
||||
$ps->{'process-information'}{process}[$ps_int]{$item}
|
||||
= time_to_seconds( $ps->{'process-information'}{process}[$ps_int]{$item} );
|
||||
}
|
||||
$data->{stats}{$item} += $ps->{'process-information'}{process}[$ps_int]{$item};
|
||||
$found->{$item} += $ps->{'process-information'}{process}[$ps_int]{$item};
|
||||
}
|
||||
$ps_int++;
|
||||
} ## end while ( defined( $ps->{'process-information'}...))
|
||||
} ## end if ( defined( $jails[0] ) )
|
||||
|
||||
$data->{jailANDportsANDset}{$jailANDportsANDset} = $found;
|
||||
$status_split_int++;
|
||||
} ## end while ( defined( $status_split[$status_split_int...]))
|
||||
|
||||
my @build_info_split = split( /\n/, $data->{build_info} );
|
||||
my $current_section;
|
||||
foreach my $line (@build_info_split) {
|
||||
if ( $line =~ /^\[.*\]\ \[.*\] .*Queued.*Built/ ) {
|
||||
$current_section = $line;
|
||||
$current_section =~ s/^\[//;
|
||||
$current_section =~ s/\].*$//;
|
||||
} elsif ( $line =~ /^\[.*\].*\:.*\|.*\:/ ) {
|
||||
my $type;
|
||||
if ( $line =~ /[\ \t]check\-sanity[\ \t]/ ) {
|
||||
$type = 'check-sanity';
|
||||
} elsif ( $line =~ /[\ \t]pkg-depends[\ \t]/ ) {
|
||||
$type = 'pkg-depends';
|
||||
} elsif ( $line =~ /[\ \t]fetch-depends[\ \t]/ ) {
|
||||
$type = 'fetch-depends';
|
||||
} elsif ( $line =~ /[\ \t]fetch[\ \t]/ ) {
|
||||
$type = 'fetch';
|
||||
} elsif ( $line =~ /[\ \t]checksum[\ \t]/ ) {
|
||||
$type = 'checksum';
|
||||
} elsif ( $line =~ /[\ \t]extract\-depends[\ \t]/ ) {
|
||||
$type = 'extract-depends';
|
||||
} elsif ( $line =~ /[\ \t]extract[\ \t]/ ) {
|
||||
$type = 'extract';
|
||||
} elsif ( $line =~ /[\ \t]patch-depends[\ \t]/ ) {
|
||||
$type = 'patch-depends';
|
||||
} elsif ( $line =~ /[\ \t]lib\-depends[\ \t]/ ) {
|
||||
$type = 'lib-depends';
|
||||
} elsif ( $line =~ /[\ \t]configure[\ \t]/ ) {
|
||||
$type = 'configure';
|
||||
} elsif ( $line =~ /[\ \t]build[\ \t]/ ) {
|
||||
$type = 'build';
|
||||
} elsif ( $line =~ /[\ \t]build\-depends[\ \t]/ ) {
|
||||
$type = 'build-depends';
|
||||
} elsif ( $line =~ /[\ \t]lib\-depends[\ \t]/ ) {
|
||||
$type = 'lib-depends';
|
||||
} elsif ( $line =~ /[\ \t]configure[\ \t]/ ) {
|
||||
$type = 'configure';
|
||||
} elsif ( $line =~ /[\ \t]build[\ \t]/ ) {
|
||||
$type = 'build';
|
||||
} elsif ( $line =~ /[\ \t]run\-depends[\ \t]/ ) {
|
||||
$type = 'run-depends';
|
||||
} elsif ( $line =~ /[\ \t]stage[\ \t]/ ) {
|
||||
$type = 'stage';
|
||||
} elsif ( $line =~ /[\ \t]package[\ \t]/ ) {
|
||||
$type = 'package';
|
||||
}
|
||||
if (defined($type)) {
|
||||
$data->{stats}{$type}++;
|
||||
if (defined($data->{jailANDportsANDset}{$current_section})) {
|
||||
$data->{jailANDportsANDset}{$current_section}{$type}++;
|
||||
}
|
||||
}
|
||||
} ## end elsif ( $line =~ /^\[[0-9]+\].*\/.*\|.*-.*\:/)
|
||||
} ## end foreach my $line (@build_info_split)
|
||||
} else {
|
||||
$to_return->{error} = 1;
|
||||
$to_return->{errorString} = 'non-zero exit for "poudriere status -f"';
|
||||
}
|
||||
|
||||
###
|
||||
###
|
||||
### finalize it
|
||||
###
|
||||
###
|
||||
|
||||
#add the data has to the return hash
|
||||
$to_return->{data} = $data;
|
||||
|
||||
#finally render the JSON
|
||||
my $raw_json = encode_json($to_return);
|
||||
if ($write) {
|
||||
write_file( $cache_base, $raw_json );
|
||||
# compress and write to the cache file for it
|
||||
my $compressed_string;
|
||||
gzip \$raw_json => \$compressed_string;
|
||||
my $compressed = encode_base64($compressed_string);
|
||||
$compressed =~ s/\n//g;
|
||||
$compressed = $compressed . "\n";
|
||||
my $print_compressed = 0;
|
||||
if ( length($compressed) > length($raw_json) ) {
|
||||
write_file( $cache_base . '.snmp', $raw_json );
|
||||
} else {
|
||||
write_file( $cache_base . '.snmp', $compressed );
|
||||
$print_compressed = 1;
|
||||
}
|
||||
|
||||
if ( $compress && $print_compressed ) {
|
||||
print $compressed;
|
||||
} else {
|
||||
print $raw_json;
|
||||
}
|
||||
} else {
|
||||
if ( !$compress ) {
|
||||
print $raw_json. "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
# compress and write to the cache file for it
|
||||
my $compressed_string;
|
||||
gzip \$raw_json => \$compressed_string;
|
||||
my $compressed = encode_base64($compressed_string);
|
||||
$compressed =~ s/\n//g;
|
||||
$compressed = $compressed . "\n";
|
||||
my $print_compressed = 0;
|
||||
if ( length($compressed) > length($raw_json) ) {
|
||||
print $raw_json;
|
||||
} else {
|
||||
print $compressed;
|
||||
}
|
||||
} ## end else [ if ($write) ]
|
||||
Reference in New Issue
Block a user