public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Lukas Wagner <l.wagner@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [pdm-devel] [PATCH proxmox-datacenter-manager 05/25] metric collection: split top_entities split into separate module
Date: Tue, 11 Feb 2025 13:05:21 +0100	[thread overview]
Message-ID: <20250211120541.163621-6-l.wagner@proxmox.com> (raw)
In-Reply-To: <20250211120541.163621-1-l.wagner@proxmox.com>

This makes the parent module a bit more easy not navigate.

No functional changes intended.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 server/src/api/resources.rs                  |   3 +-
 server/src/metric_collection/mod.rs          | 149 +------------------
 server/src/metric_collection/top_entities.rs | 148 ++++++++++++++++++
 3 files changed, 152 insertions(+), 148 deletions(-)
 create mode 100644 server/src/metric_collection/top_entities.rs

diff --git a/server/src/api/resources.rs b/server/src/api/resources.rs
index 453d9e8..afda51e 100644
--- a/server/src/api/resources.rs
+++ b/server/src/api/resources.rs
@@ -26,6 +26,7 @@ use proxmox_subscription::SubscriptionStatus;
 use pve_api_types::{ClusterResource, ClusterResourceType};
 
 use crate::connection;
+use crate::metric_collection::top_entities;
 
 pub const ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(SUBDIRS))
@@ -320,7 +321,7 @@ async fn get_top_entities(timeframe: Option<RrdTimeframe>) -> Result<TopEntities
     let (remotes_config, _) = pdm_config::remotes::config()?;
 
     let timeframe = timeframe.unwrap_or(RrdTimeframe::Day);
-    let res = crate::metric_collection::calculate_top(&remotes_config.sections, timeframe, 10);
+    let res = top_entities::calculate_top(&remotes_config.sections, timeframe, 10);
     Ok(res)
 }
 
diff --git a/server/src/metric_collection/mod.rs b/server/src/metric_collection/mod.rs
index b37d678..5540f93 100644
--- a/server/src/metric_collection/mod.rs
+++ b/server/src/metric_collection/mod.rs
@@ -6,15 +6,13 @@ use anyhow::Error;
 use pbs_api_types::{MetricDataPoint, MetricDataType};
 use proxmox_rrd::rrd::DataSourceType;
 
-use pdm_api_types::{
-    remotes::RemoteType,
-    resource::{Resource, ResourceRrdData, TopEntities, TopEntity},
-};
+use pdm_api_types::remotes::RemoteType;
 use pve_api_types::{ClusterMetricsData, ClusterMetricsDataType};
 
 use crate::{connection, task_utils};
 
 pub mod rrd_cache;
+pub mod top_entities;
 
 const COLLECTION_INTERVAL: u64 = 60;
 
@@ -150,146 +148,3 @@ fn store_metric_pbs(remote_name: &str, data_point: &MetricDataPoint) {
         data_source_type,
     );
 }
-
-fn insert_sorted<T>(vec: &mut Vec<(usize, T)>, value: (usize, T), limit: usize) {
-    let index = match vec.binary_search_by_key(&value.0, |(idx, _)| *idx) {
-        Ok(idx) | Err(idx) => idx,
-    };
-
-    vec.insert(index, value);
-    if vec.len() > limit {
-        for _ in 0..(vec.len() - limit) {
-            vec.remove(0);
-        }
-    }
-}
-
-// for now simple sum of the values => area under the graph curve
-fn calculate_coefficient(values: &proxmox_rrd::Entry) -> f64 {
-    let mut coefficient = 0.0;
-    for point in values.data.iter() {
-        let value = point.unwrap_or_default();
-        if value.is_finite() {
-            coefficient += value;
-        }
-    }
-
-    coefficient
-}
-
-// FIXME: cache the values instead of calculate freshly every time?
-// FIXME: find better way to enumerate nodes/guests/etc.(instead of relying on the cache)
-pub fn calculate_top(
-    remotes: &HashMap<String, pdm_api_types::remotes::Remote>,
-    timeframe: proxmox_rrd_api_types::RrdTimeframe,
-    num: usize,
-) -> TopEntities {
-    let mut guest_cpu = Vec::new();
-    let mut node_cpu = Vec::new();
-    let mut node_memory = Vec::new();
-
-    for remote_name in remotes.keys() {
-        if let Some(data) =
-            crate::api::resources::get_cached_resources(remote_name, i64::MAX as u64)
-        {
-            for res in data.resources {
-                let id = res.id().to_string();
-                let name = format!("pve/{remote_name}/{id}");
-                match &res {
-                    Resource::PveStorage(_) => {}
-                    Resource::PveQemu(_) | Resource::PveLxc(_) => {
-                        if let Some(entity) =
-                            get_entity(timeframe, remote_name, res, name, "cpu_current")
-                        {
-                            let coefficient = (entity.0 * 100.0).round() as usize;
-                            insert_sorted(&mut guest_cpu, (coefficient, entity.1), num);
-                        }
-                    }
-                    Resource::PveNode(_) => {
-                        if let Some(entity) = get_entity(
-                            timeframe,
-                            remote_name,
-                            res.clone(),
-                            name.clone(),
-                            "cpu_current",
-                        ) {
-                            let coefficient = (entity.0 * 100.0).round() as usize;
-                            insert_sorted(&mut node_cpu, (coefficient, entity.1), num);
-                        }
-                        // convert mem/mem_total into a single entity
-                        if let Some(mut mem) = get_entity(
-                            timeframe,
-                            remote_name,
-                            res.clone(),
-                            name.clone(),
-                            "mem_used",
-                        ) {
-                            if let Some(mem_total) =
-                                get_entity(timeframe, remote_name, res, name, "mem_total")
-                            {
-                                // skip if we don't have the same amount of data for used and total
-                                let mem_rrd = &mem.1.rrd_data.data;
-                                let mem_total_rrd = &mem_total.1.rrd_data.data;
-                                if mem_rrd.len() != mem_total_rrd.len() {
-                                    continue;
-                                }
-                                let coefficient = (100.0 * mem.0 / mem_total.0).round() as usize;
-                                let mut mem_usage = Vec::new();
-                                for i in 0..mem_rrd.len() {
-                                    let point = match (mem_rrd[i], mem_total_rrd[i]) {
-                                        (Some(mem), Some(total)) => Some(mem / total),
-                                        _ => None,
-                                    };
-                                    mem_usage.push(point)
-                                }
-                                mem.1.rrd_data.data = mem_usage;
-                                insert_sorted(&mut node_memory, (coefficient, mem.1), num);
-                            }
-                        }
-                    }
-                    Resource::PbsNode(_) => {}
-                    Resource::PbsDatastore(_) => {}
-                }
-            }
-        }
-    }
-
-    TopEntities {
-        guest_cpu: guest_cpu.into_iter().map(|(_, entity)| entity).collect(),
-        node_cpu: node_cpu.into_iter().map(|(_, entity)| entity).collect(),
-        node_memory: node_memory.into_iter().map(|(_, entity)| entity).collect(),
-    }
-}
-
-fn get_entity(
-    timeframe: proxmox_rrd_api_types::RrdTimeframe,
-    remote_name: &String,
-    res: Resource,
-    name: String,
-    metric: &str,
-) -> Option<(f64, TopEntity)> {
-    if let Ok(Some(values)) = rrd_cache::extract_data(
-        &name,
-        metric,
-        timeframe,
-        proxmox_rrd_api_types::RrdMode::Average,
-    ) {
-        let coefficient = calculate_coefficient(&values);
-        if coefficient > 0.0 {
-            return Some((
-                coefficient,
-                TopEntity {
-                    remote: remote_name.to_string(),
-                    resource: res,
-                    rrd_data: ResourceRrdData {
-                        start: values.start,
-                        resolution: values.resolution,
-                        data: values.data,
-                    },
-                },
-            ));
-        }
-    }
-
-    None
-}
diff --git a/server/src/metric_collection/top_entities.rs b/server/src/metric_collection/top_entities.rs
new file mode 100644
index 0000000..f8e053f
--- /dev/null
+++ b/server/src/metric_collection/top_entities.rs
@@ -0,0 +1,148 @@
+use std::collections::HashMap;
+
+use pdm_api_types::resource::{Resource, ResourceRrdData, TopEntities, TopEntity};
+
+use super::rrd_cache;
+
+fn insert_sorted<T>(vec: &mut Vec<(usize, T)>, value: (usize, T), limit: usize) {
+    let index = match vec.binary_search_by_key(&value.0, |(idx, _)| *idx) {
+        Ok(idx) | Err(idx) => idx,
+    };
+
+    vec.insert(index, value);
+    if vec.len() > limit {
+        for _ in 0..(vec.len() - limit) {
+            vec.remove(0);
+        }
+    }
+}
+
+// for now simple sum of the values => area under the graph curve
+fn calculate_coefficient(values: &proxmox_rrd::Entry) -> f64 {
+    let mut coefficient = 0.0;
+    for point in values.data.iter() {
+        let value = point.unwrap_or_default();
+        if value.is_finite() {
+            coefficient += value;
+        }
+    }
+
+    coefficient
+}
+
+// FIXME: cache the values instead of calculate freshly every time?
+// FIXME: find better way to enumerate nodes/guests/etc.(instead of relying on the cache)
+pub fn calculate_top(
+    remotes: &HashMap<String, pdm_api_types::remotes::Remote>,
+    timeframe: proxmox_rrd_api_types::RrdTimeframe,
+    num: usize,
+) -> TopEntities {
+    let mut guest_cpu = Vec::new();
+    let mut node_cpu = Vec::new();
+    let mut node_memory = Vec::new();
+
+    for remote_name in remotes.keys() {
+        if let Some(data) =
+            crate::api::resources::get_cached_resources(remote_name, i64::MAX as u64)
+        {
+            for res in data.resources {
+                let id = res.id().to_string();
+                let name = format!("pve/{remote_name}/{id}");
+                match &res {
+                    Resource::PveStorage(_) => {}
+                    Resource::PveQemu(_) | Resource::PveLxc(_) => {
+                        if let Some(entity) =
+                            get_entity(timeframe, remote_name, res, name, "cpu_current")
+                        {
+                            let coefficient = (entity.0 * 100.0).round() as usize;
+                            insert_sorted(&mut guest_cpu, (coefficient, entity.1), num);
+                        }
+                    }
+                    Resource::PveNode(_) => {
+                        if let Some(entity) = get_entity(
+                            timeframe,
+                            remote_name,
+                            res.clone(),
+                            name.clone(),
+                            "cpu_current",
+                        ) {
+                            let coefficient = (entity.0 * 100.0).round() as usize;
+                            insert_sorted(&mut node_cpu, (coefficient, entity.1), num);
+                        }
+                        // convert mem/mem_total into a single entity
+                        if let Some(mut mem) = get_entity(
+                            timeframe,
+                            remote_name,
+                            res.clone(),
+                            name.clone(),
+                            "mem_used",
+                        ) {
+                            if let Some(mem_total) =
+                                get_entity(timeframe, remote_name, res, name, "mem_total")
+                            {
+                                // skip if we don't have the same amount of data for used and total
+                                let mem_rrd = &mem.1.rrd_data.data;
+                                let mem_total_rrd = &mem_total.1.rrd_data.data;
+                                if mem_rrd.len() != mem_total_rrd.len() {
+                                    continue;
+                                }
+                                let coefficient = (100.0 * mem.0 / mem_total.0).round() as usize;
+                                let mut mem_usage = Vec::new();
+                                for i in 0..mem_rrd.len() {
+                                    let point = match (mem_rrd[i], mem_total_rrd[i]) {
+                                        (Some(mem), Some(total)) => Some(mem / total),
+                                        _ => None,
+                                    };
+                                    mem_usage.push(point)
+                                }
+                                mem.1.rrd_data.data = mem_usage;
+                                insert_sorted(&mut node_memory, (coefficient, mem.1), num);
+                            }
+                        }
+                    }
+                    Resource::PbsNode(_) => {}
+                    Resource::PbsDatastore(_) => {}
+                }
+            }
+        }
+    }
+
+    TopEntities {
+        guest_cpu: guest_cpu.into_iter().map(|(_, entity)| entity).collect(),
+        node_cpu: node_cpu.into_iter().map(|(_, entity)| entity).collect(),
+        node_memory: node_memory.into_iter().map(|(_, entity)| entity).collect(),
+    }
+}
+
+fn get_entity(
+    timeframe: proxmox_rrd_api_types::RrdTimeframe,
+    remote_name: &String,
+    res: Resource,
+    name: String,
+    metric: &str,
+) -> Option<(f64, TopEntity)> {
+    if let Ok(Some(values)) = rrd_cache::extract_data(
+        &name,
+        metric,
+        timeframe,
+        proxmox_rrd_api_types::RrdMode::Average,
+    ) {
+        let coefficient = calculate_coefficient(&values);
+        if coefficient > 0.0 {
+            return Some((
+                coefficient,
+                TopEntity {
+                    remote: remote_name.to_string(),
+                    resource: res,
+                    rrd_data: ResourceRrdData {
+                        start: values.start,
+                        resolution: values.resolution,
+                        data: values.data,
+                    },
+                },
+            ));
+        }
+    }
+
+    None
+}
-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


  parent reply	other threads:[~2025-02-11 12:06 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-11 12:05 [pdm-devel] [PATCH proxmox-datacenter-manager 00/25] metric collection improvements (concurrency, config, API, CLI) Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 01/25] test support: add NamedTempFile helper Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 02/25] test support: add NamedTempDir helper Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 03/25] pdm-api-types: add CollectionSettings type Lukas Wagner
2025-02-11 14:18   ` Maximiliano Sandoval
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 04/25] pdm-config: add functions for reading/writing metric collection settings Lukas Wagner
2025-02-11 12:05 ` Lukas Wagner [this message]
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 06/25] metric collection: save metric data to RRD in separate task Lukas Wagner
2025-02-12 13:59   ` Wolfgang Bumiller
2025-02-12 14:32     ` Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 07/25] metric collection: rework metric poll task Lukas Wagner
2025-02-11 12:58   ` Lukas Wagner
2025-02-12 15:57   ` Wolfgang Bumiller
2025-02-13 12:31     ` Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 08/25] metric collection: persist state after metric collection Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 09/25] metric collection: skip if last_collection < MIN_COLLECTION_INTERVAL Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 10/25] metric collection: collect overdue metrics on startup/timer change Lukas Wagner
2025-02-13  8:55   ` Wolfgang Bumiller
2025-02-13 13:50     ` Lukas Wagner
2025-02-13 14:19       ` Wolfgang Bumiller
2025-02-13 15:21         ` Lukas Wagner
2025-02-13 15:34           ` Wolfgang Bumiller
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 11/25] metric collection: add tests for the fetch_remotes function Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 12/25] metric collection: add test for fetch_overdue Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 13/25] metric collection: pass rrd cache instance as function parameter Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 14/25] metric collection: add test for rrd task Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 15/25] metric collection: wrap rrd_cache::Cache in a struct Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 16/25] metric collection: record remote response time in metric database Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 17/25] metric collection: save time needed for collection run to RRD Lukas Wagner
2025-02-13 11:53   ` Wolfgang Bumiller
2025-02-13 12:12     ` Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 18/25] metric collection: periodically clean removed remotes from statefile Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 19/25] api: add endpoint for updating metric collection settings Lukas Wagner
2025-02-13 12:09   ` Wolfgang Bumiller
2025-02-13 12:15     ` Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 20/25] api: add endpoint to trigger metric collection Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 21/25] api: remotes: trigger immediate metric collection for newly added nodes Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 22/25] api: add api for querying metric collection RRD data Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 23/25] api: metric-collection: add status endpoint Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 24/25] pdm-client: add metric collection API methods Lukas Wagner
2025-02-13 12:10   ` Wolfgang Bumiller
2025-02-13 13:52     ` Lukas Wagner
2025-02-11 12:05 ` [pdm-devel] [PATCH proxmox-datacenter-manager 25/25] cli: add commands for metric-collection settings, trigger, status Lukas Wagner
2025-02-13 12:14   ` Wolfgang Bumiller
2025-02-13 14:17     ` Lukas Wagner
2025-02-13 14:56       ` Wolfgang Bumiller
2025-02-13 14:58         ` Lukas Wagner
2025-02-13 15:11           ` Lukas Wagner
2025-02-14 13:08 ` [pdm-devel] [PATCH proxmox-datacenter-manager 00/25] metric collection improvements (concurrency, config, API, CLI) Lukas Wagner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250211120541.163621-6-l.wagner@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal