datastore status: factor out api type DataStoreStatusListItem

And use the rust type instead of json::Value.
This commit is contained in:
Dietmar Maurer 2022-03-20 09:36:03 +01:00
parent 80ab05e40c
commit 762f7d15dc
2 changed files with 77 additions and 67 deletions

View File

@ -633,6 +633,48 @@ pub struct DataStoreStatus {
pub counts: Option<Counts>, pub counts: Option<Counts>,
} }
#[api(
properties: {
store: {
schema: DATASTORE_SCHEMA,
},
history: {
type: Array,
optional: true,
items: {
type: Number,
description: "The usage of a time in the past. Either null or between 0.0 and 1.0.",
}
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of a Datastore
pub struct DataStoreStatusListItem {
pub store: String,
/// The Size of the underlying storage in bytes.
pub total: u64,
/// The used bytes of the underlying storage.
pub used: u64,
/// The available bytes of the underlying storage.
pub avail: u64,
/// A list of usages of the past (last Month).
pub history: Option<Vec<Option<f64>>>,
/// History start time (epoch)
pub history_start: Option<u64>,
/// History resolution (seconds)
pub history_delta: Option<u64>,
/// Estimation of the UNIX epoch when the storage will be full.
/// This is calculated via a simple Linear Regression (Least
/// Squares) of RRD data of the last Month. Missing if there are
/// not enough data points yet. If the estimate lies in the past,
/// the usage is decreasing.
pub estimated_full_date: Option<i64>,
/// An error description, for example, when the datastore could not be looked up
pub error: Option<String>,
}
pub const ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE: ReturnType = ReturnType { pub const ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE: ReturnType = ReturnType {
optional: false, optional: false,
schema: &ArraySchema::new( schema: &ArraySchema::new(

View File

@ -1,7 +1,7 @@
//! Datastote status //! Datastote status
use anyhow::{Error}; use anyhow::Error;
use serde_json::{json, Value}; use serde_json::Value;
use proxmox_schema::api; use proxmox_schema::api;
use proxmox_router::{ use proxmox_router::{
@ -14,7 +14,7 @@ use proxmox_router::{
use proxmox_router::list_subdirs_api_method; use proxmox_router::list_subdirs_api_method;
use pbs_api_types::{ use pbs_api_types::{
Authid, DATASTORE_SCHEMA, RRDMode, RRDTimeFrame, Authid, DataStoreStatusListItem, RRDMode, RRDTimeFrame,
PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
}; };
@ -29,47 +29,7 @@ use crate::rrd_cache::extract_rrd_data;
description: "Lists the Status of the Datastores.", description: "Lists the Status of the Datastores.",
type: Array, type: Array,
items: { items: {
description: "Status of a Datastore", type: DataStoreStatusListItem,
type: Object,
properties: {
store: {
schema: DATASTORE_SCHEMA,
},
total: {
type: Integer,
description: "The Size of the underlying storage in bytes",
},
used: {
type: Integer,
description: "The used bytes of the underlying storage",
},
avail: {
type: Integer,
description: "The available bytes of the underlying storage",
},
history: {
type: Array,
optional: true,
description: "A list of usages of the past (last Month).",
items: {
type: Number,
description: "The usage of a time in the past. Either null or between 0.0 and 1.0.",
}
},
"estimated-full-date": {
type: Integer,
optional: true,
description: "Estimation of the UNIX epoch when the storage will be full.\
This is calculated via a simple Linear Regression (Least Squares)\
of RRD data of the last Month. Missing if there are not enough data points yet.\
If the estimate lies in the past, the usage is decreasing.",
},
"error": {
type: String,
optional: true,
description: "An error description, for example, when the datastore could not be looked up.",
},
},
}, },
}, },
access: { access: {
@ -81,7 +41,7 @@ pub fn datastore_status(
_param: Value, _param: Value,
_info: &ApiMethod, _info: &ApiMethod,
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<Value, Error> { ) -> Result<Vec<DataStoreStatusListItem>, Error> {
let (config, _digest) = pbs_config::datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
@ -100,25 +60,33 @@ pub fn datastore_status(
let datastore = match DataStore::lookup_datastore(store) { let datastore = match DataStore::lookup_datastore(store) {
Ok(datastore) => datastore, Ok(datastore) => datastore,
Err(err) => { Err(err) => {
list.push(json!({ list.push(DataStoreStatusListItem {
"store": store, store: store.clone(),
"total": -1, total: 0,
"used": -1, used: 0,
"avail": -1, avail: 0,
"error": err.to_string() history: None,
})); history_start: None,
history_delta: None,
estimated_full_date: None,
error: Some(err.to_string()),
});
continue; continue;
} }
}; };
let status = crate::tools::disks::disk_usage(&datastore.base_path())?; let status = crate::tools::disks::disk_usage(&datastore.base_path())?;
let mut entry = json!({ let mut entry = DataStoreStatusListItem {
"store": store, store: store.clone(),
"total": status.total, total: status.total,
"used": status.used, used: status.used,
"avail": status.avail, avail: status.avail,
"gc-status": datastore.last_gc_status(), history: None,
}); history_start: None,
history_delta: None,
estimated_full_date: None,
error: None,
};
let rrd_dir = format!("datastore/{}", store); let rrd_dir = format!("datastore/{}", store);
@ -149,23 +117,23 @@ pub fn datastore_status(
time_list.push(start + (idx as u64)*reso); time_list.push(start + (idx as u64)*reso);
let usage = used/total; let usage = used/total;
usage_list.push(usage); usage_list.push(usage);
history.push(json!(usage)); history.push(Some(usage));
}, },
_ => { _ => {
history.push(json!(null)) history.push(None)
} }
} }
} }
entry["history-start"] = start.into(); entry.history_start = Some(start);
entry["history-delta"] = reso.into(); entry.history_delta = Some(reso);
entry["history"] = history.into(); entry.history = Some(history);
// we skip the calculation for datastores with not enough data // we skip the calculation for datastores with not enough data
if usage_list.len() >= 7 { if usage_list.len() >= 7 {
entry["estimated-full-date"] = match linear_regression(&time_list, &usage_list) { entry.estimated_full_date = match linear_regression(&time_list, &usage_list) {
Some((a, b)) if b != 0.0 => Value::from(((1.0 - a) / b).floor() as u64), Some((a, b)) if b != 0.0 => Some(((1.0 - a) / b).floor() as i64),
_ => Value::from(0), _ => None,
}; };
} }
} }