]> git.proxmox.com Git - proxmox.git/blame - proxmox-rrd/examples/prrd.rs
rrd: fixup examples with the renamed types
[proxmox.git] / proxmox-rrd / examples / prrd.rs
CommitLineData
4cd28918
DM
1//! RRD toolkit - create/manage/update proxmox RRD (v2) file
2
3use std::path::PathBuf;
4
5use anyhow::{bail, Error};
d5b9d1f4 6use serde::{Deserialize, Serialize};
392d646f 7use serde_json::json;
4cd28918 8
d5b9d1f4
TL
9use proxmox_router::cli::{
10 complete_file_name, run_cli_command, CliCommand, CliCommandMap, CliEnvironment,
11};
4cd28918 12use proxmox_router::RpcEnvironment;
cb32acc7 13use proxmox_schema::{api, ApiStringFormat, ApiType, IntegerSchema, Schema, StringSchema};
4cd28918 14
a092ef9c 15use proxmox_sys::fs::CreateOptions;
4cd28918 16
2f942833 17use proxmox_rrd::rrd::{AggregationFn, Archive, DataSourceType, Database};
4cd28918 18
d5b9d1f4 19pub const RRA_INDEX_SCHEMA: Schema = IntegerSchema::new("Index of the RRA.").minimum(0).schema();
392d646f 20
d5b9d1f4 21pub const RRA_CONFIG_STRING_SCHEMA: Schema = StringSchema::new("RRA configuration")
4cd28918
DM
22 .format(&ApiStringFormat::PropertyString(&RRAConfig::API_SCHEMA))
23 .schema();
24
25#[api(
26 properties: {},
27 default_key: "cf",
28)]
29#[derive(Debug, Serialize, Deserialize)]
30/// RRA configuration
31pub struct RRAConfig {
32 /// Time resolution
33 pub r: u64,
2f942833 34 pub cf: AggregationFn,
4cd28918
DM
35 /// Number of data points
36 pub n: u64,
37}
38
39#[api(
40 input: {
41 properties: {
42 path: {
43 description: "The filename."
44 },
45 },
46 },
47)]
392d646f
DM
48/// Dump the RRD file in JSON format
49pub fn dump_rrd(path: String) -> Result<(), Error> {
2f942833 50 let rrd = Database::load(&PathBuf::from(path), false)?;
4cd28918 51 serde_json::to_writer_pretty(std::io::stdout(), &rrd)?;
d80d195c 52 println!();
392d646f
DM
53 Ok(())
54}
55
56#[api(
57 input: {
58 properties: {
59 path: {
60 description: "The filename."
61 },
62 },
63 },
64)]
65/// RRD file information
66pub fn rrd_info(path: String) -> Result<(), Error> {
2f942833 67 let rrd = Database::load(&PathBuf::from(path), false)?;
392d646f
DM
68
69 println!("DST: {:?}", rrd.source.dst);
70
71 for (i, rra) in rrd.rra_list.iter().enumerate() {
72 // use RRAConfig property string format
d5b9d1f4
TL
73 println!(
74 "RRA[{}]: {:?},r={},n={}",
75 i,
76 rra.cf,
77 rra.resolution,
78 rra.data.len()
79 );
392d646f
DM
80 }
81
4cd28918
DM
82 Ok(())
83}
84
85#[api(
86 input: {
87 properties: {
88 path: {
89 description: "The filename."
90 },
91 time: {
92 description: "Update time.",
93 optional: true,
94 },
95 value: {
96 description: "Update value.",
97 },
98 },
99 },
100)]
392d646f 101/// Update the RRD database
d5b9d1f4 102pub fn update_rrd(path: String, time: Option<u64>, value: f64) -> Result<(), Error> {
4cd28918
DM
103 let path = PathBuf::from(path);
104
d5b9d1f4
TL
105 let time = time
106 .map(|v| v as f64)
4cd28918
DM
107 .unwrap_or_else(proxmox_time::epoch_f64);
108
2f942833 109 let mut rrd = Database::load(&path, false)?;
4cd28918
DM
110 rrd.update(time, value);
111
a7ee3455 112 rrd.save(&path, CreateOptions::new(), false)?;
4cd28918
DM
113
114 Ok(())
115}
116
117#[api(
118 input: {
119 properties: {
120 path: {
121 description: "The filename."
122 },
123 cf: {
724c3dda 124 type: AggregationFn,
4cd28918
DM
125 },
126 resolution: {
109902fb 127 description: "Time resolution",
4cd28918
DM
128 },
129 start: {
109902fb 130 description: "Start time. If not specified, we simply extract 10 data points.",
4cd28918
DM
131 optional: true,
132 },
133 end: {
134 description: "End time (Unix Epoch). Default is the last update time.",
135 optional: true,
136 },
137 },
138 },
139)]
392d646f
DM
140/// Fetch data from the RRD file
141pub fn fetch_rrd(
4cd28918 142 path: String,
2f942833 143 cf: AggregationFn,
4cd28918
DM
144 resolution: u64,
145 start: Option<u64>,
146 end: Option<u64>,
147) -> Result<(), Error> {
2f942833 148 let rrd = Database::load(&PathBuf::from(path), false)?;
4cd28918
DM
149
150 let data = rrd.extract_data(cf, resolution, start, end)?;
151
152 println!("{}", serde_json::to_string_pretty(&data)?);
153
154 Ok(())
155}
156
392d646f
DM
157#[api(
158 input: {
159 properties: {
160 path: {
161 description: "The filename."
162 },
163 "rra-index": {
164 schema: RRA_INDEX_SCHEMA,
165 },
166 },
167 },
168)]
169/// Return the Unix timestamp of the first time slot inside the
170/// specified RRA (slot start time)
d5b9d1f4 171pub fn first_update_time(path: String, rra_index: usize) -> Result<(), Error> {
2f942833 172 let rrd = Database::load(&PathBuf::from(path), false)?;
392d646f
DM
173
174 if rra_index >= rrd.rra_list.len() {
175 bail!("rra-index is out of range");
176 }
177 let rra = &rrd.rra_list[rra_index];
d5b9d1f4 178 let duration = (rra.data.len() as u64) * rra.resolution;
392d646f
DM
179 let first = rra.slot_start_time((rrd.source.last_update as u64).saturating_sub(duration));
180
181 println!("{}", first);
182 Ok(())
183}
184
185#[api(
186 input: {
187 properties: {
188 path: {
189 description: "The filename."
190 },
191 },
192 },
193)]
194/// Return the Unix timestamp of the last update
195pub fn last_update_time(path: String) -> Result<(), Error> {
2f942833 196 let rrd = Database::load(&PathBuf::from(path), false)?;
392d646f
DM
197
198 println!("{}", rrd.source.last_update);
199 Ok(())
200}
201
202#[api(
203 input: {
204 properties: {
205 path: {
206 description: "The filename."
207 },
208 },
209 },
210)]
211/// Return the time and value from the last update
212pub fn last_update(path: String) -> Result<(), Error> {
2f942833 213 let rrd = Database::load(&PathBuf::from(path), false)?;
392d646f
DM
214
215 let result = json!({
216 "time": rrd.source.last_update,
217 "value": rrd.source.last_value,
218 });
219
220 println!("{}", serde_json::to_string_pretty(&result)?);
221
222 Ok(())
223}
224
4cd28918
DM
225#[api(
226 input: {
227 properties: {
228 dst: {
724c3dda 229 type: DataSourceType,
4cd28918
DM
230 },
231 path: {
232 description: "The filename to create."
233 },
234 rra: {
235 description: "Configuration of contained RRAs.",
236 type: Array,
237 items: {
238 schema: RRA_CONFIG_STRING_SCHEMA,
239 }
240 },
241 },
242 },
243)]
392d646f 244/// Create a new RRD file
2f942833 245pub fn create_rrd(dst: DataSourceType, path: String, rra: Vec<String>) -> Result<(), Error> {
4cd28918
DM
246 let mut rra_list = Vec::new();
247
248 for item in rra.iter() {
d5b9d1f4
TL
249 let rra: RRAConfig =
250 serde_json::from_value(RRAConfig::API_SCHEMA.parse_property_string(item)?)?;
4cd28918 251 println!("GOT {:?}", rra);
2f942833 252 rra_list.push(Archive::new(rra.cf, rra.r, rra.n as usize));
4cd28918
DM
253 }
254
255 let path = PathBuf::from(path);
256
2f942833 257 let rrd = Database::new(dst, rra_list);
4cd28918 258
a7ee3455 259 rrd.save(&path, CreateOptions::new(), false)?;
4cd28918
DM
260
261 Ok(())
262}
263
392d646f
DM
264#[api(
265 input: {
266 properties: {
267 path: {
268 description: "The filename."
269 },
270 "rra-index": {
271 schema: RRA_INDEX_SCHEMA,
272 },
273 slots: {
274 description: "The number of slots you want to add or remove.",
275 type: i64,
276 },
277 },
278 },
279)]
280/// Resize. Change the number of data slots for the specified RRA.
d5b9d1f4 281pub fn resize_rrd(path: String, rra_index: usize, slots: i64) -> Result<(), Error> {
392d646f
DM
282 let path = PathBuf::from(&path);
283
2f942833 284 let mut rrd = Database::load(&path, false)?;
392d646f
DM
285
286 if rra_index >= rrd.rra_list.len() {
287 bail!("rra-index is out of range");
288 }
289
290 let rra = &rrd.rra_list[rra_index];
291
292 let new_slots = (rra.data.len() as i64) + slots;
293
294 if new_slots < 1 {
109902fb 295 bail!("number of new slots is too small ('{}' < 1)", new_slots);
392d646f
DM
296 }
297
d5b9d1f4 298 if new_slots > 1024 * 1024 {
109902fb 299 bail!("number of new slots is too big ('{}' > 1M)", new_slots);
392d646f
DM
300 }
301
302 let rra_end = rra.slot_end_time(rrd.source.last_update as u64);
d5b9d1f4 303 let rra_start = rra_end - rra.resolution * (rra.data.len() as u64);
56b5c289
WB
304 let (start, reso, data) = rra
305 .extract_data(rra_start, rra_end, rrd.source.last_update)
306 .into();
392d646f 307
2f942833 308 let mut new_rra = Archive::new(rra.cf, rra.resolution, new_slots as usize);
392d646f
DM
309 new_rra.last_count = rra.last_count;
310
311 new_rra.insert_data(start, reso, data)?;
312
313 rrd.rra_list[rra_index] = new_rra;
314
a7ee3455 315 rrd.save(&path, CreateOptions::new(), false)?;
392d646f
DM
316
317 Ok(())
318}
4cd28918
DM
319
320fn main() -> Result<(), Error> {
4cd28918
DM
321 let uid = nix::unistd::Uid::current();
322
323 let username = match nix::unistd::User::from_uid(uid)? {
324 Some(user) => user.name,
325 None => bail!("unable to get user name"),
326 };
327
328 let cmd_def = CliCommandMap::new()
329 .insert(
330 "create",
392d646f 331 CliCommand::new(&API_METHOD_CREATE_RRD)
4cd28918 332 .arg_param(&["path"])
d5b9d1f4 333 .completion_cb("path", complete_file_name),
4cd28918
DM
334 )
335 .insert(
392d646f
DM
336 "dump",
337 CliCommand::new(&API_METHOD_DUMP_RRD)
4cd28918 338 .arg_param(&["path"])
d5b9d1f4
TL
339 .completion_cb("path", complete_file_name),
340 )
4cd28918
DM
341 .insert(
342 "fetch",
392d646f 343 CliCommand::new(&API_METHOD_FETCH_RRD)
4cd28918 344 .arg_param(&["path"])
d5b9d1f4
TL
345 .completion_cb("path", complete_file_name),
346 )
392d646f
DM
347 .insert(
348 "first",
349 CliCommand::new(&API_METHOD_FIRST_UPDATE_TIME)
350 .arg_param(&["path"])
d5b9d1f4 351 .completion_cb("path", complete_file_name),
4cd28918
DM
352 )
353 .insert(
392d646f
DM
354 "info",
355 CliCommand::new(&API_METHOD_RRD_INFO)
356 .arg_param(&["path"])
d5b9d1f4 357 .completion_cb("path", complete_file_name),
392d646f
DM
358 )
359 .insert(
360 "last",
361 CliCommand::new(&API_METHOD_LAST_UPDATE_TIME)
362 .arg_param(&["path"])
d5b9d1f4 363 .completion_cb("path", complete_file_name),
392d646f
DM
364 )
365 .insert(
366 "lastupdate",
367 CliCommand::new(&API_METHOD_LAST_UPDATE)
368 .arg_param(&["path"])
d5b9d1f4 369 .completion_cb("path", complete_file_name),
392d646f
DM
370 )
371 .insert(
372 "resize",
373 CliCommand::new(&API_METHOD_RESIZE_RRD)
374 .arg_param(&["path"])
d5b9d1f4 375 .completion_cb("path", complete_file_name),
392d646f
DM
376 )
377 .insert(
378 "update",
379 CliCommand::new(&API_METHOD_UPDATE_RRD)
4cd28918 380 .arg_param(&["path"])
d5b9d1f4
TL
381 .completion_cb("path", complete_file_name),
382 );
4cd28918
DM
383
384 let mut rpcenv = CliEnvironment::new();
385 rpcenv.set_auth_id(Some(format!("{}@pam", username)));
386
387 run_cli_command(cmd_def, rpcenv, None);
388
389 Ok(())
4cd28918 390}