From: Dietmar Maurer <dietmar@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 15/15] proxmox-rrd: add more commands to the rrd cli tool
Date: Wed, 13 Oct 2021 10:24:52 +0200 [thread overview]
Message-ID: <20211013082452.619406-16-dietmar@proxmox.com> (raw)
In-Reply-To: <20211013082452.619406-1-dietmar@proxmox.com>
---
proxmox-rrd/src/bin/rrd.rs | 229 ++++++++++++++++++++++++++++++++++---
proxmox-rrd/src/rrd.rs | 4 +-
2 files changed, 215 insertions(+), 18 deletions(-)
diff --git a/proxmox-rrd/src/bin/rrd.rs b/proxmox-rrd/src/bin/rrd.rs
index fdb61ffd..bf2817c4 100644
--- a/proxmox-rrd/src/bin/rrd.rs
+++ b/proxmox-rrd/src/bin/rrd.rs
@@ -4,16 +4,22 @@ use std::path::PathBuf;
use anyhow::{bail, Error};
use serde::{Serialize, Deserialize};
+use serde_json::json;
use proxmox_router::RpcEnvironment;
use proxmox_router::cli::{run_cli_command, CliCommand, CliCommandMap, CliEnvironment};
use proxmox_schema::{api, parse_property_string};
-use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema};
+use proxmox_schema::{ApiStringFormat, ApiType, IntegerSchema, Schema, StringSchema};
use proxmox::tools::fs::CreateOptions;
use proxmox_rrd::rrd::{CF, DST, RRA, RRD};
+pub const RRA_INDEX_SCHEMA: Schema = IntegerSchema::new(
+ "Index of the RRA.")
+ .minimum(0)
+ .schema();
+
pub const RRA_CONFIG_STRING_SCHEMA: Schema = StringSchema::new(
"RRA configuration")
.format(&ApiStringFormat::PropertyString(&RRAConfig::API_SCHEMA))
@@ -42,11 +48,36 @@ pub struct RRAConfig {
},
},
)]
-/// Dump the RRDB database in JSON format
-pub fn dump_rrdb(path: String) -> Result<(), Error> {
+/// Dump the RRD file in JSON format
+pub fn dump_rrd(path: String) -> Result<(), Error> {
let rrd = RRD::load(&PathBuf::from(path))?;
serde_json::to_writer_pretty(std::io::stdout(), &rrd)?;
+ println!("");
+ Ok(())
+}
+
+#[api(
+ input: {
+ properties: {
+ path: {
+ description: "The filename."
+ },
+ },
+ },
+)]
+/// RRD file information
+pub fn rrd_info(path: String) -> Result<(), Error> {
+
+ let rrd = RRD::load(&PathBuf::from(path))?;
+
+ println!("DST: {:?}", rrd.source.dst);
+
+ for (i, rra) in rrd.rra_list.iter().enumerate() {
+ // use RRAConfig property string format
+ println!("RRA[{}]: {:?},r={},n={}", i, rra.cf, rra.resolution, rra.data.len());
+ }
+
Ok(())
}
@@ -66,8 +97,8 @@ pub fn dump_rrdb(path: String) -> Result<(), Error> {
},
},
)]
-/// Update the RRDB database
-pub fn update_rrdb(
+/// Update the RRD database
+pub fn update_rrd(
path: String,
time: Option<u64>,
value: f64,
@@ -109,8 +140,8 @@ pub fn update_rrdb(
},
},
)]
-/// Fetch data from the RRDB database
-pub fn fetch_rrdb(
+/// Fetch data from the RRD file
+pub fn fetch_rrd(
path: String,
cf: CF,
resolution: u64,
@@ -127,6 +158,80 @@ pub fn fetch_rrdb(
Ok(())
}
+#[api(
+ input: {
+ properties: {
+ path: {
+ description: "The filename."
+ },
+ "rra-index": {
+ schema: RRA_INDEX_SCHEMA,
+ },
+ },
+ },
+)]
+/// Return the Unix timestamp of the first time slot inside the
+/// specified RRA (slot start time)
+pub fn first_update_time(
+ path: String,
+ rra_index: usize,
+) -> Result<(), Error> {
+
+ let rrd = RRD::load(&PathBuf::from(path))?;
+
+ if rra_index >= rrd.rra_list.len() {
+ bail!("rra-index is out of range");
+ }
+ let rra = &rrd.rra_list[rra_index];
+ let duration = (rra.data.len() as u64)*rra.resolution;
+ let first = rra.slot_start_time((rrd.source.last_update as u64).saturating_sub(duration));
+
+ println!("{}", first);
+ Ok(())
+}
+
+#[api(
+ input: {
+ properties: {
+ path: {
+ description: "The filename."
+ },
+ },
+ },
+)]
+/// Return the Unix timestamp of the last update
+pub fn last_update_time(path: String) -> Result<(), Error> {
+
+ let rrd = RRD::load(&PathBuf::from(path))?;
+
+ println!("{}", rrd.source.last_update);
+ Ok(())
+}
+
+#[api(
+ input: {
+ properties: {
+ path: {
+ description: "The filename."
+ },
+ },
+ },
+)]
+/// Return the time and value from the last update
+pub fn last_update(path: String) -> Result<(), Error> {
+
+ let rrd = RRD::load(&PathBuf::from(path))?;
+
+ let result = json!({
+ "time": rrd.source.last_update,
+ "value": rrd.source.last_value,
+ });
+
+ println!("{}", serde_json::to_string_pretty(&result)?);
+
+ Ok(())
+}
+
#[api(
input: {
properties: {
@@ -146,8 +251,8 @@ pub fn fetch_rrdb(
},
},
)]
-/// Create a new RRDB database file
-pub fn create_rrdb(
+/// Create a new RRD file
+pub fn create_rrd(
dst: DST,
path: String,
rra: Vec<String>,
@@ -172,6 +277,64 @@ pub fn create_rrdb(
Ok(())
}
+#[api(
+ input: {
+ properties: {
+ path: {
+ description: "The filename."
+ },
+ "rra-index": {
+ schema: RRA_INDEX_SCHEMA,
+ },
+ slots: {
+ description: "The number of slots you want to add or remove.",
+ type: i64,
+ },
+ },
+ },
+)]
+/// Resize. Change the number of data slots for the specified RRA.
+pub fn resize_rrd(
+ path: String,
+ rra_index: usize,
+ slots: i64,
+) -> Result<(), Error> {
+
+ let path = PathBuf::from(&path);
+
+ let mut rrd = RRD::load(&path)?;
+
+ if rra_index >= rrd.rra_list.len() {
+ bail!("rra-index is out of range");
+ }
+
+ let rra = &rrd.rra_list[rra_index];
+
+ let new_slots = (rra.data.len() as i64) + slots;
+
+ if new_slots < 1 {
+ bail!("numer of new slots is too small ('{}' < 1)", new_slots);
+ }
+
+ if new_slots > 1024*1024 {
+ bail!("numer of new slots is too big ('{}' > 1M)", new_slots);
+ }
+
+ let rra_end = rra.slot_end_time(rrd.source.last_update as u64);
+ let rra_start = rra_end - rra.resolution*(rra.data.len() as u64);
+ let (start, reso, data) = rra.extract_data(rra_start, rra_end, rrd.source.last_update);
+
+ let mut new_rra = RRA::new(rra.cf, rra.resolution, new_slots as usize);
+ new_rra.last_count = rra.last_count;
+
+ new_rra.insert_data(start, reso, data)?;
+
+ rrd.rra_list[rra_index] = new_rra;
+
+ rrd.save(&path, CreateOptions::new())?;
+
+ Ok(())
+}
fn main() -> Result<(), Error> {
@@ -185,23 +348,57 @@ fn main() -> Result<(), Error> {
let cmd_def = CliCommandMap::new()
.insert(
"create",
- CliCommand::new(&API_METHOD_CREATE_RRDB)
+ CliCommand::new(&API_METHOD_CREATE_RRD)
.arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
)
.insert(
- "update",
- CliCommand::new(&API_METHOD_UPDATE_RRDB)
+ "dump",
+ CliCommand::new(&API_METHOD_DUMP_RRD)
.arg_param(&["path"])
- )
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
.insert(
"fetch",
- CliCommand::new(&API_METHOD_FETCH_RRDB)
+ CliCommand::new(&API_METHOD_FETCH_RRD)
.arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
+ .insert(
+ "first",
+ CliCommand::new(&API_METHOD_FIRST_UPDATE_TIME)
+ .arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
)
.insert(
- "dump",
- CliCommand::new(&API_METHOD_DUMP_RRDB)
+ "info",
+ CliCommand::new(&API_METHOD_RRD_INFO)
+ .arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
+ .insert(
+ "last",
+ CliCommand::new(&API_METHOD_LAST_UPDATE_TIME)
+ .arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
+ .insert(
+ "lastupdate",
+ CliCommand::new(&API_METHOD_LAST_UPDATE)
+ .arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
+ .insert(
+ "resize",
+ CliCommand::new(&API_METHOD_RESIZE_RRD)
+ .arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
+ )
+ .insert(
+ "update",
+ CliCommand::new(&API_METHOD_UPDATE_RRD)
.arg_param(&["path"])
+ //.completion_cb("path", pbs_tools::fs::complete_file_name)
)
;
diff --git a/proxmox-rrd/src/rrd.rs b/proxmox-rrd/src/rrd.rs
index 72769bfd..c6996e42 100644
--- a/proxmox-rrd/src/rrd.rs
+++ b/proxmox-rrd/src/rrd.rs
@@ -153,7 +153,7 @@ impl RRA {
// directly overwrite data slots
// the caller need to set last_update value on the DataSource manually.
- pub(crate) fn insert_data(
+ pub fn insert_data(
&mut self,
start: u64,
resolution: u64,
@@ -238,7 +238,7 @@ impl RRA {
}
}
- fn extract_data(
+ pub fn extract_data(
&self,
start: u64,
end: u64,
--
2.30.2
next prev parent reply other threads:[~2021-10-13 8:25 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-13 8:24 [pbs-devel] [PATCH proxmox-backup 00/15] RRD database improvements Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 01/15] proxmox-rrd: use a journal to reduce amount of bytes written Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 02/15] RRD_CACHE: use a OnceCell instead of lazy_static Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 03/15] proxmox-backup-proxy: use tokio::task::spawn_blocking instead of block_in_place Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 04/15] proxmox-rrd: implement new CBOR based format Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 05/15] proxmox-rrd: remove dependency to proxmox-rrd-api-types Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 06/15] proxmox-rrd: extract_data: include values from current slot Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 07/15] remove proxmox-rrd-api-types crate, s/RRDTimeFrameResolution/RRDTimeFrame/ Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 08/15] proxmox-rrd: support CF::Last Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 09/15] proxmox-rrd: split out load_rrd (cleanup) Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 10/15] proxmox-rrd: add binary to create/manage rrd files Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 11/15] proxmox-rrd: avoid % inside loop Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 12/15] proxmox-rrd: new helper methods - slot() and slot_end_time() Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 13/15] proxmox-rrd: protect against negative update time Dietmar Maurer
2021-10-13 8:24 ` [pbs-devel] [PATCH proxmox-backup 14/15] proxmox-rrd: rename last_counter to last_value Dietmar Maurer
2021-10-13 8:24 ` Dietmar Maurer [this message]
2021-10-13 11:58 ` [pbs-devel] applied-series: [PATCH proxmox-backup 00/15] RRD database improvements Thomas Lamprecht
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=20211013082452.619406-16-dietmar@proxmox.com \
--to=dietmar@proxmox.com \
--cc=pbs-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.