4 use bitflags
::bitflags
;
6 /// The number of data entries per RRA
7 pub const RRD_DATA_ENTRIES
: usize = 70;
9 /// Proxmox RRD file magic number
10 // openssl::sha::sha256(b"Proxmox Round Robin Database file v1.0")[0..8];
11 pub const PROXMOX_RRD_MAGIC_1_0
: [u8; 8] = [206, 46, 26, 212, 172, 158, 5, 186];
13 use crate::rrd
::{DataSource, CF, DST, RRA, RRD}
;
16 /// Flags to specify the data source type and consolidation function
17 pub struct RRAFlags
: u64 {
21 const DST_COUNTER
= 4;
22 const DST_MASK
= 255; // first 8 bits
24 // Consolidation Functions
25 const CF_AVERAGE
= 1 << 8;
26 const CF_MAX
= 2 << 8;
27 const CF_MASK
= 255 << 8;
31 /// Round Robin Archive with [RRD_DATA_ENTRIES] data slots.
33 /// This data structure is used inside [RRD] and directly written to the
37 /// Defined the data source type and consolidation function
39 /// Resolution (seconds)
41 /// Last update time (epoch)
43 /// Count values computed inside this update interval
45 /// Stores the last value, used to compute differential value for derive/counters
46 pub counter_value
: f64,
48 pub data
: [f64; RRD_DATA_ENTRIES
],
52 fn extract_data(&self) -> (u64, u64, Vec
<Option
<f64>>) {
53 let reso
= self.resolution
;
55 let mut list
= Vec
::new();
57 let rra_end
= reso
* ((self.last_update
as u64) / reso
);
58 let rra_start
= rra_end
- reso
* (RRD_DATA_ENTRIES
as u64);
60 let mut t
= rra_start
;
61 let mut index
= ((t
/ reso
) % (RRD_DATA_ENTRIES
as u64)) as usize;
62 for _
in 0..RRD_DATA_ENTRIES
{
63 let value
= self.data
[index
];
67 list
.push(Some(value
));
71 index
= (index
+ 1) % RRD_DATA_ENTRIES
;
74 (rra_start
, reso
, list
)
78 /// Round Robin Database file format with fixed number of [RRA]s
80 // Note: Avoid alignment problems by using 8byte types only
82 /// The magic number to identify the file type
84 /// Hourly data (average values)
86 /// Hourly data (maximum values)
88 /// Daily data (average values)
90 /// Daily data (maximum values)
92 /// Weekly data (average values)
94 /// Weekly data (maximum values)
96 /// Monthly data (average values)
98 /// Monthly data (maximum values)
100 /// Yearly data (average values)
102 /// Yearly data (maximum values)
107 pub fn from_raw(mut raw
: &[u8]) -> Result
<Self, std
::io
::Error
> {
108 let expected_len
= std
::mem
::size_of
::<RRDv1
>();
110 if raw
.len() != expected_len
{
111 let msg
= format
!("wrong data size ({} != {})", raw
.len(), expected_len
);
112 return Err(std
::io
::Error
::new(std
::io
::ErrorKind
::Other
, msg
));
115 let mut rrd
: RRDv1
= unsafe { std::mem::zeroed() }
;
118 std
::slice
::from_raw_parts_mut(&mut rrd
as *mut _
as *mut u8, expected_len
);
119 raw
.read_exact(rrd_slice
)?
;
122 if rrd
.magic
!= PROXMOX_RRD_MAGIC_1_0
{
123 let msg
= "wrong magic number".to_string();
124 return Err(std
::io
::Error
::new(std
::io
::ErrorKind
::Other
, msg
));
130 pub fn to_rrd_v2(&self) -> Result
<RRD
, Error
> {
131 let mut rra_list
= Vec
::new();
135 // hour 1 min, 70 points
136 // day 30 min, 70 points
137 // week 3 hours, 70 points
138 // month 12 hours, 70 points
139 // year 1 week, 70 points
141 // new default for RRD v2:
143 // day 1 min, 1440 points
144 // month 30 min, 1440 points
145 // year 365 min (6h), 1440 points
146 // decade 1 week, 570 points
148 // Linear extrapolation
153 data
: Vec
<Option
<f64>>,
154 ) -> (u64, u64, Vec
<Option
<f64>>) {
155 let mut new
= Vec
::new();
157 for i
in 0..data
.len() {
158 let mut next
= i
+ 1;
159 if next
>= data
.len() {
165 (Some(v
), Some(v1
)) => {
166 let diff
= (v1
- v
) / (factor
as f64);
168 new
.push(Some(v
+ diff
* (j
as f64)));
173 for _
in 0..factor
- 1 {
177 (None
, Some(v1
)) => {
178 for _
in 0..factor
- 1 {
191 (start
, reso
/ factor
, new
)
194 // Try to convert to new, higher capacity format
196 // compute daily average (merge old self.day_avg and self.hour_avg
197 let mut day_avg
= RRA
::new(CF
::Average
, 60, 1440);
199 let (start
, reso
, data
) = self.day_avg
.extract_data();
200 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 30, data
);
201 day_avg
.insert_data(start
, reso
, data
)?
;
203 let (start
, reso
, data
) = self.hour_avg
.extract_data();
204 day_avg
.insert_data(start
, reso
, data
)?
;
206 // compute daily maximum (merge old self.day_max and self.hour_max
207 let mut day_max
= RRA
::new(CF
::Maximum
, 60, 1440);
209 let (start
, reso
, data
) = self.day_max
.extract_data();
210 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 30, data
);
211 day_max
.insert_data(start
, reso
, data
)?
;
213 let (start
, reso
, data
) = self.hour_max
.extract_data();
214 day_max
.insert_data(start
, reso
, data
)?
;
216 // compute monthly average (merge old self.month_avg,
217 // self.week_avg and self.day_avg)
218 let mut month_avg
= RRA
::new(CF
::Average
, 30 * 60, 1440);
220 let (start
, reso
, data
) = self.month_avg
.extract_data();
221 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 24, data
);
222 month_avg
.insert_data(start
, reso
, data
)?
;
224 let (start
, reso
, data
) = self.week_avg
.extract_data();
225 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 6, data
);
226 month_avg
.insert_data(start
, reso
, data
)?
;
228 let (start
, reso
, data
) = self.day_avg
.extract_data();
229 month_avg
.insert_data(start
, reso
, data
)?
;
231 // compute monthly maximum (merge old self.month_max,
232 // self.week_max and self.day_max)
233 let mut month_max
= RRA
::new(CF
::Maximum
, 30 * 60, 1440);
235 let (start
, reso
, data
) = self.month_max
.extract_data();
236 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 24, data
);
237 month_max
.insert_data(start
, reso
, data
)?
;
239 let (start
, reso
, data
) = self.week_max
.extract_data();
240 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 6, data
);
241 month_max
.insert_data(start
, reso
, data
)?
;
243 let (start
, reso
, data
) = self.day_max
.extract_data();
244 month_max
.insert_data(start
, reso
, data
)?
;
246 // compute yearly average (merge old self.year_avg)
247 let mut year_avg
= RRA
::new(CF
::Average
, 6 * 3600, 1440);
249 let (start
, reso
, data
) = self.year_avg
.extract_data();
250 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 28, data
);
251 year_avg
.insert_data(start
, reso
, data
)?
;
253 // compute yearly maximum (merge old self.year_avg)
254 let mut year_max
= RRA
::new(CF
::Maximum
, 6 * 3600, 1440);
256 let (start
, reso
, data
) = self.year_max
.extract_data();
257 let (start
, reso
, data
) = extrapolate_data(start
, reso
, 28, data
);
258 year_max
.insert_data(start
, reso
, data
)?
;
260 // compute decade average (merge old self.year_avg)
261 let mut decade_avg
= RRA
::new(CF
::Average
, 7 * 86400, 570);
262 let (start
, reso
, data
) = self.year_avg
.extract_data();
263 decade_avg
.insert_data(start
, reso
, data
)?
;
265 // compute decade maximum (merge old self.year_max)
266 let mut decade_max
= RRA
::new(CF
::Maximum
, 7 * 86400, 570);
267 let (start
, reso
, data
) = self.year_max
.extract_data();
268 decade_max
.insert_data(start
, reso
, data
)?
;
270 rra_list
.push(day_avg
);
271 rra_list
.push(day_max
);
272 rra_list
.push(month_avg
);
273 rra_list
.push(month_max
);
274 rra_list
.push(year_avg
);
275 rra_list
.push(year_max
);
276 rra_list
.push(decade_avg
);
277 rra_list
.push(decade_max
);
279 // use values from hour_avg for source (all RRAv1 must have the same config)
280 let dst
= if self.hour_avg
.flags
.contains(RRAFlags
::DST_COUNTER
) {
282 } else if self.hour_avg
.flags
.contains(RRAFlags
::DST_DERIVE
) {
288 let source
= DataSource
{
290 last_value
: f64::NAN
,
291 last_update
: self.hour_avg
.last_update
, // IMPORTANT!
293 Ok(RRD { source, rra_list }
)