* [pve-devel] [PATCH container 1/1] api: add hardware sensors endpoint and API support
[not found] <20251014193609.34452-1-davide.guerri@gmail.com>
@ 2025-10-14 19:36 ` Davide Guerri via pve-devel
0 siblings, 0 replies; only message in thread
From: Davide Guerri via pve-devel @ 2025-10-14 19:36 UTC (permalink / raw)
To: pve-devel; +Cc: Davide Guerri
[-- Attachment #1: Type: message/rfc822, Size: 20516 bytes --]
From: Davide Guerri <davide.guerri@gmail.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH container 1/1] api: add hardware sensors endpoint and API support
Date: Tue, 14 Oct 2025 21:36:09 +0200
Message-ID: <20251014193609.34452-2-davide.guerri@gmail.com>
This change adds support for querying hardware sensors via the API,
enhancing system monitoring capabilities.
API hierarchy:
/sensors -> GET index (returns: temperature, fan)
/temperature -> GET index (returns: cpu, disk, other)
/cpu -> GET CPU temperature data
/disk -> GET disk temperature data
/other -> GET other temperature data
/fan -> GET fan speed data
Signed-off-by: Davide Guerri <davide.guerri@gmail.com>
---
PVE/API2/Hardware.pm | 8 +-
PVE/API2/Hardware/Makefile | 1 +
PVE/API2/Hardware/Sensors.pm | 420 +++++++++++++++++++++++++++++++++++
3 files changed, 428 insertions(+), 1 deletion(-)
create mode 100644 PVE/API2/Hardware/Sensors.pm
diff --git a/PVE/API2/Hardware.pm b/PVE/API2/Hardware.pm
index 48b0765a..31391d92 100644
--- a/PVE/API2/Hardware.pm
+++ b/PVE/API2/Hardware.pm
@@ -7,6 +7,7 @@ use PVE::JSONSchema qw(get_standard_option);
use PVE::RESTHandler;
use PVE::API2::Hardware::PCI;
+use PVE::API2::Hardware::Sensors;
use PVE::API2::Hardware::USB;
use base qw(PVE::RESTHandler);
@@ -16,6 +17,11 @@ __PACKAGE__->register_method({
path => 'pci',
});
+__PACKAGE__->register_method({
+ subclass => "PVE::API2::Hardware::Sensors",
+ path => 'sensors',
+});
+
__PACKAGE__->register_method({
subclass => "PVE::API2::Hardware::USB",
path => 'usb',
@@ -47,7 +53,7 @@ __PACKAGE__->register_method({
my ($param) = @_;
my $res = [
- { type => 'pci' }, { type => 'usb' },
+ { type => 'pci' }, { type => 'sensors' }, { type => 'usb' },
];
return $res;
diff --git a/PVE/API2/Hardware/Makefile b/PVE/API2/Hardware/Makefile
index 026a8dd6..36f359cc 100644
--- a/PVE/API2/Hardware/Makefile
+++ b/PVE/API2/Hardware/Makefile
@@ -2,6 +2,7 @@ include ../../../defines.mk
PERLSOURCE= \
PCI.pm \
+ Sensors.pm \
USB.pm \
all:
diff --git a/PVE/API2/Hardware/Sensors.pm b/PVE/API2/Hardware/Sensors.pm
new file mode 100644
index 00000000..4509b88b
--- /dev/null
+++ b/PVE/API2/Hardware/Sensors.pm
@@ -0,0 +1,420 @@
+package PVE::API2::Hardware::Sensors;
+
+use v5.36;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTHandler;
+use PVE::SensorInfo;
+
+use base qw(PVE::RESTHandler);
+
+# Helper function to add optional fields to an entry
+sub add_optional_fields {
+ my ($entry, $info, $fields) = @_;
+
+ for my $field (@$fields) {
+ if (exists $info->{$field}) {
+ $entry->{$field} = $info->{$field};
+ }
+ }
+
+ return $entry;
+}
+
+__PACKAGE__->register_method({
+ name => 'sensor_index',
+ path => '',
+ method => 'GET',
+ description => "Index of available sensor methods.",
+ permissions => {
+ user => 'all',
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => { subdir => { type => 'string' } },
+ },
+ links => [{ rel => 'child', href => "{subdir}" }],
+ },
+ code => sub {
+ my ($param) = @_;
+
+ return [
+ { subdir => 'temperature' }, { subdir => 'fan' },
+ ];
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'temperature_index',
+ path => 'temperature',
+ method => 'GET',
+ description => "Index of temperature sensor types.",
+ permissions => {
+ user => 'all',
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => { subdir => { type => 'string' } },
+ },
+ links => [{ rel => 'child', href => "{subdir}" }],
+ },
+ code => sub {
+ my ($param) = @_;
+
+ return [
+ { subdir => 'cpu' }, { subdir => 'disk' }, { subdir => 'other' },
+ ];
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'read_cpu_temperature_values',
+ path => 'temperature/cpu',
+ method => 'GET',
+ description => "Read current CPU temperature values.",
+ protected => 1,
+ proxyto => "node",
+ permissions => {
+ check => ['perm', '/', ['Sys.Audit'], any => 1],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ sensor => {
+ type => 'string',
+ description => 'Sensor identifier (e.g., coretemp/Core 0).',
+ },
+ temperature => {
+ type => 'number',
+ description => 'Current temperature in degrees Celsius.',
+ },
+ unit => {
+ type => 'string',
+ description => 'Temperature unit (always celsius).',
+ },
+ driver => {
+ type => 'string',
+ description => 'Human-readable driver description.',
+ },
+ max => {
+ type => 'number',
+ optional => 1,
+ description => 'Maximum operating temperature before throttling.',
+ },
+ critical => {
+ type => 'number',
+ optional => 1,
+ description => 'Critical temperature threshold.',
+ },
+ logical_core => {
+ type => 'integer',
+ optional => 1,
+ description => 'Logical core number.',
+ },
+ physical_core => {
+ type => 'string',
+ optional => 1,
+ description => 'Physical core ID.',
+ },
+ package => {
+ type => 'integer',
+ optional => 1,
+ description => 'CPU package/socket number.',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $temps = PVE::SensorInfo::read_temperatures('cpu');
+
+ my $result = [];
+ foreach my $sensor (sort keys %$temps) {
+ my $info = $temps->{$sensor};
+
+ my $entry = {
+ sensor => $sensor,
+ temperature => $info->{temperature},
+ unit => $info->{unit},
+ driver => $info->{driver},
+ };
+
+ add_optional_fields(
+ $entry,
+ $info,
+ [qw(max critical logical_core physical_core package)],
+ );
+
+ push @$result, $entry;
+ }
+
+ return $result;
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'read_disk_temperature_values',
+ path => 'temperature/disk',
+ method => 'GET',
+ description => "Read current disk temperature values.",
+ protected => 1,
+ proxyto => "node",
+ permissions => {
+ check => ['perm', '/', ['Sys.Audit'], any => 1],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ sensor => {
+ type => 'string',
+ description => 'Sensor identifier (e.g., hwmon1/Composite).',
+ },
+ device => {
+ type => 'string',
+ optional => 1,
+ description => 'Block device name (e.g., nvme0n1, sda).',
+ },
+ temperature => {
+ type => 'number',
+ description => 'Current temperature in degrees Celsius.',
+ },
+ unit => {
+ type => 'string',
+ description => 'Temperature unit (always celsius).',
+ },
+ driver => {
+ type => 'string',
+ description => 'Human-readable driver description.',
+ },
+ max => {
+ type => 'number',
+ optional => 1,
+ description => 'Maximum operating temperature.',
+ },
+ critical => {
+ type => 'number',
+ optional => 1,
+ description => 'Critical temperature threshold.',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $temps = PVE::SensorInfo::read_temperatures('disk');
+
+ my $result = [];
+ foreach my $sensor (sort keys %$temps) {
+ my $info = $temps->{$sensor};
+
+ my $entry = {
+ sensor => $sensor,
+ temperature => $info->{temperature},
+ unit => $info->{unit},
+ driver => $info->{driver},
+ };
+
+ add_optional_fields($entry, $info, [qw(device max critical)]);
+
+ push @$result, $entry;
+ }
+
+ return $result;
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'read_other_temperature_values',
+ path => 'temperature/other',
+ method => 'GET',
+ description => "Read current temperature values from other sensors.",
+ protected => 1,
+ proxyto => "node",
+ permissions => {
+ check => ['perm', '/', ['Sys.Audit'], any => 1],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ sensor => {
+ type => 'string',
+ description => 'Sensor identifier.',
+ },
+ temperature => {
+ type => 'number',
+ description => 'Current temperature in degrees Celsius.',
+ },
+ unit => {
+ type => 'string',
+ description => 'Temperature unit (always celsius).',
+ },
+ driver => {
+ type => 'string',
+ description => 'Human-readable driver description.',
+ },
+ max => {
+ type => 'number',
+ optional => 1,
+ description => 'Maximum operating temperature.',
+ },
+ critical => {
+ type => 'number',
+ optional => 1,
+ description => 'Critical temperature threshold.',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $temps = PVE::SensorInfo::read_temperatures('other');
+
+ my $result = [];
+ foreach my $sensor (sort keys %$temps) {
+ my $info = $temps->{$sensor};
+
+ my $entry = {
+ sensor => $sensor,
+ temperature => $info->{temperature},
+ unit => $info->{unit},
+ driver => $info->{driver},
+ };
+
+ add_optional_fields($entry, $info, [qw(max critical)]);
+
+ push @$result, $entry;
+ }
+
+ return $result;
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'read_fan_speeds',
+ path => 'fan',
+ method => 'GET',
+ description => "Read current fan speed values.",
+ protected => 1,
+ proxyto => "node",
+ permissions => {
+ check => ['perm', '/', ['Sys.Audit'], any => 1],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ fan => {
+ type => 'string',
+ description => 'Fan identifier (e.g., nct6775/CPU Fan).',
+ },
+ speed => {
+ type => 'integer',
+ description => 'Current fan speed in RPM.',
+ },
+ unit => {
+ type => 'string',
+ description => 'Speed unit (always rpm).',
+ },
+ driver => {
+ type => 'string',
+ description => 'Human-readable driver description.',
+ },
+ min => {
+ type => 'integer',
+ optional => 1,
+ description => 'Minimum fan speed in RPM.',
+ },
+ max => {
+ type => 'integer',
+ optional => 1,
+ description => 'Maximum fan speed in RPM.',
+ },
+ target => {
+ type => 'integer',
+ optional => 1,
+ description => 'Target fan speed in RPM.',
+ },
+ alarm => {
+ type => 'integer',
+ optional => 1,
+ description => 'Fan alarm status (0=OK, 1=Alarm).',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $speeds = PVE::SensorInfo::read_fan_speeds();
+
+ my $result = [];
+ foreach my $fan (sort keys %$speeds) {
+ my $info = $speeds->{$fan};
+
+ my $entry = {
+ fan => $fan,
+ speed => $info->{speed},
+ unit => $info->{unit},
+ driver => $info->{driver},
+ };
+
+ add_optional_fields($entry, $info, [qw(min max target alarm)]);
+
+ push @$result, $entry;
+ }
+
+ return $result;
+ },
+});
+
+1;
--
2.50.1 (Apple Git-155)
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-10-14 21:21 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20251014193609.34452-1-davide.guerri@gmail.com>
2025-10-14 19:36 ` [pve-devel] [PATCH container 1/1] api: add hardware sensors endpoint and API support Davide Guerri via pve-devel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox