#!/usr/bin/perl -w
# -*- cperl -*-
=head1 NAME

  freeipmi - Multigraph-plugin to monitor sensors using FreeIPMI

=head1 CONFIGURATION
When used to monitor the local host, plugin config should define user root for direct ipmi access:
	[freeipmi]
		user root

=head2 ENVIRONMENT VARIABLES

When used to monitor a foreign host, this plugins use the variables
IPMI_USERNAME and IPMI_PASSWORD to log in on the remote system.

=head2 WILDCARD PLUGIN

When used for the local host, the plugin should be linked as
non-wildcard plugin, i.e., 'freeipmi', whereas when used to monitor a
foreign host it should be, e.g., 'freeipmi_192.168.0.253'.

=head1 DEPENDENCIES

The plugin requires FreeIPMI 1.1.5 or later to fetch the information.
Limits set on thresholds are available when using FreeIPMI 1.2.0 or
later.

=head1 AUTHOR

Diego Elio Pettenò <flameeyes@flameeyes.eu>.

With help and suggestions of:

Bart ten Brinke <info@retrosync.com>

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=auto
 #%# capabilities=autoconf

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=auto
 #%# capabilities=autoconf

=cut

use strict;
use Munin::Plugin;

$ENV{'LANG'} = "C"; # Force parsable output from sensors.
$ENV{'LC_ALL'} = "C"; # Force parsable output from sensors.
my $IPMISENSORS = $ENV{'ipmisensors'} || 'ipmi-sensors';

$0 =~ /freeipmi(?:_(.+))$/;
my $hostname = $1;

my $help_output = `$IPMISENSORS --help`;

$IPMISENSORS .= " --output-sensor-thresholds" if $help_output =~ /--output-sensor-thresholds/;
$IPMISENSORS .= " --quiet-cache --comma-separated-output --no-header-output --ignore-not-available-sensors --sensor-types=Temperature,Fan,Current,Voltage";
$IPMISENSORS .= " --hostname=$hostname" if defined($hostname);
$IPMISENSORS .= " --username=$ENV{IPMI_USERNAME}" if defined($ENV{IPMI_USERNAME});
$IPMISENSORS .= " --password=$ENV{IPMI_PASSWORD}" if defined($ENV{IPMI_PASSWORD});

my $output=`$IPMISENSORS 2>/dev/null`;
my $retval=$?;

if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
  if ($retval >= 1) {
    print "no (ipmi-sensors died)\n";
  } elsif ($retval == -1) {
    print "no (ipmi-sensors not found)\n";
  } elsif ($output eq "\n") {
    print "no (no compatible sensors)\n";
  } else {
    print "yes\n";
  }

  exit 0;
}

my %sensors = (
	       temp => {
			inputs => [],
			title => "Temperatures by IPMI",
			vlabel => "Degrees Celsius",
			graph_args => "--base 1000 -l 0",
		       },
	       fan => {
		       inputs => [],
		       title => "Fans speed by IPMI",
		       vlabel => "RPM",
		       graph_args => "--base 1000 -l 0",
		      },
	       power => {
			 inputs => [],
			 title => "Power by IPMI",
			 vlabel => "W",
			 graph_args => "--base 1000 -l 0",
			},
	       current => {
			 inputs => [],
			 title => "Current by IPMI",
			 vlabel => "A",
			 graph_args => "--base 1000 -l 0",
			},
	       voltage => {
			   inputs => [],
			   title => "Voltages by IPMI",
			   vlabel => "Volt",
			   graph_args => "--base 1000 --logarithmic",
			  },
	      );

my @data = split(/\n/, $output);
foreach my $line (@data) {
  my @dataline = split(/,/, $line);

  # skip values that are not useful
  next if $dataline[3] eq "N/A";

  my %sensor = (
		graphid => "ipmi" . $dataline[0],
		value => $dataline[3],
		label => $dataline[1]
	       );
  $sensor{lwarn} = (defined($dataline[7]) and $dataline[7] ne "N/A") ? $dataline[7] : '';
  $sensor{hwarn} = (defined($dataline[8]) and $dataline[8] ne "N/A") ? $dataline[8] : '';

  $sensor{lcrit} = (defined($dataline[6]) and $dataline[6] ne "N/A") ? $dataline[6] : '';
  $sensor{hcrit} = (defined($dataline[9]) and $dataline[9] ne "N/A") ? $dataline[9] : '';

  my $type;
  if ( $dataline[2] eq "Temperature" ) {
    $type = "temp";
  } elsif ( $dataline[2] eq "Fan" ) {
    $type = "fan"
  } elsif ( $dataline[2] eq "Current" and $dataline[4] eq "W" ) {
    $type = "power";
  } elsif ( $dataline[2] eq "Current" and $dataline[4] eq "A" ) {
    $type = "current";
  } elsif ( $dataline[2] eq "Voltage" ) {
    $type = "voltage";
  }

  push(@{$sensors{$type}->{inputs}}, \%sensor);
}


if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) {
  foreach my $type (keys %sensors) {
    # don't print anything if no value is found
    next if scalar(@{$sensors{$type}->{inputs}}) == 0;

    print "host_name $hostname" if defined($hostname);

    print <<END;

multigraph freeipmi_$type
graph_title $sensors{$type}->{title}
graph_vlabel $sensors{$type}->{vlabel}
graph_args $sensors{$type}->{graph_args}
graph_category sensors
END

    foreach my $sensor (@{$sensors{$type}->{inputs}}) {
      print "$sensor->{graphid}.label $sensor->{label}\n";

      print "$sensor->{graphid}.warning $sensor->{lwarn}:$sensor->{hwarn}\n"
	unless $sensor->{lwarn} eq '' and $sensor->{hwarn} eq '';
      print "$sensor->{graphid}.critical $sensor->{lcrit}:$sensor->{hcrit}\n"
	unless $sensor->{lcrit} eq '' and $sensor->{hcrit} eq '';
    }
  }

  unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) {
    exit 0;
  }
}

foreach my $type (keys %sensors) {
  # don't print anything if no value is found
  next if scalar(@{$sensors{$type}->{inputs}}) == 0;

  print "multigraph freeipmi_$type\n";

  foreach my $sensor (@{$sensors{$type}->{inputs}}) {
    print "$sensor->{graphid}.value $sensor->{value}\n";
  }
}
