Add UPS support. Refactor file namings
This commit is contained in:
parent
0d608abf6f
commit
db653e4c4b
265
GPUcollecter.pm
265
GPUcollecter.pm
@ -42,9 +42,11 @@ my $pve_mod_working_dir = '/run/pveproxy/pve-mod';
|
||||
my $stats_dir = $pve_mod_working_dir;
|
||||
my $state_file = "$pve_mod_working_dir/stats.json";
|
||||
my $sensors_state_file = "$pve_mod_working_dir/sensors.json";
|
||||
my $lock_file = "$pve_mod_working_dir/pve-mod-worker.lock";
|
||||
my $pve_mod_worker_lock = "$pve_mod_working_dir/pve-mod-pve_mod_worker.lock";
|
||||
my $startup_lock = $lock_file . ".startup";
|
||||
my $ups_state_file = "$pve_mod_working_dir/ups.json";
|
||||
|
||||
my $collectors_pid_file = "$pve_mod_working_dir/collectors.pids"; # List of collector PIDs
|
||||
my $pve_mod_worker_lock = "$pve_mod_working_dir/pve-mod-pve_mod_worker.lock"; # PVE Mod worker lock
|
||||
my $startup_lock = "$pve_mod_working_dir/startup.lock"; # Exclusive startup lock
|
||||
my $last_snapshot = {};
|
||||
my $last_mtime = 0;
|
||||
my $is_collector_parent = 0; # Flag to track if this process started collectors
|
||||
@ -54,9 +56,15 @@ my $COLLECTOR_TIMEOUT = 10; # Stop collectors x seconds after last get_graphic
|
||||
my $intel_gpu_enabled = 1; # Set to 0 to disable Intel GPU support
|
||||
my $amd_gpu_enabled = 0; # Set to 1 to enable AMD GPU support (not yet implemented)
|
||||
my $nvidia_gpu_enabled = 1; # Set to 1 to enable NVIDIA GPU support (not yet implemented)
|
||||
my $ups_enabled = 1; # Set to 1 to enable UPS support
|
||||
my $pve_mod_worker_pid;
|
||||
my $pve_mod_worker_running = 0;
|
||||
|
||||
# UPS Configuration
|
||||
my $ups_device = {
|
||||
ups_name => 'ups@192.168.3.2', # Format: upsname[@hostname[:port]]
|
||||
};
|
||||
|
||||
# ============================================================================
|
||||
# Intel GPU Support
|
||||
# ============================================================================
|
||||
@ -985,11 +993,136 @@ sub _cpu_model_by_package {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# UPS Support
|
||||
# ============================================================================
|
||||
|
||||
sub _collector_for_ups {
|
||||
my ($device) = @_;
|
||||
|
||||
$0 = "pve-mod-ups-collector";
|
||||
_debug(__LINE__, "UPS collector started");
|
||||
|
||||
# Set up signal handlers for graceful shutdown
|
||||
my $shutdown = 0;
|
||||
$SIG{TERM} = sub {
|
||||
_debug(__LINE__, "UPS collector received SIGTERM");
|
||||
$shutdown = 1;
|
||||
};
|
||||
$SIG{INT} = sub {
|
||||
_debug(__LINE__, "UPS collector received SIGINT");
|
||||
$shutdown = 1;
|
||||
};
|
||||
while (!$shutdown) {
|
||||
my $upsData = _get_ups_status($device->{ups_name});
|
||||
|
||||
# Write to ups state file
|
||||
eval {
|
||||
open my $ofh, '>', $ups_state_file or die "Failed to open $ups_state_file: $!";
|
||||
print $ofh $upsData;
|
||||
close $ofh;
|
||||
_debug(__LINE__, "Wrote ups data to $ups_state_file");
|
||||
};
|
||||
if ($@) {
|
||||
_debug(__LINE__, "Error writing ups data: $@");
|
||||
}
|
||||
|
||||
sleep 1 unless $shutdown;
|
||||
}
|
||||
_debug(__LINE__, "UPS collector shutting down");
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub _get_ups_status {
|
||||
my ($ups_name) = @_;
|
||||
|
||||
# upsc upsname[@hostname[:port]]
|
||||
my $cmd = "upsc $ups_name 2>/dev/null";
|
||||
|
||||
_debug(__LINE__, "Running command: $cmd");
|
||||
|
||||
# Execute command and capture output
|
||||
my $output = `$cmd`;
|
||||
|
||||
_debug(__LINE__, "upsc command output collected");
|
||||
|
||||
my $exit_code = $? >> 8;
|
||||
|
||||
_debug(__LINE__, "upsc command exited with code $exit_code");
|
||||
|
||||
if ($exit_code != 0) {
|
||||
_debug(__LINE__, "upsc command failed with exit code $exit_code");
|
||||
return encode_json({ error => "Failed to execute upsc for $ups_name" });
|
||||
}
|
||||
|
||||
_debug(__LINE__, "upsc command executed successfully");
|
||||
|
||||
# Convert upsc output to nested hash structure
|
||||
my $ups_data = _parse_upsc_output($output);
|
||||
|
||||
_debug(__LINE__, "Parsed upsc output for $ups_name");
|
||||
|
||||
# Check if we got any data
|
||||
unless (keys %$ups_data) {
|
||||
_debug(__LINE__, "No data received from upsc for $ups_name");
|
||||
return encode_json({ error => "No data from UPS $ups_name" });
|
||||
}
|
||||
|
||||
# Wrap in UPS name structure
|
||||
my $result = {
|
||||
$ups_name => $ups_data
|
||||
};
|
||||
|
||||
_debug(__LINE__, "Successfully parsed UPS data for $ups_name");
|
||||
|
||||
# Return as pretty JSON
|
||||
return JSON->new->pretty->canonical->encode($result);
|
||||
}
|
||||
|
||||
sub _parse_upsc_output {
|
||||
my ($output) = @_;
|
||||
|
||||
my $ups_data = {};
|
||||
|
||||
_debug(__LINE__, "Parsing upsc output");
|
||||
|
||||
eval {
|
||||
foreach my $line (split /\n/, $output) {
|
||||
# Skip empty lines and SSL init message
|
||||
next if $line =~ /^\s*$/;
|
||||
next if $line =~ /^Init SSL/;
|
||||
|
||||
# Parse key-value pairs (format: "key: value")
|
||||
if ($line =~ /^([^:]+):\s*(.*)$/) {
|
||||
my ($key, $value) = ($1, $2);
|
||||
|
||||
# Trim whitespace
|
||||
$key =~ s/^\s+|\s+$//g;
|
||||
$value =~ s/^\s+|\s+$//g;
|
||||
|
||||
# Store as flat key-value pairs (no nesting)
|
||||
# Convert numeric values to numbers, keep strings as strings
|
||||
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;
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Supporting functions
|
||||
# ============================================================================
|
||||
|
||||
# Check if a process is alive
|
||||
sub _is_process_alive {
|
||||
my ($pid) = @_;
|
||||
return -d "/proc/$pid";
|
||||
@ -1226,6 +1359,38 @@ sub get_sensors_stats {
|
||||
return $sensors_data;
|
||||
}
|
||||
|
||||
sub get_ups_stats {
|
||||
_debug(__LINE__, "get_ups_stats called");
|
||||
|
||||
# Start PVE Mod
|
||||
_pve_mod_starter();
|
||||
|
||||
unless (-f $ups_state_file) {
|
||||
_debug(__LINE__, "UPS state file does not exist: $ups_state_file");
|
||||
return {};
|
||||
}
|
||||
|
||||
my $ups_data;
|
||||
eval {
|
||||
open my $fh, '<', $ups_state_file or die "Failed to open $ups_state_file: $!";
|
||||
local $/;
|
||||
my $json = <$fh>;
|
||||
close($fh);
|
||||
$ups_data = $json;
|
||||
_debug(__LINE__, "Read UPS data, JSON length: " . length($json) . " bytes");
|
||||
_debug(__LINE__, "Read UPS data from $ups_state_file");
|
||||
};
|
||||
if ($@) {
|
||||
_debug(__LINE__, "Failed to read/parse UPS data: $@");
|
||||
return {};
|
||||
}
|
||||
|
||||
# Notify pve_mod_worker of activity
|
||||
_notify_pve_mod_worker();
|
||||
|
||||
return $ups_data;
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Collector
|
||||
# ============================================================================
|
||||
@ -1241,7 +1406,7 @@ sub _start_graphics_collectors {
|
||||
}
|
||||
|
||||
# Read existing collectors from lock file
|
||||
my %existing_collectors = _read_collector_lock($lock_file);
|
||||
my %existing_collectors = _read_collector_lock($collectors_pid_file);
|
||||
|
||||
# Generalized device collector management for future AMD/NVIDIA support
|
||||
my @all_devices;
|
||||
@ -1307,7 +1472,7 @@ sub _start_graphics_collectors {
|
||||
my $device_name = $device->{card} // $device->{name} // "device$i";
|
||||
|
||||
# Check if collector already running
|
||||
my $existing_pid = _is_collector_running($device_name, $lock_file);
|
||||
my $existing_pid = _is_collector_running($device_name, $collectors_pid_file);
|
||||
if ($existing_pid) {
|
||||
_debug(__LINE__, "Collector for $type $device_name already running with PID $existing_pid");
|
||||
push @collector_entries, [$existing_pid, $device_name, $type];
|
||||
@ -1322,7 +1487,7 @@ sub _start_graphics_collectors {
|
||||
}
|
||||
|
||||
# Write all collector PIDs to lock file
|
||||
unless (_write_collector_lock($lock_file, @collector_entries)) {
|
||||
unless (_write_collector_lock($collectors_pid_file, @collector_entries)) {
|
||||
_debug(__LINE__, "Failed to write lock file, terminating collectors");
|
||||
foreach my $entry (@collector_entries) {
|
||||
kill 'TERM', $entry->[0];
|
||||
@ -1363,7 +1528,7 @@ sub _start_sensors_collector {
|
||||
}
|
||||
|
||||
# Check if already running
|
||||
my $existing_pid = _is_collector_running('sensors', $lock_file);
|
||||
my $existing_pid = _is_collector_running('sensors', $collectors_pid_file);
|
||||
if ($existing_pid) {
|
||||
_debug(__LINE__, "Sensors collector already running with PID $existing_pid");
|
||||
return;
|
||||
@ -1375,7 +1540,7 @@ sub _start_sensors_collector {
|
||||
if ($pid) {
|
||||
# Read existing entries
|
||||
my @collector_entries;
|
||||
my %existing = _read_collector_lock($lock_file);
|
||||
my %existing = _read_collector_lock($collectors_pid_file);
|
||||
foreach my $name (keys %existing) {
|
||||
push @collector_entries, [$existing{$name}->{pid}, $name, $existing{$name}->{type}];
|
||||
}
|
||||
@ -1384,7 +1549,7 @@ sub _start_sensors_collector {
|
||||
push @collector_entries, [$pid, 'sensors', 'sensors'];
|
||||
|
||||
# Write updated lock file
|
||||
unless (_write_collector_lock($lock_file, @collector_entries)) {
|
||||
unless (_write_collector_lock($collectors_pid_file, @collector_entries)) {
|
||||
_debug(__LINE__, "Failed to update lock file, terminating sensors collector");
|
||||
kill 'TERM', $pid;
|
||||
return;
|
||||
@ -1400,6 +1565,59 @@ sub _start_sensors_collector {
|
||||
}
|
||||
}
|
||||
|
||||
sub _start_ups_collector {
|
||||
|
||||
if ($ups_enabled == 0) {
|
||||
_debug(__LINE__, "UPS support not enabled, skipping collector startup");
|
||||
return;
|
||||
}
|
||||
|
||||
_debug(__LINE__, "Starting UPS collector");
|
||||
|
||||
# Check if UPS is configured
|
||||
unless (defined $ups_device && $ups_device->{ups_name}) {
|
||||
_debug(__LINE__, "No UPS configured, skipping collector startup");
|
||||
return;
|
||||
}
|
||||
|
||||
# Check if already running
|
||||
my $existing_pid = _is_collector_running('ups', $collectors_pid_file);
|
||||
if ($existing_pid) {
|
||||
_debug(__LINE__, "UPS collector already running with PID $existing_pid");
|
||||
return;
|
||||
}
|
||||
|
||||
# Start the collector
|
||||
my $pid = _start_child_collector('ups', \&_collector_for_ups, $ups_device);
|
||||
|
||||
if ($pid) {
|
||||
# Read existing entries
|
||||
my @collector_entries;
|
||||
my %existing = _read_collector_lock($collectors_pid_file);
|
||||
foreach my $name (keys %existing) {
|
||||
push @collector_entries, [$existing{$name}->{pid}, $name, $existing{$name}->{type}];
|
||||
}
|
||||
|
||||
# Add UPS entry
|
||||
push @collector_entries, [$pid, 'ups', 'ups'];
|
||||
|
||||
# Write updated lock file
|
||||
unless (_write_collector_lock($collectors_pid_file, @collector_entries)) {
|
||||
_debug(__LINE__, "Failed to update lock file, terminating UPS collector");
|
||||
kill 'TERM', $pid;
|
||||
return;
|
||||
}
|
||||
|
||||
# Verify it's alive
|
||||
sleep 0.1;
|
||||
if (kill(0, $pid)) {
|
||||
_debug(__LINE__, "Verified UPS collector (PID $pid) is alive");
|
||||
} else {
|
||||
_debug(__LINE__, "WARNING - UPS collector (PID $pid) died immediately!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PVE Mod Worker
|
||||
# ============================================================================
|
||||
@ -1418,12 +1636,22 @@ sub _pve_mod_starter {
|
||||
# Ensure directory exists
|
||||
_ensure_pve_mod_directory_exists();
|
||||
|
||||
# Try to acquire startup lock FIRST (prevents race conditions)
|
||||
# Try to get the lock
|
||||
_debug(__LINE__, "Trying to acquire startup lock: $startup_lock");
|
||||
my $startup_fh = _acquire_exclusive_lock($startup_lock, 'startup lock');
|
||||
return unless $startup_fh;
|
||||
|
||||
# SECOND CHECK (after lock) - verify nothing changed while waiting
|
||||
if (_is_pve_mod_worker_running()) {
|
||||
_debug(__LINE__, "Worker started by another process while we waited for lock");
|
||||
close($startup_fh);
|
||||
unlink($startup_lock);
|
||||
return "already running";
|
||||
}
|
||||
|
||||
# Now we KNOW we're the only one starting things
|
||||
print $startup_fh "$$\n";
|
||||
close($startup_fh);
|
||||
$startup_fh->flush();
|
||||
_debug(__LINE__, "Wrote PID, $$, to startup lock");
|
||||
|
||||
# Start sensors collector first
|
||||
@ -1432,6 +1660,9 @@ sub _pve_mod_starter {
|
||||
# Start graphics collectors
|
||||
_start_graphics_collectors();
|
||||
|
||||
# Start UPS collector
|
||||
_start_ups_collector();
|
||||
|
||||
_debug(__LINE__, "All collectors started");
|
||||
|
||||
# Start pve mod worker
|
||||
@ -1444,10 +1675,6 @@ sub _pve_mod_starter {
|
||||
_debug(__LINE__, "pve_mod_worker started successfully, returning");
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PVE Mod Worker
|
||||
# ============================================================================
|
||||
|
||||
sub _pve_mod_worker {
|
||||
_debug(__LINE__, "_pve_mod_worker called");
|
||||
|
||||
@ -1577,7 +1804,7 @@ sub _stop_collectors {
|
||||
|
||||
# Read current PIDs from lock file
|
||||
my @pids;
|
||||
if (open my $lock_fh, '<', $lock_file) {
|
||||
if (open my $lock_fh, '<', $collectors_pid_file) {
|
||||
while (my $line = <$lock_fh>) {
|
||||
chomp $line;
|
||||
# Extract PID from "PID card type" format
|
||||
@ -1625,8 +1852,8 @@ sub _stop_collectors {
|
||||
if (-f $state_file) {
|
||||
unlink $state_file or _debug(__LINE__, "Failed to remove $state_file: $!");
|
||||
}
|
||||
if (-f $lock_file) {
|
||||
unlink $lock_file or _debug(__LINE__, "Failed to remove $lock_file: $!");
|
||||
if (-f $collectors_pid_file) {
|
||||
unlink $collectors_pid_file or _debug(__LINE__, "Failed to remove $collectors_pid_file: $!");
|
||||
}
|
||||
|
||||
# Remove pve mod worker directory and all files if it exists
|
||||
|
||||
Loading…
Reference in New Issue
Block a user