From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 78DD97537E for ; Wed, 13 Oct 2021 10:25:06 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9843FE6DF for ; Wed, 13 Oct 2021 10:25:05 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id D3B69E542 for ; Wed, 13 Oct 2021 10:24:58 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id AB7DB4451C; Wed, 13 Oct 2021 10:24:58 +0200 (CEST) From: Dietmar Maurer To: pbs-devel@lists.proxmox.com Date: Wed, 13 Oct 2021 10:24:52 +0200 Message-Id: <20211013082452.619406-16-dietmar@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211013082452.619406-1-dietmar@proxmox.com> References: <20211013082452.619406-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.541 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [rrd.rs] Subject: [pbs-devel] [PATCH proxmox-backup 15/15] proxmox-rrd: add more commands to the rrd cli tool X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 13 Oct 2021 08:25:06 -0000 --- 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, 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, @@ -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