diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs index d0215403..36d86c98 100644 --- a/pbs-api-types/src/datastore.rs +++ b/pbs-api-types/src/datastore.rs @@ -633,6 +633,48 @@ pub struct DataStoreStatus { pub counts: Option, } +#[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>>, + /// History start time (epoch) + pub history_start: Option, + /// History resolution (seconds) + pub history_delta: Option, + /// 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, + /// An error description, for example, when the datastore could not be looked up + pub error: Option, +} + pub const ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE: ReturnType = ReturnType { optional: false, schema: &ArraySchema::new( diff --git a/src/api2/status.rs b/src/api2/status.rs index 029529ac..2da512e1 100644 --- a/src/api2/status.rs +++ b/src/api2/status.rs @@ -1,7 +1,7 @@ //! Datastote status -use anyhow::{Error}; -use serde_json::{json, Value}; +use anyhow::Error; +use serde_json::Value; use proxmox_schema::api; use proxmox_router::{ @@ -14,7 +14,7 @@ use proxmox_router::{ use proxmox_router::list_subdirs_api_method; use pbs_api_types::{ - Authid, DATASTORE_SCHEMA, RRDMode, RRDTimeFrame, + Authid, DataStoreStatusListItem, RRDMode, RRDTimeFrame, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, }; @@ -29,47 +29,7 @@ use crate::rrd_cache::extract_rrd_data; description: "Lists the Status of the Datastores.", type: Array, items: { - description: "Status of a Datastore", - 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.", - }, - }, + type: DataStoreStatusListItem, }, }, access: { @@ -81,7 +41,7 @@ pub fn datastore_status( _param: Value, _info: &ApiMethod, rpcenv: &mut dyn RpcEnvironment, - ) -> Result { + ) -> Result, Error> { let (config, _digest) = pbs_config::datastore::config()?; @@ -100,25 +60,33 @@ pub fn datastore_status( let datastore = match DataStore::lookup_datastore(store) { Ok(datastore) => datastore, Err(err) => { - list.push(json!({ - "store": store, - "total": -1, - "used": -1, - "avail": -1, - "error": err.to_string() - })); + list.push(DataStoreStatusListItem { + store: store.clone(), + total: 0, + used: 0, + avail: 0, + history: None, + history_start: None, + history_delta: None, + estimated_full_date: None, + error: Some(err.to_string()), + }); continue; } }; let status = crate::tools::disks::disk_usage(&datastore.base_path())?; - let mut entry = json!({ - "store": store, - "total": status.total, - "used": status.used, - "avail": status.avail, - "gc-status": datastore.last_gc_status(), - }); + let mut entry = DataStoreStatusListItem { + store: store.clone(), + total: status.total, + used: status.used, + avail: status.avail, + history: None, + history_start: None, + history_delta: None, + estimated_full_date: None, + error: None, + }; let rrd_dir = format!("datastore/{}", store); @@ -149,23 +117,23 @@ pub fn datastore_status( time_list.push(start + (idx as u64)*reso); let usage = used/total; 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-delta"] = reso.into(); - entry["history"] = history.into(); + entry.history_start = Some(start); + entry.history_delta = Some(reso); + entry.history = Some(history); // we skip the calculation for datastores with not enough data if usage_list.len() >= 7 { - 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), - _ => Value::from(0), + entry.estimated_full_date = match linear_regression(&time_list, &usage_list) { + Some((a, b)) if b != 0.0 => Some(((1.0 - a) / b).floor() as i64), + _ => None, }; } }