diff --git a/proxmox-rrd/src/cache.rs b/proxmox-rrd/src/cache.rs index 550eb30c..d884d01d 100644 --- a/proxmox-rrd/src/cache.rs +++ b/proxmox-rrd/src/cache.rs @@ -8,7 +8,7 @@ use proxmox::tools::fs::{create_path, CreateOptions}; use proxmox_rrd_api_types::{RRDMode, RRDTimeFrameResolution}; -use super::*; +use crate::{DST, rrd::RRD}; /// RRD cache - keep RRD data in RAM, but write updates to disk /// diff --git a/proxmox-rrd/src/lib.rs b/proxmox-rrd/src/lib.rs index 446aff04..303cd55d 100644 --- a/proxmox-rrd/src/lib.rs +++ b/proxmox-rrd/src/lib.rs @@ -1,5 +1,21 @@ -mod rrd; -pub use rrd::*; +//! # Simple Round Robin Database files with fixed format +//! +//! ## Features +//! +//! * One file stores a single data source +//! * Small/constant file size (6008 bytes) +//! * Stores avarage and maximum values +//! * Stores data for different time resolution ([RRDTimeFrameResolution](proxmox_rrd_api_types::RRDTimeFrameResolution)) + +pub mod rrd; mod cache; pub use cache::*; + +/// RRD data source tyoe +pub enum DST { + /// Gauge values are stored unmodified. + Gauge, + /// Stores the difference to the previous value. + Derive, +} diff --git a/proxmox-rrd/src/rrd.rs b/proxmox-rrd/src/rrd.rs index 526d36b3..8ea182c7 100644 --- a/proxmox-rrd/src/rrd.rs +++ b/proxmox-rrd/src/rrd.rs @@ -1,7 +1,10 @@ +//! # Round Robin Database file format + use std::io::Read; use std::path::Path; use anyhow::Error; +use bitflags::bitflags; use proxmox::tools::{fs::replace_file, fs::CreateOptions}; @@ -14,10 +17,11 @@ pub const RRD_DATA_ENTRIES: usize = 70; // openssl::sha::sha256(b"Proxmox Round Robin Database file v1.0")[0..8]; pub const PROXMOX_RRD_MAGIC_1_0: [u8; 8] = [206, 46, 26, 212, 172, 158, 5, 186]; -use bitflags::bitflags; +use crate::DST; bitflags!{ - struct RRAFlags: u64 { + /// Flags to specify the data soure type and consolidation function + pub struct RRAFlags: u64 { // Data Source Types const DST_GAUGE = 1; const DST_DERIVE = 2; @@ -31,20 +35,24 @@ bitflags!{ } } -/// RRD data source tyoe -pub enum DST { - Gauge, - Derive, -} - +/// Round Robin Archive with [RRD_DATA_ENTRIES] data slots. +/// +/// This data structure is used inside [RRD] and directly written to the +/// RRD files. #[repr(C)] -struct RRA { - flags: RRAFlags, - resolution: u64, - last_update: f64, - last_count: u64, - counter_value: f64, // used for derive/counters - data: [f64; RRD_DATA_ENTRIES], +pub struct RRA { + /// Defined the data soure type and consolidation function + pub flags: RRAFlags, + /// Resulution (seconds) from [RRDTimeFrameResolution] + pub resolution: u64, + /// Last update time (epoch) + pub last_update: f64, + /// Count values computed inside this update interval + pub last_count: u64, + /// Stores the last value, used to compute differential value for derive/counters + pub counter_value: f64, + /// Data slots + pub data: [f64; RRD_DATA_ENTRIES], } impl RRA { @@ -157,24 +165,37 @@ impl RRA { } } +/// Round Robin Database file format with fixed number of [RRA]s #[repr(C)] // Note: Avoid alignment problems by using 8byte types only pub struct RRD { - magic: [u8; 8], - hour_avg: RRA, - hour_max: RRA, - day_avg: RRA, - day_max: RRA, - week_avg: RRA, - week_max: RRA, - month_avg: RRA, - month_max: RRA, - year_avg: RRA, - year_max: RRA, + /// The magic number to identify the file type + pub magic: [u8; 8], + /// Hourly data (average values) + pub hour_avg: RRA, + /// Hourly data (maximum values) + pub hour_max: RRA, + /// Dayly data (average values) + pub day_avg: RRA, + /// Dayly data (maximum values) + pub day_max: RRA, + /// Weekly data (average values) + pub week_avg: RRA, + /// Weekly data (maximum values) + pub week_max: RRA, + /// Monthly data (average values) + pub month_avg: RRA, + /// Monthly data (maximum values) + pub month_max: RRA, + /// Yearly data (average values) + pub year_avg: RRA, + /// Yearly data (maximum values) + pub year_max: RRA, } impl RRD { + /// Create a new empty instance pub fn new(dst: DST) -> Self { let flags = match dst { DST::Gauge => RRAFlags::DST_GAUGE, @@ -226,6 +247,7 @@ impl RRD { } } + /// Extract data from the archive pub fn extract_data( &self, time: f64, @@ -276,6 +298,7 @@ impl RRD { (start, reso, list) } + /// Create instance from raw data, testing data len and magic number pub fn from_raw(mut raw: &[u8]) -> Result { let expected_len = std::mem::size_of::(); if raw.len() != expected_len { @@ -297,11 +320,13 @@ impl RRD { Ok(rrd) } + /// Load data from a file pub fn load(path: &Path) -> Result { let raw = std::fs::read(path)?; Self::from_raw(&raw) } + /// Store data into a file (atomic replace file) pub fn save(&self, filename: &Path, options: CreateOptions) -> Result<(), Error> { let rrd_slice = unsafe { std::slice::from_raw_parts(self as *const _ as *const u8, std::mem::size_of::()) @@ -309,7 +334,9 @@ impl RRD { replace_file(filename, rrd_slice, options) } - + /// Update the value (in memory) + /// + /// Note: This does not call [Self::save]. pub fn update(&mut self, time: f64, value: f64) { self.hour_avg.update(time, value); self.hour_max.update(time, value); diff --git a/src/api2/node/rrd.rs b/src/api2/node/rrd.rs index df79b872..00555030 100644 --- a/src/api2/node/rrd.rs +++ b/src/api2/node/rrd.rs @@ -7,7 +7,7 @@ use pbs_api_types::{ NODE_SCHEMA, RRDMode, RRDTimeFrameResolution, PRIV_SYS_AUDIT, }; -use proxmox_rrd::RRD_DATA_ENTRIES; +use proxmox_rrd::rrd::RRD_DATA_ENTRIES; use crate::RRD_CACHE;