323 lines
8.6 KiB
Perl
Executable File
323 lines
8.6 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
# --------------------------------------------------------------
|
|
# IPMI Sensors parser for Unix
|
|
# Used as Plugin in Pandora FMS Monitoring System
|
|
# Written by Robert Nelson <robertn@the-nelsons.org> 2015
|
|
# Licensed under BSD Licence
|
|
# --------------------------------------------------------------
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Getopt::Std;
|
|
use File::Copy;
|
|
|
|
|
|
$Getopt::Std::STANDARD_HELP_VERSION = 1;
|
|
$Getopt::Std::OUTPUT_HELP_VERSION = \*STDERR;
|
|
|
|
my $TMP_DIR = "/tmp";
|
|
|
|
our $VERSION = "0.9.0";
|
|
|
|
sub VERSION_MESSAGE($) {
|
|
my ($fh) = @_;
|
|
|
|
print $fh "$0 Version $VERSION\n";
|
|
}
|
|
|
|
sub HELP_MESSAGE($) {
|
|
my ($fh) = @_;
|
|
|
|
print $fh "usage: $0 (-l | -h <hostname> -u <username> -p <password>) [-g <modulegroup>] [-a <agent_name> -d <pool_path>]\n";
|
|
print $fh "\nThis agent plugin can be used for local or remote IPMI monitoring\n";
|
|
print $fh "For local monitoring:\n";
|
|
print $fh "-l\n";
|
|
print $fh "For remote monitoring:\n";
|
|
print $fh "-h\tIPMI host name or IP address\n";
|
|
print $fh "-u\tIPMI user name\n";
|
|
print $fh "-p\tIPMI password\n";
|
|
print $fh "In either case the module can be assigned to a module group using:\n";
|
|
print $fh "-g\tModule group (must already exist in PandoraFMS)\n\n";
|
|
print $fh "-a\tAgent name, requires also -d option\n";
|
|
print $fh "-d\tLocal directory pool for agent data files\n";
|
|
print $fh "-D\tdriver to be used\n";
|
|
}
|
|
|
|
sub exec_program($) {
|
|
use IPC::Open3;
|
|
use IO::Select;
|
|
|
|
my $cmd = shift;
|
|
|
|
my $pid = open3(\*CHILD_STDIN, \*CHILD_STDOUT, \*CHILD_STDERR, $cmd);
|
|
|
|
close(CHILD_STDIN);
|
|
|
|
my $sel = new IO::Select(\*CHILD_STDOUT, \*CHILD_STDERR);
|
|
|
|
my ($child_stdout, $child_stderr) = ('', '');
|
|
|
|
while (my @ready = $sel->can_read(60)) {
|
|
foreach my $h (@ready)
|
|
{
|
|
my ($buf, $count);
|
|
if ($h eq \*CHILD_STDERR)
|
|
{
|
|
$count = sysread(CHILD_STDERR, $buf, 4096);
|
|
if ($count > 0) {
|
|
$child_stderr .= $buf;
|
|
} else {
|
|
$sel->remove(\*CHILD_STDERR);
|
|
}
|
|
} else {
|
|
$count = sysread(CHILD_STDOUT, $buf, 4096);
|
|
if ($count > 0) {
|
|
$child_stdout .= $buf;
|
|
} else {
|
|
$sel->remove(\*CHILD_STDOUT);
|
|
}
|
|
}
|
|
}
|
|
my @active_handles = $sel->handles;
|
|
if ($#active_handles < 0) {
|
|
last;
|
|
}
|
|
}
|
|
|
|
waitpid($pid, 1);
|
|
|
|
my $retval = $? >> 8;
|
|
|
|
return ($retval, $child_stdout, $child_stderr);
|
|
}
|
|
|
|
my %options;
|
|
|
|
if (!getopts('lh:u:p:g:a:d:', \%options)) {
|
|
exit 1;
|
|
}
|
|
|
|
if (defined $options{'l'}) {
|
|
if (defined $options{'h'} || defined $options{'u'} || defined $options{'p'} || defined $options{'v'}) {
|
|
print STDERR "Option -l can't be used with -h, -u, -p or -v\n";
|
|
HELP_MESSAGE(\*STDERR);
|
|
exit 1;
|
|
}
|
|
} elsif (!defined $options{'h'} || !defined $options{'u'} || !defined $options{'p'}) {
|
|
print STDERR "Either -l or all of -h, -u and -p must be specified\n";
|
|
HELP_MESSAGE(\*STDERR);
|
|
exit 1;
|
|
}
|
|
if ( ( defined $options{'a'} && !defined $options{'d'} ) || ( !defined $options{'a'} && defined $options{'d'} ) ) {
|
|
print STDERR "Arguments -a and -d must be specified together\n";
|
|
HELP_MESSAGE(\*STDERR);
|
|
exit 1;
|
|
}
|
|
|
|
my $host_name = $options{'h'};
|
|
my $user_name = $options{'u'};
|
|
my $user_password = $options{'p'};
|
|
my $module_group = $options{'g'};
|
|
my $agent_name = $options{'a'};
|
|
my $dest_dir = $options{'d'};
|
|
my $driver = $options{'D'};
|
|
|
|
|
|
# Map Sensor type to module type and thresholds
|
|
# 0 = numeric, record has thresholds
|
|
# 1 = simple flag, 0 normal, > 0 critical
|
|
# 2 = complex flags, for now ignore alert settings
|
|
# 3 = string or unknown
|
|
|
|
my %sensor_types = (
|
|
'Temperature' => 0,
|
|
'Voltage' => 0,
|
|
'Current' => 0,
|
|
'Fan' => 0,
|
|
'Physical Security' => 1,
|
|
'Platform Security Violation Attempt' => 1,
|
|
'Processor' => 2,
|
|
'Power Supply' => 2,
|
|
'Power Unit' => 2,
|
|
'Cooling Device' => 0,
|
|
'Other Units Based Sensor' => 0,
|
|
'Memory' => 2,
|
|
'Drive Slot' => 3,
|
|
'POST Memory Resize' => 3,
|
|
'System Firmware Progress' => 1,
|
|
'Event Logging Disabled' => 2,
|
|
'Watchdog 1' => 2,
|
|
'System Event' => 2,
|
|
'Critical Interrupt' => 1,
|
|
'Button Switch' => 2,
|
|
'Module Board' => 3,
|
|
'Microcontroller Coprocessor' => 3,
|
|
'Add In Card' => 3,
|
|
'Chassis' => 3,
|
|
'Chip Set' => 3,
|
|
'Other Fru' => 3,
|
|
'Cable Interconnect' => 3,
|
|
'Terminator' => 3,
|
|
'System Boot Initiated' => 2,
|
|
'Boot Error' => 1,
|
|
'OS Boot' => 2,
|
|
'OS Critical Stop' => 1,
|
|
'Slot Connector' => 2,
|
|
'System ACPI Power State' => 2,
|
|
'Watchdog 2' => 2,
|
|
'Platform Alert' => 2,
|
|
'Entity Presence' => 2,
|
|
'Monitor ASIC IC' => 3,
|
|
'LAN' => 2,
|
|
'Management Subsystem Health' => 1,
|
|
'Battery' => 2,
|
|
'Session Audit' => 3,
|
|
'Version Change' => 3,
|
|
'FRU State' => 3,
|
|
'OEM Reserved' => 3
|
|
);
|
|
|
|
my $command = 'ipmi-sensors';
|
|
if (defined $driver){
|
|
$command .= " -D $driver "
|
|
}
|
|
if (defined $host_name) {
|
|
$command .= " -h $host_name -u $user_name -p $user_password -l user";
|
|
}
|
|
$command .= ' --ignore-not-available-sensors --no-header-output --comma-separated-output --non-abbreviated-units --output-sensor-thresholds --output-event-bitmask';
|
|
|
|
my ($retval, $stdout, $stderr) = exec_program($command);
|
|
|
|
|
|
my $module_list = "";
|
|
|
|
if ($retval == 0) {
|
|
my ($module_name, $module_type, $module_warn_min, $module_warn_max, $module_warn_invert, $module_critical_min, $module_critical_max, $module_critical_invert);
|
|
|
|
foreach my $line (split(/\n/, $stdout)) {
|
|
my ($sensor, $name, $type, $value, $units, $lowerNR, $lowerC, $lowerNC, $upperNC, $upperC, $upperNR, $eventmask) = split(/,/, $line);
|
|
|
|
$module_name = "$type: $name";
|
|
|
|
my ($module_warn_min, $module_warn_max, $module_warn_invert, $module_critical_min, $module_critical_max, $module_critical_invert);
|
|
|
|
my $sensor_type = $sensor_types{$type};
|
|
if ($sensor_type == 0) {
|
|
$module_type = 'generic_data';
|
|
if ($lowerC ne 'N/A' and $upperC ne 'N/A') {
|
|
if ($lowerC <= $upperC) {
|
|
$module_critical_min = $lowerC;
|
|
$module_critical_max = $upperC;
|
|
} else {
|
|
$module_critical_min = $upperC;
|
|
$module_critical_max = $lowerC;
|
|
}
|
|
$module_critical_invert = '1';
|
|
}
|
|
if ($lowerNC ne 'N/A' and $upperNC ne 'N/A') {
|
|
if ($lowerNC <= $upperNC) {
|
|
$module_warn_min = $lowerNC;
|
|
$module_warn_max = $upperNC;
|
|
} else {
|
|
$module_warn_min = $upperNC;
|
|
$module_warn_max = $lowerNC;
|
|
}
|
|
$module_warn_invert = '1';
|
|
}
|
|
} elsif ($sensor_type == 1) {
|
|
$module_type = 'generic_data';
|
|
$module_critical_min = '1';
|
|
$module_critical_max = '0';
|
|
} elsif ($sensor_type == 2) {
|
|
$module_type = 'generic_data';
|
|
} elsif ($sensor_type == 3) {
|
|
$module_type = 'generic_data_string';
|
|
} else {
|
|
$module_type = 'generic_data_string';
|
|
}
|
|
|
|
$module_list .= "<module>\n";
|
|
$module_list .= " <name><![CDATA[$module_name]]></name>\n";
|
|
$module_list .= " <type><![CDATA[$module_type]]></type>\n";
|
|
$module_list .= " <module_group><![CDATA[$module_group]]></module_group>\n" if defined $module_group;
|
|
if ($value eq 'N/A') {
|
|
if ($eventmask =~ /([0-9A-Fa-f]+)h/) {
|
|
$value = hex $1;
|
|
} else {
|
|
$value = $eventmask;
|
|
}
|
|
}
|
|
$module_list .= " <data><![CDATA[$value]]></data>\n";
|
|
$module_list .= " <unit><![CDATA[$units]]></unit>\n" if ($units ne 'N/A');
|
|
$module_list .= " <min_warning>$module_warn_min</min_warning>\n" if defined $module_warn_min;
|
|
$module_list .= " <max_warning>$module_warn_max</max_warning>\n" if defined $module_warn_max;
|
|
$module_list .= " <warning_inverse>$module_warn_invert</warning_inverse>\n" if defined $module_warn_invert;
|
|
$module_list .= " <min_critical>$module_critical_min</min_critical>\n" if defined $module_critical_min;
|
|
$module_list .= " <max_critical>$module_critical_max</max_critical>\n" if defined $module_critical_max;
|
|
$module_list .= " <critical_inverse>$module_critical_invert</critical_inverse>\n" if defined $module_critical_invert;
|
|
$module_list .= "</module>\n";
|
|
}
|
|
|
|
if (defined ($agent_name)){
|
|
|
|
my $xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
|
|
|
# print header
|
|
$xml .= "<agent_data ";
|
|
$xml .= "agent_name='" . $agent_name . "'";
|
|
$xml .= ">\n";
|
|
|
|
# add content
|
|
$xml .= $module_list;
|
|
|
|
# print tail
|
|
$xml .= "</agent_data>\n";
|
|
|
|
# dump xml content to file
|
|
my $file_name = $agent_name."_".time().".data";
|
|
my $file_path = $TMP_DIR . "/" . $file_name;
|
|
#Creating XML file in temp directory
|
|
|
|
if ( -e $file_path ) {
|
|
sleep (1);
|
|
$file_name = $agent_name . "_" . time().".data";
|
|
$file_path = $TMP_DIR . "/" . $file_name;
|
|
}
|
|
|
|
open (FD, ">>", $file_path);
|
|
|
|
my $bin_opts = ':raw:encoding(UTF8)';
|
|
|
|
if ($^O eq "Windows") {
|
|
$bin_opts .= ':crlf';
|
|
}
|
|
|
|
binmode(FD, $bin_opts);
|
|
|
|
print FD $xml;
|
|
|
|
close (FD);
|
|
|
|
# transfer file
|
|
my $rc = copy($file_path, $dest_dir);
|
|
|
|
#If there was no error, delete file
|
|
if ($rc == 0) {
|
|
print STDERR "There was a problem copying local file to $dest_dir: $!\n";
|
|
} else {
|
|
unlink ($file_path);
|
|
}
|
|
|
|
|
|
}
|
|
else {
|
|
print $module_list;
|
|
}
|
|
|
|
} else {
|
|
print STDERR "ipmi_sensors: Error Executing - $command\n";
|
|
print STDERR $stderr;
|
|
exit 1;
|
|
}
|