PVE-mods/src/PVENodeInfo/PVEMod_Collectors/Ups.pm
2026-06-07 11:17:27 +02:00

116 lines
3.1 KiB
Perl

package PVE::PVEMod::Collector::Ups;
use strict;
use warnings;
use Exporter 'import';
use JSON;
use PVE::PVEMod::Config qw($process_type $ups_state_file);
use PVE::PVEMod::Utils qw(debug setup_collector_signals);
our @EXPORT_OK = qw(
collector_for_ups
);
# ============================================================================
# UPS — long-running collector
# ============================================================================
sub collector_for_ups {
my ($device) = @_;
$process_type = 'collector';
$0 = "collector-ups-$device->{ups_name}";
debug(__LINE__, "UPS collector started");
my $shutdown = 0;
setup_collector_signals("ups-$device->{ups_name}", \$shutdown);
while (!$shutdown) {
my $ups_data = _get_ups_status($device->{ups_name});
eval {
open my $ofh, '>', $ups_state_file
or die "Failed to open $ups_state_file: $!";
print $ofh $ups_data;
close $ofh;
debug(__LINE__, "Wrote UPS data to $ups_state_file");
};
if ($@) {
debug(__LINE__, "Error writing UPS data: $@");
}
sleep 1 unless $shutdown; # $config{intervals}{data_pull}
}
debug(__LINE__, "UPS collector shutting down");
exit 0;
}
# ============================================================================
# UPS — status query
# ============================================================================
sub _get_ups_status {
my ($ups_name) = @_;
debug(__LINE__, "Collecting UPS status for $ups_name");
my $output = `/usr/bin/upsc $ups_name 2>/dev/null`;
unless (defined $output && length($output) > 0) {
debug(__LINE__, "No output from upsc for $ups_name");
return encode_json({ error => "No data from UPS $ups_name" });
}
my $ups_data = _parse_upsc_output($output);
unless (keys %$ups_data) {
debug(__LINE__, "No data received from upsc for $ups_name");
return encode_json({ error => "No data from UPS $ups_name" });
}
return JSON->new->pretty->canonical->encode({ $ups_name => $ups_data });
}
# ============================================================================
# UPS — output parser
# ============================================================================
sub _parse_upsc_output {
my ($output) = @_;
my $ups_data = {};
debug(__LINE__, "Parsing upsc output");
eval {
foreach my $line (split /\n/, $output) {
next if $line =~ /^\s*$/;
next if $line =~ /^Init SSL/;
if ($line =~ /^([^:]+):\s*(.*)$/) {
my ($key, $value) = ($1, $2);
$key =~ s/^\s+|\s+$//g;
$value =~ s/^\s+|\s+$//g;
# Coerce numeric values
if ($value =~ /^-?\d+\.?\d*$/) {
$ups_data->{$key} = $value + 0;
} else {
$ups_data->{$key} = $value;
}
}
}
};
if ($@) {
debug(__LINE__, "Error parsing upsc output: $@");
}
debug(__LINE__, "Completed parsing upsc output");
return $ups_data;
}
1;