diff --git a/plugins/sensors/upsmonpro_ b/plugins/sensors/upsmonpro_ new file mode 100644 index 00000000..cdb3083a --- /dev/null +++ b/plugins/sensors/upsmonpro_ @@ -0,0 +1,207 @@ +#!/usr/bin/perl + +=head1 NAME + +upsmonpro_ - Munin plugin to monitor Powercom UPS via UPSMON PRO program L + +=head1 INSTALLATION + + /etc/munin/plugins/upsmonpro_load -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_status -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_temp -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_voltage -> /usr/share/munin/plugins/upsmonpro_ + +=head1 CONFIGURATION + +Environment variables: + + host - UPSMON PRO server host, default localhost + port - UPSMON PRO port, default 2601 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 AUTHOR + +Copyright (C) 2017 pru.mike@gmail.com + +=head1 LICENSE + +GPLv2. + +=cut + +use strict; +use warnings; +use feature qw/say/; +use Munin::Plugin; +use IO::Socket::INET; +use Time::HiRes qw/usleep/; +use English qw/-no-math-vars/; +our $VERSION = '0.0.1'; +$OUTPUT_AUTOFLUSH++; + +our $DEFAULT_HOST = 'localhost'; +our $DEFAULT_PORT = 2601; +our %TYPES = ( + voltage => [qw/input output/], + load => [qw/battery_load battery_capacity/], + temp => [q/temp/], + status => [qw/power_failure low_battery voltage_status ups_status battery_test/] +); +our @TYPES = keys %TYPES; + +{ + no strict 'refs'; + for my $t (@TYPES) { + for my $sub (qw/autoconf/) { + *{ __PACKAGE__ . "::run_${sub}_$t" } = "run_$sub"; + } + *{ __PACKAGE__ . "::run_default_$t" } = sub { + run_default(@{ $TYPES{$t} }); + }; + } +} + +{ + no strict 'refs'; + find_key($ARGV[0])->(); +} + +sub find_key { + my $p = shift || ""; + my $type_re = join '|', @TYPES; + unless ($Munin::Plugin::me =~ /upsmonpro_{1,}($type_re)$/) { + die "Could not determine script type [@TYPES] ? name=" . $Munin::Plugin::me . "\n"; + } + my $graph = $1; + + return 'run_' . ((grep { $p eq $_ } qw/autoconf config/) ? $ARGV[0] : 'default') . "_$graph"; +} + +sub run_config_voltage { + print <<'END'; +graph_title UPS Input/Output Voltage +graph_vlabel volt +graph_scale no +graph_category sensors +input.label input +input.info Input Voltage +input.type GAUGE +output.label output +output.info Output Voltage +output.type GAUGE +END + exit(0); +} + +sub run_config_temp { + print <<'END'; +graph_title UPS Temperature +graph_vlabel celsius +graph_scale no +graph_category sensors +temp.label temperature +temp.type GAUGE +END + exit(0); +} + +sub run_config_load { + print <<'END'; +graph_title UPS Battery Load/Capacity +graph_vlabel precent% +graph_scale no +graph_category sensors +battery_load.label battery_load +battery_load.type GAUGE +battery_capacity.label battery_capacity +battery_capacity.type GAUGE +END + exit(0); +} + +sub run_config_status { + print <<'END'; +graph_title UPS Statuses +graph_vlabel status +graph_scale no +graph_category sensors +power_failure.label power_failure +power_failure.type GAUGE +low_battery.label low_battery +low_battery.type GAUGE +voltage_status.label voltage_status +voltage_status.info 0 normal, 1 boost, 2 buck +voltage_status.type GAUGE +ups_status.label ups_status +ups_status.type GAUGE +battery_test.label battery_test +battery_test.type GAUGE +END + exit(0); +} + +sub run_default { + my $host = $ENV{host} || $DEFAULT_HOST; + my $port = $ENV{port} || $DEFAULT_PORT; + my @val_list = @_; + my $r = gather_data($host, $port); + for (@val_list) { + die "Wrong value: $_" if not exists $r->{$_}; + say "${_}.value $r->{$_}"; + } +} + +sub run_autoconf { + if (gather_data($DEFAULT_HOST, $DEFAULT_PORT)->{response} eq 'ok') { + say "yes"; + } else { + say "no ($DEFAULT_HOST:$DEFAULT_PORT not response)"; + } + exit(0); +} + +sub gather_data { + my $host = shift; + my $port = shift; + my %data = map { ($_ => 'U') } map { @{ $TYPES{$_} } } @TYPES; + $data{response} = 'failed'; + + my $sock = IO::Socket::INET->new( + PeerAddr => $host, + Proto => 'udp', + PeerPort => $port, + Blocking => 0 + ) or die "Cannot create socket: $@"; + + my $pattern = pack 'AAAACAAA', split //, 'PCMG1END'; + $sock->send($pattern) or die "send to $host: $!"; + usleep(200); + my $data; + my $buf; + while ($sock->read($buf, 32)) { + $data .= $buf; + } + if (defined $data and $data =~ /^PCMR.*END$/) { + my @data = unpack('C*', $data); + %data = ( + response => 'ok', + input => $data[6] + 256 * $data[5], + output => $data[8] + 256 * $data[7], + battery_load => $data[11], + battery_capacity => $data[12], + temp => $data[15], + power_failure => ($data[18] ? 1 : 0), + low_battery => ($data[19] ? 1 : 0), + + #voltage_status: 0 = normal, 3 = buck, 2 = boost + voltage_status => ($data[17] == 0 ? 0 : ($data[17] == 3 ? 2 : 1)), + ups_status => ($data[21] ? 1 : 0), + battery_test => ($data[23] ? 1 : 0), + ); + } + return \%data; +}