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 2A04675405 for ; Wed, 13 Oct 2021 10:25:08 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A3FB4E7C3 for ; Wed, 13 Oct 2021 10:25:07 +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 D7900E512 for ; Wed, 13 Oct 2021 10:24:57 +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 AF7A845DCC; Wed, 13 Oct 2021 10:24:57 +0200 (CEST) From: Dietmar Maurer To: pbs-devel@lists.proxmox.com Date: Wed, 13 Oct 2021 10:24:47 +0200 Message-Id: <20211013082452.619406-11-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.489 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 PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far 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 10/15] proxmox-rrd: add binary to create/manage rrd files 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:08 -0000 --- proxmox-rrd/Cargo.toml | 1 + proxmox-rrd/src/bin/rrd.rs | 215 +++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 proxmox-rrd/src/bin/rrd.rs diff --git a/proxmox-rrd/Cargo.toml b/proxmox-rrd/Cargo.toml index 69a68530..e3ab5380 100644 --- a/proxmox-rrd/Cargo.toml +++ b/proxmox-rrd/Cargo.toml @@ -16,4 +16,5 @@ serde_cbor = "0.11.1" proxmox = { version = "0.14.0" } proxmox-time = "1" +proxmox-router = "1" proxmox-schema = { version = "1", features = [ "api-macro" ] } diff --git a/proxmox-rrd/src/bin/rrd.rs b/proxmox-rrd/src/bin/rrd.rs new file mode 100644 index 00000000..fdb61ffd --- /dev/null +++ b/proxmox-rrd/src/bin/rrd.rs @@ -0,0 +1,215 @@ +//! RRD toolkit - create/manage/update proxmox RRD (v2) file + +use std::path::PathBuf; + +use anyhow::{bail, Error}; +use serde::{Serialize, Deserialize}; + +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::tools::fs::CreateOptions; + +use proxmox_rrd::rrd::{CF, DST, RRA, RRD}; + +pub const RRA_CONFIG_STRING_SCHEMA: Schema = StringSchema::new( + "RRA configuration") + .format(&ApiStringFormat::PropertyString(&RRAConfig::API_SCHEMA)) + .schema(); + +#[api( + properties: {}, + default_key: "cf", +)] +#[derive(Debug, Serialize, Deserialize)] +/// RRA configuration +pub struct RRAConfig { + /// Time resolution + pub r: u64, + pub cf: CF, + /// Number of data points + pub n: u64, +} + +#[api( + input: { + properties: { + path: { + description: "The filename." + }, + }, + }, +)] +/// Dump the RRDB database in JSON format +pub fn dump_rrdb(path: String) -> Result<(), Error> { + + let rrd = RRD::load(&PathBuf::from(path))?; + serde_json::to_writer_pretty(std::io::stdout(), &rrd)?; + Ok(()) +} + +#[api( + input: { + properties: { + path: { + description: "The filename." + }, + time: { + description: "Update time.", + optional: true, + }, + value: { + description: "Update value.", + }, + }, + }, +)] +/// Update the RRDB database +pub fn update_rrdb( + path: String, + time: Option, + value: f64, +) -> Result<(), Error> { + + let path = PathBuf::from(path); + + let time = time.map(|v| v as f64) + .unwrap_or_else(proxmox_time::epoch_f64); + + let mut rrd = RRD::load(&path)?; + rrd.update(time, value); + + rrd.save(&path, CreateOptions::new())?; + + Ok(()) +} + +#[api( + input: { + properties: { + path: { + description: "The filename." + }, + cf: { + type: CF, + }, + resolution: { + description: "Time resulution", + }, + start: { + description: "Start time. If not sepecified, we simply extract 10 data points.", + optional: true, + }, + end: { + description: "End time (Unix Epoch). Default is the last update time.", + optional: true, + }, + }, + }, +)] +/// Fetch data from the RRDB database +pub fn fetch_rrdb( + path: String, + cf: CF, + resolution: u64, + start: Option, + end: Option, +) -> Result<(), Error> { + + let rrd = RRD::load(&PathBuf::from(path))?; + + let data = rrd.extract_data(cf, resolution, start, end)?; + + println!("{}", serde_json::to_string_pretty(&data)?); + + Ok(()) +} + +#[api( + input: { + properties: { + dst: { + type: DST, + }, + path: { + description: "The filename to create." + }, + rra: { + description: "Configuration of contained RRAs.", + type: Array, + items: { + schema: RRA_CONFIG_STRING_SCHEMA, + } + }, + }, + }, +)] +/// Create a new RRDB database file +pub fn create_rrdb( + dst: DST, + path: String, + rra: Vec, +) -> Result<(), Error> { + + let mut rra_list = Vec::new(); + + for item in rra.iter() { + let rra: RRAConfig = serde_json::from_value( + parse_property_string(item, &RRAConfig::API_SCHEMA)? + )?; + println!("GOT {:?}", rra); + rra_list.push(RRA::new(rra.cf, rra.r, rra.n as usize)); + } + + let path = PathBuf::from(path); + + let rrd = RRD::new(dst, rra_list); + + rrd.save(&path, CreateOptions::new())?; + + Ok(()) +} + + +fn main() -> Result<(), Error> { + + let uid = nix::unistd::Uid::current(); + + let username = match nix::unistd::User::from_uid(uid)? { + Some(user) => user.name, + None => bail!("unable to get user name"), + }; + + let cmd_def = CliCommandMap::new() + .insert( + "create", + CliCommand::new(&API_METHOD_CREATE_RRDB) + .arg_param(&["path"]) + ) + .insert( + "update", + CliCommand::new(&API_METHOD_UPDATE_RRDB) + .arg_param(&["path"]) + ) + .insert( + "fetch", + CliCommand::new(&API_METHOD_FETCH_RRDB) + .arg_param(&["path"]) + ) + .insert( + "dump", + CliCommand::new(&API_METHOD_DUMP_RRDB) + .arg_param(&["path"]) + ) + ; + + let mut rpcenv = CliEnvironment::new(); + rpcenv.set_auth_id(Some(format!("{}@pam", username))); + + run_cli_command(cmd_def, rpcenv, None); + + Ok(()) + +} -- 2.30.2