[pbs-devel] [PATCH proxmox-backup v4 2/4] metric_collection: use ip link for determining the type of interfaces
Stefan Hanreich
s.hanreich at proxmox.com
Thu Jul 31 16:08:49 CEST 2025
Physical interfaces now can have arbitrary names with the introduction
of proxmox-network-interface pinning. In order to correctly report the
bandwidth metrics for PBS hosts, the method for determining whether an
interface is physical or not needs to be adjusted as well. Use the
adapted helper from proxmox-network-api to query the information about
interfaces and use that to determine the type of interface at every
site that uses the generated stats. To avoid spawning a new process
with every update loop, cache the initial query and use that
information.
Signed-off-by: Stefan Hanreich <s.hanreich at proxmox.com>
Tested-by: Christian Ebner <c.ebner at proxmox.com>
---
src/server/metric_collection/mod.rs | 87 +++++++++++++++++---
src/server/metric_collection/pull_metrics.rs | 5 +-
src/server/metric_collection/rrd.rs | 5 +-
3 files changed, 79 insertions(+), 18 deletions(-)
diff --git a/src/server/metric_collection/mod.rs b/src/server/metric_collection/mod.rs
index daedfb72..9b62cbb4 100644
--- a/src/server/metric_collection/mod.rs
+++ b/src/server/metric_collection/mod.rs
@@ -1,7 +1,8 @@
use std::{
+ collections::HashMap,
path::Path,
pin::pin,
- sync::Arc,
+ sync::{Arc, OnceLock},
time::{Duration, Instant},
};
@@ -9,6 +10,7 @@ use anyhow::Error;
use tokio::join;
use pbs_api_types::{DataStoreConfig, Operation};
+use proxmox_network_api::{get_network_interfaces, IpLink};
use proxmox_sys::{
fs::FileSystemInformation,
linux::procfs::{Loadavg, ProcFsMemInfo, ProcFsNetDev, ProcFsStat},
@@ -101,7 +103,7 @@ async fn run_stat_generator() {
struct HostStats {
proc: Option<ProcFsStat>,
meminfo: Option<ProcFsMemInfo>,
- net: Option<Vec<ProcFsNetDev>>,
+ net: Option<Vec<NetdevStat>>,
load: Option<Loadavg>,
}
@@ -111,11 +113,78 @@ struct DiskStat {
dev: Option<BlockDevStat>,
}
-fn collect_host_stats_sync() -> HostStats {
- use proxmox_sys::linux::procfs::{
- read_loadavg, read_meminfo, read_proc_net_dev, read_proc_stat,
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
+enum NetdevType {
+ Physical,
+ Virtual,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
+struct NetdevStat {
+ pub device: String,
+ pub receive: u64,
+ pub send: u64,
+ pub ty: NetdevType,
+}
+
+impl NetdevStat {
+ fn from_fs_net_dev(net_dev: ProcFsNetDev, ty: NetdevType) -> Self {
+ Self {
+ device: net_dev.device,
+ receive: net_dev.receive,
+ send: net_dev.send,
+ ty,
+ }
+ }
+}
+
+static NETWORK_INTERFACE_CACHE: OnceLock<HashMap<String, IpLink>> = OnceLock::new();
+
+fn collect_netdev_stats() -> Option<Vec<NetdevStat>> {
+ use proxmox_sys::linux::procfs::read_proc_net_dev;
+
+ let net_devs = match read_proc_net_dev() {
+ Ok(net_devs) => net_devs,
+ Err(err) => {
+ eprintln!("read_prox_net_dev failed - {err}");
+ return None;
+ }
};
+ let ip_links = match NETWORK_INTERFACE_CACHE.get() {
+ Some(ip_links) => ip_links,
+ None => match get_network_interfaces() {
+ Ok(network_interfaces) => {
+ let _ = NETWORK_INTERFACE_CACHE.set(network_interfaces);
+ NETWORK_INTERFACE_CACHE.get().unwrap()
+ }
+ Err(err) => {
+ eprintln!("get_network_interfaces failed - {err}");
+ return None;
+ }
+ },
+ };
+
+ let mut stat_devs = Vec::with_capacity(net_devs.len());
+
+ for net_dev in net_devs {
+ if let Some(ip_link) = ip_links.get(&net_dev.device) {
+ let ty = if ip_link.is_physical() {
+ NetdevType::Physical
+ } else {
+ NetdevType::Virtual
+ };
+
+ stat_devs.push(NetdevStat::from_fs_net_dev(net_dev, ty));
+ }
+ }
+
+ Some(stat_devs)
+}
+
+fn collect_host_stats_sync() -> HostStats {
+ use proxmox_sys::linux::procfs::{read_loadavg, read_meminfo, read_proc_stat};
+
let proc = match read_proc_stat() {
Ok(stat) => Some(stat),
Err(err) => {
@@ -132,13 +201,7 @@ fn collect_host_stats_sync() -> HostStats {
}
};
- let net = match read_proc_net_dev() {
- Ok(netdev) => Some(netdev),
- Err(err) => {
- eprintln!("read_prox_net_dev failed - {err}");
- None
- }
- };
+ let net = collect_netdev_stats();
let load = match read_loadavg() {
Ok(loadavg) => Some(loadavg),
diff --git a/src/server/metric_collection/pull_metrics.rs b/src/server/metric_collection/pull_metrics.rs
index 3b105eaf..e99662fa 100644
--- a/src/server/metric_collection/pull_metrics.rs
+++ b/src/server/metric_collection/pull_metrics.rs
@@ -12,7 +12,7 @@ use proxmox_shared_cache::SharedCache;
use proxmox_sys::fs::CreateOptions;
use serde::{Deserialize, Serialize};
-use super::{DiskStat, HostStats, METRIC_COLLECTION_INTERVAL};
+use super::{DiskStat, HostStats, NetdevType, METRIC_COLLECTION_INTERVAL};
const METRIC_CACHE_TIME: Duration = Duration::from_secs(30 * 60);
const STORED_METRIC_GENERATIONS: u64 =
@@ -113,11 +113,10 @@ pub(super) fn update_metrics(
}
if let Some(netdev) = &host.net {
- use pbs_config::network::is_physical_nic;
let mut netin = 0;
let mut netout = 0;
for item in netdev {
- if !is_physical_nic(&item.device) {
+ if item.ty != NetdevType::Physical {
continue;
}
netin += item.receive;
diff --git a/src/server/metric_collection/rrd.rs b/src/server/metric_collection/rrd.rs
index ed39cc94..d129432e 100644
--- a/src/server/metric_collection/rrd.rs
+++ b/src/server/metric_collection/rrd.rs
@@ -16,7 +16,7 @@ use proxmox_sys::fs::CreateOptions;
use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
use proxmox_rrd_api_types::{RrdMode, RrdTimeframe};
-use super::{DiskStat, HostStats};
+use super::{DiskStat, HostStats, NetdevType};
const RRD_CACHE_BASEDIR: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/rrdb");
@@ -162,11 +162,10 @@ pub(super) fn update_metrics(host: &HostStats, hostdisk: &DiskStat, datastores:
}
if let Some(netdev) = &host.net {
- use pbs_config::network::is_physical_nic;
let mut netin = 0;
let mut netout = 0;
for item in netdev {
- if !is_physical_nic(&item.device) {
+ if item.ty != NetdevType::Physical {
continue;
}
netin += item.receive;
--
2.47.2
More information about the pbs-devel
mailing list