my ($self) = @_;
my $reader = Config::General->new($self->config_file);
my %config = $reader->getall;
+
return \%config;
}
sub gather_all {
my ($self) = @_;
my %report;
+
+ log_trace { "gather_all() has been invoked" };
for my $spec (@{ $self->introspectors }) {
my ($base, $args) = @$spec;
my $module = "System::Introspector::Probe::$base";
- log_trace { "Using '$module' for this gather" };
- $report{$base} = use_module($module)
- ->new($args)
- ->gather;
+ log_debug { "Using '$module' for this gather" };
+ my $moduleName = use_module($module);
+ log_trace { "Finished loading '$module'; returned value was '$moduleName'" };
+ my $instance = $moduleName->new($args);
+ log_trace { "Finished constructing '$moduleName'" };
+ $report{$base} = $instance->gather;
+ log_trace { "Finished invocation of '$moduleName->gather()'" };
}
+
+ log_trace { "gather_all() has completed" };
+
return \%report;
}
sub _new_direct {
my ($class, $remote, $args) = @_;
+
+ log_trace {"Using direct gatherer with '$remote'" };
+
return $class->new::on($remote, $args || {});
}
sub _new_bridged {
my ($class, $bridge, $remote, $args) = @_;
+
+ log_trace { "Building bridged gatherer for '$remote'" };
+
return System::Introspector::Gatherer::Bridge->new::on($bridge,
remote_spec => $remote,
remote_class => $class,
use Object::Remote;
use Object::Remote::Future;
use Moo;
-use System::Introspector::Logger::Remote qw( set_logger );
has remote_spec => (is => 'ro', required => 1);
has remote_class => (is => 'ro', required => 1);
sub _build_remote {
my ($self) = @_;
- my $logger = System::Introspector::Logger::Remote->new({ env_prefix => 'SYSTEM_INTROSPECTOR_LOG' });
-
- #TODO: this doesn't work because it's not respected on the
- #remote side
- set_logger($logger);
-
+
return $self->remote_class
->new::on($self->remote_spec, $self->remote_args);
}
package System::Introspector::Logger;
use base qw(Log::Contextual);
-use System::Introspector::Logger::Local;
+use System::Introspector::Logger::Output;
+use Log::Contextual qw( set_logger );
-sub arg_default_logger { $_[1] || System::Introspector::Logger::Local->new({
+sub arg_default_logger { $_[1] || System::Introspector::Logger::Output->new({
env_prefix => 'SYSTEM_INTROSPECTOR_LOG',
}) };
+++ /dev/null
-use strictures 1;
-
-package System::Introspector::Logger::Local;
-
-use base qw ( Log::Contextual::WarnLogger );
-
-sub _log {
- my $self = shift;
- my $level = shift;
- my $message = join( "\n", @_ );
- my @timedata = localtime;
- my $time = sprintf("%0.2i:%0.2i:%0.2i", $timedata[2], $timedata[1], $timedata[0]);
- $message .= "\n" unless $message =~ /\n$/;
- warn "[local $level $time] $message";
-}
-
-1;
-
--- /dev/null
+use strictures 1;
+
+package System::Introspector::Logger::Output;
+
+use base qw ( Log::Contextual::WarnLogger );
+
+sub _log {
+ my $self = shift;
+ my $level = shift;
+ my $message = join( "\n", @_ );
+ my @timedata = localtime;
+ my $time = sprintf("%0.2i:%0.2i:%0.2i", $timedata[2], $timedata[1], $timedata[0]);
+ $message .= "\n" unless $message =~ /\n$/;
+ our ($hostname);
+ #this is just a stub for right now - the configured hostname and the hostname the
+ #system reports don't have to match - the user would be expecting the configured name
+ $hostname = scalar(`hostname`) unless defined $hostname;
+ chomp($hostname);
+ warn "[$level $time] [$hostname] $message";
+}
+
+1;
+
+++ /dev/null
-use strictures 1;
-
-BEGIN { $ENV{SYSTEM_INTROSPECTOR_PROBE_LOG_UPTO} = "TRACE" unless exists $ENV{SYSTEM_INTROSPECTOR_PROBE_LOG_UPTO} };
-
-package System::Introspector::Logger::Probe;
-
-use base qw(System::Introspector::Logger);
-use System::Introspector::Logger::Remote;
-
-sub arg_package_logger { $_[1] || System::Introspector::Logger::Remote->new({
- env_prefix => 'SYSTEM_INTROSPECTOR_PROBE_LOG',
-}) };
-
-1;
\ No newline at end of file
+++ /dev/null
-use strictures 1;
-
-package System::Introspector::Logger::Remote;
-
-use base qw ( System::Introspector::Logger::Local );
-
-sub new {
- my ($class, $args) = @_;
- my $spec = delete($args->{remote_spec});
- my $self = $class->SUPER::new($args);
-
- die "must specify 'remote_spec' of machine in arguments to new()" unless defined $spec;
-
- $self->{si}->{remote_spec} = $spec;
-
- return $self;
-
-}
-
-sub _log {
- my $self = shift;
- my $level = shift;
- my $message = join( "\n", @_ );
- my $time = localtime;
- my $remote = $self->{si}->{remote_spec};
- $message .= "\n" unless $message =~ /\n$/;
- warn "[remote:$remote $level $time] $message";
-}
-
-1;
-
package System::Introspector::Probe::DiskUsage;
use Moo;
+#this probe is a point of potential reliability issues
+#df is prone to hang for long periods of time when faced with
+#errors on the devices that back filesystems it's trying to
+#report on
+
use System::Introspector::Util qw(
lines_from_command
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
+
with 'System::Introspector::Role::Probe';
sub gather {
my ($self) = @_;
+ log_debug { "Starting to gather disk usage information" };
return transform_exceptions {
+ log_trace { "Invoking df command" };
my @lines = lines_from_command ['df', '-aP'];
+ log_trace { "df command has finished executing" };
shift @lines; # header
my @rows;
for my $line (@lines) {
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
+
with 'System::Introspector::Role::Probe';
has lsof_command => (is => 'ro', default => sub { 'lsof' });
sub gather {
my ($self) = @_;
+ log_debug { "Gathering open file information" };
return transform_exceptions {
+ log_trace { "Invoking lsof" };
my @lines = lines_from_command [$self->_lsof_command_call];
+ log_trace { "lsof has finished executing" };
my @handles;
for my $line (@lines) {
chomp $line;
($1, $2);
} @fields };
}
+ log_trace { "Completed gathering open file information" };
return { handles => \@handles };
};
}
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
+
with 'System::Introspector::Role::Probe';
sub gather {
my ($self) = @_;
-
+ log_debug { "Collecting group membership information" };
return transform_exceptions {
my %group;
my $fh = $self->_open_group_file;
+ log_trace { "Reading group file" };
while (defined( my $line = <$fh> )) {
chomp $line;
next if !(length $line);
users => $users,
};
}
-
+ log_trace { "Completed gathering group information" };
return { groups => \%group };
};
}
sub _open_group_file {
my ($self) = @_;
+ log_trace { "opening /etc/group" };
return handle_from_file '/etc/group';
}
output_from_file
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
with 'System::Introspector::Role::Probe';
sub gather {
my ($self) = @_;
+ log_debug { "Starting to gather host information" };
return transform_exceptions {
return {
hostname => $self->_gather_hostname,
my %uname;
for my $field (@UnameFields) {
(my $option = $field) =~ s{_}{-}g;
+ log_trace { "Invoking uname with option of '--$option'" };
my $value = output_from_command [uname => "--$option"];
chomp $value;
$uname{ $field } = $value;
sub _gather_hostname {
my ($self) = @_;
+ log_trace { sprintf("Loading hostname info from '%s'", $self->hostname_file) };
my $hostname = output_from_file $self->hostname_file;
chomp $hostname;
$hostname =~ s{(?:^\s+|\s+$)}{}g;
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
+
with 'System::Introspector::Role::Probe';
has hosts_file => (
sub gather {
my ($self) = @_;
my $file = $self->hosts_file;
+ log_debug { "Starting to gather contents of hosts file" };
+ log_trace { "Using contents of '$file' as system host list" };
return {
hosts_file => transform_exceptions {
return {
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
+
+with 'System::Introspector::Role::Probe';
+
has root => (
is => 'ro',
default => sub { '/' },
sub gather {
my ($self) = @_;
+ log_debug { "Gathering Perl library information" };
return transform_exceptions {
my $pipe = $self->_open_locate_libdirs_pipe;
my %libdir;
return { modules => $self->_gather_libdir_info($line) };
};
}
+ log_trace { "Completed gathering Perl library information" };
return { libdirs_perl => \%libdir };
};
}
sub _open_locate_pm_pipe {
my ($self, $libdir) = @_;
+ log_debug { "Executing 'find' to search for Perl module files in '$libdir'" };
return handle_from_command
sprintf q{find %s -name '*.pm'}, $libdir;
}
my $root = $self->root;
$root .= '/'
unless $root =~ m{/$};
+ log_debug { "Executing 'locate' to identify Perl 5 library directories" };
return handle_from_command sprintf
q{locate --regex '^%s.*lib/perl5$'}, $root;
}
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
+
with 'System::Introspector::Role::Probe';
sub gather {
my ($self) = @_;
+
+ log_debug { "Starting to gather system mount point configuration" };
+
return {
mtab => transform_exceptions {
return { entries
sub _open_fh {
my ($self, $file) = @_;
+ log_debug { "Opening '$file' to use as mount point configuration source" };
return handle_from_file $file;
}
sub _parse_tab_fh {
my ($self, $fh) = @_;
my @mounts;
+ log_trace { "Reading contents of tab file" };
while (defined( my $line = <$fh> )) {
chomp $line;
next if $line =~ m{^\s*$}
},
};
}
+ log_trace { "Finished reading contents of tab file" };
no warnings 'uninitialized';
return [ sort {
($a->{device_name} cmp $b->{device_name})
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
+
+with 'System::Introspector::Role::Probe';
+
sub gather {
my ($self) = @_;
+ log_debug { "Gathering Nagios mk agent info" };
return transform_exceptions {
+ log_trace { "Collecting check_mk_agent output" };
my @lines = $self->_get_check_mk_agent_output;
+ log_trace { "Done with check_mk_agent " };
chomp @lines;
my %plugin;
my $current;
next unless $current;
push @$current, $line;
}
+ log_trace { "Completed Nagios mk agent gather" };
return { nagios_check_mk_agent => \%plugin };
};
}
files_from_dir
);
+use System::Introspector::Logger qw ( :log );
+
+with 'System::Introspector::Role::Probe';
+
has apt_lists_dir => (is => 'ro', builder => 1);
has apt_update_after => (is => 'ro', default => sub { 86400 });
has apt_update => (is => 'ro');
sub gather {
my ($self) = @_;
- return {
+ log_debug { "Starting to gather apt based package information" };
+ my $retval = {
update => {
last => $self->_last_apt_update,
run => transform_exceptions {
return { config => $self->_gather_sources };
},
};
+ log_trace { "Completed gathering apt based package information" };
+ return $retval;
}
sub _last_apt_update {
my $threshold = $self->apt_update_after;
my $last_change = $self->_last_apt_update;
return 'no'if ($last_change + $threshold) > time;
+ log_trace { "invoking apt-get update" };
handle_from_command 'apt-get update';
return 'yes';
}
sub _open_dpkg_query_pipe {
my ($self) = @_;
+ log_trace { "invoking dpkg-query" };
return handle_from_command 'dpkg-query --show';
}
sub _open_apt_get_upgrade_simulation_pipe {
my ($self) = @_;
+ log_trace { "invoking apt-get simulated upgrade" };
return handle_from_command 'apt-get -s upgrade';
}
sub _gather_sources {
my ($self) = @_;
my $sources_dir = $self->apt_sources_dir;
+ log_trace { "Gathering apt package source data from directory '$sources_dir'" };
return {
'sources_list' => $self->_fetch_source_list($self->apt_sources),
'sources_list_dir' => (-e $sources_dir) ? transform_exceptions {
fail
);
+use System::Introspector::Logger qw( :log );
+
has root => (
is => 'ro',
default => sub { '/' },
sub gather {
my ($self) = @_;
+ log_debug { "Gathering installed Perl information" };
return transform_exceptions {
my @configs = $self->_find_possible_perl_configs;
my %found;
$found{$config} = $info
if defined $info;
}
+ log_trace { "Completed with Perl searches" };
return { perls => \%found };
};
}
transform_exceptions
);
+use System::Introspector::Logger qw ( :log );
# args is automatically included, since it has to be last
my @Included = qw(
sub gather {
my ($self) = @_;
my @names = (@Included, 'args');
+
+ log_debug { "Starting to gather running process information" };
return transform_exceptions {
my $pipe = $self->_open_ps_pipe;
sub _open_ps_pipe {
my ($self) = @_;
+ log_trace { "Invoking ps" };
return handle_from_command sprintf
'ps -eo %s',
join(',', @Included, 'args');
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
+
with 'System::Introspector::Role::Probe';
has classes_file => (
sub gather {
my ($self) = @_;
+
+ log_debug { "Starting to gather Puppet information" };
+
return {
classes => $self->_gather_classes,
resources => $self->_gather_resources,
sub _gather_resources {
my ($self) = @_;
return transform_exceptions {
+ log_trace { sprintf "Reading contents of resource file '%s'", $self->resources_file };
my @lines = output_from_file $self->resources_file;
chomp @lines;
return [ map {
sub _gather_classes {
my ($self) = @_;
return transform_exceptions {
+ log_trace { sprintf "Reading contents of classes file '%s'", $self->classes_file };
my @lines = output_from_file $self->classes_file;
chomp @lines;
return \@lines;
lines_from_command
);
+use System::Introspector::Logger qw( :log );
+
+with 'System::Introspector::Role::Probe';
+
has root => (
is => 'ro',
default => sub { '/' },
sub gather {
my ($self) = @_;
+ log_debug { "Gathering data on Git repos" };
return transform_exceptions {
my $pipe = $self->_open_locate_git_config_pipe;
my %location;
sub _gather_git_info {
my ($self, $config) = @_;
+ log_debug { "Found git repo config file '$config'" };
return {
config_file => $config,
config => transform_exceptions {
sub _find_tracking {
my ($self, $dir) = @_;
local $ENV{GIT_DIR} = $dir;
+ log_trace { "Invoking 'git for-each-ref' to find tracking info for '$dir'" };
my @lines = lines_from_command
['git', 'for-each-ref',
'--format', q{OK %(refname:short) %(upstream:short)},
return { __error__ => "No remote" }
unless defined $remote;
local $ENV{GIT_DIR} = $dir;
+ log_trace { "Invoking 'git log' local:'$local' remote:'$remote'" };
my @lines = lines_from_command
['git', 'log', '--oneline', "$remote..$local"];
return scalar @lines;
return { __error__ => "No remote" }
unless defined $remote;
local $ENV{GIT_DIR} = $dir;
+ log_trace { "Invoking 'git diff' local:'$local' remote:'$remote'" };
my @lines = lines_from_command
['git', 'diff', '--name-only', $local, $remote];
return \@lines;
sub _open_git_config_pipe {
my ($self, $config) = @_;
+ log_trace { "Invoking 'git config' for file '$config'" };
return handle_from_command "git config --file $config --list";
}
sub _open_locate_git_config_pipe {
my ($self) = @_;
(my $root = $self->root) =~ s{/$}{};
+ log_trace { "Invoking locate to find git repository location" };
return handle_from_command sprintf
q{locate --regex '^%s/.*\\.git/config$'}, $root;
}
output_from_file
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
with 'System::Introspector::Role::Probe';
return {
resolv_conf_file => transform_exceptions {
my $file = $self->resolv_conf_file;
- return {
- file_name => $file,
- body => scalar output_from_file $file,
- };
+ log_debug { sprintf("Starting to gather host resolver configuration from '$file'") };
+ my $buf = scalar output_from_file $file;
+ log_trace { "Finished reading contents of file" };
+ return { file_name => $file, body => $buf };
},
};
}
transform_exceptions
);
+use System::Introspector::Logger qw( :log );
+
with 'System::Introspector::Role::Probe';
has sudoers_file => (
sub gather {
my ($self) = @_;
+ log_debug { my $hostname = $self->hostname; chomp($hostname); sprintf("Gathering sudo data with file:'%s' hostname:'%s' ", $self->sudoers_file, $hostname) };
my %file = $self->_gather_files($self->sudoers_file);
+ log_trace { "Completed gathering sudo data" };
return \%file;
}
sub _gather_files {
my ($self, $file) = @_;
+ log_trace { "gathering sudo information from file '$file'" };
my $result = transform_exceptions {
my @lines = output_from_file $file;
my @result = ({ body => join '', @lines });
handle_from_file
);
+use System::Introspector::Logger qw( :log );
+
with 'System::Introspector::Role::Probe';
has passwd_file => (is => 'ro', default => sub { '/etc/passwd' });
sub gather {
my ($self) = @_;
+ log_debug { "Gathering operating system user info from " . $self->passwd_file };
return transform_exceptions {
my $fh = $self->_open_passwd_fh;
my %user;
+ log_trace { "file is now open, starting to read" };
while (defined( my $line = <$fh> )) {
my $data = $self->_deparse_htpasswd_line($line);
my $user = $data->{username};
};
$user{ $data->{username} } = $data;
}
+ log_trace { "completed reading user information" };
return { users => \%user };
};
}
sub _gather_crontab {
my ($self, $user) = @_;
+ log_trace { "Invoking crontab to get info for user '$user'" };
my ($out, $err, $ok) = output_from_command
['crontab', '-u', $user, '-l'];
+ log_trace { "crontab has finished executing" };
unless ($ok) {
return {}
if $err =~ m{^no crontab}i;
sub _gather_ssh_keys {
my ($self, $user, $home) = @_;
my $ssh_dir = "$home/.ssh";
- my $ssh_authkeys = "$ssh_dir/authorized_keys";
- return {
- files => {},
- authorized => { file_name => $ssh_authkeys, body => '' }
- } unless -d $ssh_dir;
+ my $ssh_authkeys = "$ssh_dir/authorized_keys";
+
+ log_trace { "Checking ssh keys for user '$user'" };
+
+ unless(-d $ssh_dir) {
+ log_trace { "User has no ssh directory" };
+ return {
+ files => {},
+ authorized => { file_name => $ssh_authkeys, body => '' }
+ }
+ }
my %key;
for my $item (files_from_dir $ssh_dir) {
next unless $item =~ m{\.pub$};
body => scalar output_from_file $ssh_authkeys,
};
}) : { file_name => $ssh_authkeys, body => '' };
+ log_trace { "Finished with user ssh keys" };
return { files => \%key, authorized => $auth_keys };
}
sub _gather_user_groups {
my ($self, $user) = @_;
+ log_trace { "Gathering group membership for user '$user'" };
my $groups = output_from_command [groups => $user];
chomp $groups;
$groups =~ s{^ .* : \s* }{}x;
+ log_trace { "Completed gathering group membership data" };
return [split m{\s+}, $groups];
}
for my $group (@groups) {
my @waiting;
for my $host ($self->config->hosts) {
- log_trace { "Adding group '$group' on '$host' to waiting list" };
+ log_debug { "Adding group '$group' on '$host' to list of active fetches" };
push @waiting, [$host, $self->fetch($host, $group)];
}
- log_debug { sprintf("Waiting for results to become available for %i groups", scalar(@waiting)) };
+ log_debug { sprintf("There are %i hosts in the waiting list", scalar(@waiting)) };
for my $wait (@waiting) {
my ($host, @futures) = @$wait;
- log_trace { "Waiting for futures on host '$host'" };
+ log_debug { "Waiting on futures for host '$host'" };
my @data = await_all @futures;
log_trace { "Received all from group '$group' on '$host'" };
push(@{ $spec->{$_}{sudo} ? \@sudo : \@nosudo}, [$_, $spec->{$_}])
for sort keys %$spec;
my @futures;
- log_debug { sprintf("Fetching results for '%s': sudo:%i nosudo:%i", $host, scalar(@sudo), scalar(@nosudo)) };
+ log_info { "Probing host '$host' with " . join ", ", map $_->[0], @nosudo, @sudo };
if (@nosudo) {
- log_trace { "Preparing to fetch without sudo: " . join ", ", map $_->[0], @nosudo };
+ log_debug { "Preparing to fetch without sudo: " . join ", ", map $_->[0], @nosudo };
my $proxy = $self->_create_gatherer(
host => $host,
introspectors => [@nosudo],
push @futures, $proxy->start::gather_all;
}
if (@sudo) {
- log_trace { "Preparing to fetch with sudo: ", join ", ", map $_->[0], @nosudo };
+ log_debug { "Preparing to fetch with sudo: ", join ", ", map $_->[0], @nosudo };
my $proxy = $self->_create_gatherer(
sudo => 1,
host => $host,
use File::Spec;
use Scalar::Util qw( blessed );
use Capture::Tiny qw( capture_stderr );
-use Log::Contextual qw( :log );
our @EXPORT_OK = qw(
handle_from_command