6 use crate::api2
::types
::{RRDMode, RRDTimeFrameResolution}
;
8 pub const RRD_DATA_ENTRIES
: usize = 70;
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 bitflags
::bitflags
;
16 pub struct RRAFlags
: u64 {
20 const DST_COUNTER
= 4;
21 const DST_MASK
= 255; // first 8 bits
23 // Consolidation Functions
24 const CF_AVERAGE
= 1 << 8;
25 const CF_MAX
= 2 << 8;
26 const CF_MASK
= 255 << 8;
41 counter_value
: f64, // used for derive/counters
42 data
: [f64; RRD_DATA_ENTRIES
],
46 fn new(flags
: RRAFlags
, resolution
: u64) -> Self {
51 counter_value
: f64::NAN
,
52 data
: [f64::NAN
; RRD_DATA_ENTRIES
],
56 fn delete_old(&mut self, time
: f64) {
57 let epoch
= time
as u64;
58 let last_update
= self.last_update
as u64;
59 let reso
= self.resolution
;
61 let min_time
= epoch
- (RRD_DATA_ENTRIES
as u64)*reso
;
62 let min_time
= (min_time
/reso
+ 1)*reso
;
63 let mut t
= last_update
- (RRD_DATA_ENTRIES
as u64)*reso
;
64 let mut index
= ((t
/reso
) % (RRD_DATA_ENTRIES
as u64)) as usize;
65 for _
in 0..RRD_DATA_ENTRIES
{
66 t
+= reso
; index
= (index
+ 1) % RRD_DATA_ENTRIES
;
68 self.data
[index
] = f64::NAN
;
75 fn compute_new_value(&mut self, time
: f64, value
: f64) {
76 let epoch
= time
as u64;
77 let last_update
= self.last_update
as u64;
78 let reso
= self.resolution
;
80 let index
= ((epoch
/reso
) % (RRD_DATA_ENTRIES
as u64)) as usize;
81 let last_index
= ((last_update
/reso
) % (RRD_DATA_ENTRIES
as u64)) as usize;
83 if (epoch
- (last_update
as u64)) > reso
|| index
!= last_index
{
87 let last_value
= self.data
[index
];
88 if last_value
.is_nan() {
92 let new_count
= if self.last_count
< u64::MAX
{
95 u64::MAX
// should never happen
98 if self.last_count
== 0 {
99 self.data
[index
] = value
;
102 let new_value
= if self.flags
.contains(RRAFlags
::CF_MAX
) {
103 if last_value
> value { last_value }
else { value }
104 } else if self.flags
.contains(RRAFlags
::CF_AVERAGE
) {
105 (last_value
*(self.last_count
as f64))/(new_count
as f64)
106 + value
/(new_count
as f64)
108 eprintln
!("rrdb update failed - unknown CF");
111 self.data
[index
] = new_value
;
112 self.last_count
= new_count
;
114 self.last_update
= time
;
117 fn update(&mut self, time
: f64, mut value
: f64) {
119 if time
<= self.last_update
{
120 eprintln
!("rrdb update failed - time in past ({} < {})", time
, self.last_update
);
124 eprintln
!("rrdb update failed - new value is NAN");
128 // derive counter value
129 if self.flags
.intersects(RRAFlags
::DST_DERIVE
| RRAFlags
::DST_COUNTER
) {
130 let time_diff
= time
- self.last_update
;
131 let diff
= if self.counter_value
.is_nan() {
134 if self.flags
.contains(RRAFlags
::DST_COUNTER
) { // check for overflow
136 eprintln
!("rrdb update failed - got negative value for counter");
139 // Note: We do not try automatic overflow corrections
140 if value
< self.counter_value
{ // overflow or counter reset
141 self.counter_value
= value
;
142 eprintln
!("rrdb update failed - conter overflow/reset detected");
145 value
- self.counter_value
148 value
- self.counter_value
151 self.counter_value
= value
;
152 value
= diff
/time_diff
;
155 self.delete_old(time
);
156 self.compute_new_value(time
, value
);
161 // Note: Avoid alignment problems by using 8byte types only
178 pub fn new(dst
: DST
) -> Self {
179 let flags
= match dst
{
180 DST
::Gauge
=> RRAFlags
::DST_GAUGE
,
181 DST
::Derive
=> RRAFlags
::DST_DERIVE
,
185 magic
: PROXMOX_RRD_MAGIC_1_0
,
187 flags
| RRAFlags
::CF_AVERAGE
,
188 RRDTimeFrameResolution
::Hour
as u64,
191 flags
| RRAFlags
::CF_MAX
,
192 RRDTimeFrameResolution
::Hour
as u64,
195 flags
| RRAFlags
::CF_AVERAGE
,
196 RRDTimeFrameResolution
::Day
as u64,
199 flags
| RRAFlags
::CF_MAX
,
200 RRDTimeFrameResolution
::Day
as u64,
203 flags
| RRAFlags
::CF_AVERAGE
,
204 RRDTimeFrameResolution
::Week
as u64,
207 flags
| RRAFlags
::CF_MAX
,
208 RRDTimeFrameResolution
::Week
as u64,
211 flags
| RRAFlags
::CF_AVERAGE
,
212 RRDTimeFrameResolution
::Month
as u64,
215 flags
| RRAFlags
::CF_MAX
,
216 RRDTimeFrameResolution
::Month
as u64,
219 flags
| RRAFlags
::CF_AVERAGE
,
220 RRDTimeFrameResolution
::Year
as u64,
223 flags
| RRAFlags
::CF_MAX
,
224 RRDTimeFrameResolution
::Year
as u64,
232 timeframe
: RRDTimeFrameResolution
,
234 ) -> (u64, u64, Vec
<Option
<f64>>) {
235 let epoch
= time
as u64;
236 let reso
= timeframe
as u64;
238 let end
= reso
*(epoch
/reso
+ 1);
239 let start
= end
- reso
*(RRD_DATA_ENTRIES
as u64);
241 let mut list
= Vec
::new();
243 let raa
= match (mode
, timeframe
) {
244 (RRDMode
::Average
, RRDTimeFrameResolution
::Hour
) => &self.hour_avg
,
245 (RRDMode
::Max
, RRDTimeFrameResolution
::Hour
) => &self.hour_max
,
246 (RRDMode
::Average
, RRDTimeFrameResolution
::Day
) => &self.day_avg
,
247 (RRDMode
::Max
, RRDTimeFrameResolution
::Day
) => &self.day_max
,
248 (RRDMode
::Average
, RRDTimeFrameResolution
::Week
) => &self.week_avg
,
249 (RRDMode
::Max
, RRDTimeFrameResolution
::Week
) => &self.week_max
,
250 (RRDMode
::Average
, RRDTimeFrameResolution
::Month
) => &self.month_avg
,
251 (RRDMode
::Max
, RRDTimeFrameResolution
::Month
) => &self.month_max
,
252 (RRDMode
::Average
, RRDTimeFrameResolution
::Year
) => &self.year_avg
,
253 (RRDMode
::Max
, RRDTimeFrameResolution
::Year
) => &self.year_max
,
256 let rrd_end
= reso
*((raa
.last_update
as u64)/reso
);
257 let rrd_start
= rrd_end
- reso
*(RRD_DATA_ENTRIES
as u64);
260 let mut index
= ((t
/reso
) % (RRD_DATA_ENTRIES
as u64)) as usize;
261 for _
in 0..RRD_DATA_ENTRIES
{
262 if t
< rrd_start
|| t
> rrd_end
{
265 let value
= raa
.data
[index
];
269 list
.push(Some(value
));
272 t
+= reso
; index
= (index
+ 1) % RRD_DATA_ENTRIES
;
275 (start
, reso
, list
.into())
278 pub fn from_raw(mut raw
: &[u8]) -> Result
<Self, std
::io
::Error
> {
279 let expected_len
= std
::mem
::size_of
::<RRD
>();
280 if raw
.len() != expected_len
{
281 let msg
= format
!("wrong data size ({} != {})", raw
.len(), expected_len
);
282 return Err(std
::io
::Error
::new(std
::io
::ErrorKind
::Other
, msg
));
285 let mut rrd
: RRD
= unsafe { std::mem::zeroed() }
;
287 let rrd_slice
= std
::slice
::from_raw_parts_mut(&mut rrd
as *mut _
as *mut u8, expected_len
);
288 raw
.read_exact(rrd_slice
)?
;
291 if rrd
.magic
!= PROXMOX_RRD_MAGIC_1_0
{
292 let msg
= format
!("wrong magic number");
293 return Err(std
::io
::Error
::new(std
::io
::ErrorKind
::Other
, msg
));
299 pub fn load(path
: &Path
) -> Result
<Self, std
::io
::Error
> {
300 let raw
= std
::fs
::read(path
)?
;
304 pub fn save(&self, filename
: &Path
) -> Result
<(), Error
> {
305 use proxmox
::tools
::{fs::replace_file, fs::CreateOptions}
;
307 let rrd_slice
= unsafe {
308 std
::slice
::from_raw_parts(self as *const _
as *const u8, std
::mem
::size_of
::<RRD
>())
311 let backup_user
= crate::backup
::backup_user()?
;
312 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0644);
313 // set the correct owner/group/permissions while saving file
314 // owner(rw) = backup, group(r)= backup
315 let options
= CreateOptions
::new()
317 .owner(backup_user
.uid
)
318 .group(backup_user
.gid
);
320 replace_file(filename
, rrd_slice
, options
)?
;
326 pub fn update(&mut self, time
: f64, value
: f64) {
327 self.hour_avg
.update(time
, value
);
328 self.hour_max
.update(time
, value
);
330 self.day_avg
.update(time
, value
);
331 self.day_max
.update(time
, value
);
333 self.week_avg
.update(time
, value
);
334 self.week_max
.update(time
, value
);
336 self.month_avg
.update(time
, value
);
337 self.month_max
.update(time
, value
);
339 self.year_avg
.update(time
, value
);
340 self.year_max
.update(time
, value
);