Returning the GC status was dropped by mistake in commit762f7d15
("datastore status: factor out api type DataStoreStatusListItem") As this is considered a breaking change which we also felt, due to the gc-status being used in the web interface for the datastore overview list (not the dashboard), re add it. Fixes:762f7d15
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> [ T: add reference to breaking commit, reword message ] Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
145 lines
4.6 KiB
Rust
145 lines
4.6 KiB
Rust
//! Datastote status
|
|
|
|
use anyhow::Error;
|
|
use serde_json::Value;
|
|
|
|
use proxmox_router::list_subdirs_api_method;
|
|
use proxmox_router::{ApiMethod, Permission, Router, RpcEnvironment, SubdirMap};
|
|
use proxmox_schema::api;
|
|
|
|
use pbs_api_types::{
|
|
Authid, DataStoreStatusListItem, Operation, RRDMode, RRDTimeFrame, PRIV_DATASTORE_AUDIT,
|
|
PRIV_DATASTORE_BACKUP,
|
|
};
|
|
|
|
use pbs_config::CachedUserInfo;
|
|
use pbs_datastore::DataStore;
|
|
|
|
use crate::rrd_cache::extract_rrd_data;
|
|
use crate::tools::statistics::linear_regression;
|
|
|
|
#[api(
|
|
returns: {
|
|
description: "Lists the Status of the Datastores.",
|
|
type: Array,
|
|
items: {
|
|
type: DataStoreStatusListItem,
|
|
},
|
|
},
|
|
access: {
|
|
permission: &Permission::Anybody,
|
|
},
|
|
)]
|
|
/// List Datastore usages and estimates
|
|
pub fn datastore_status(
|
|
_param: Value,
|
|
_info: &ApiMethod,
|
|
rpcenv: &mut dyn RpcEnvironment,
|
|
) -> Result<Vec<DataStoreStatusListItem>, Error> {
|
|
let (config, _digest) = pbs_config::datastore::config()?;
|
|
|
|
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
|
let user_info = CachedUserInfo::new()?;
|
|
|
|
let mut list = Vec::new();
|
|
|
|
for (store, (_, _)) in &config.sections {
|
|
let user_privs = user_info.lookup_privs(&auth_id, &["datastore", store]);
|
|
let allowed = (user_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP)) != 0;
|
|
if !allowed {
|
|
continue;
|
|
}
|
|
|
|
let datastore = match DataStore::lookup_datastore(&store, Some(Operation::Read)) {
|
|
Ok(datastore) => datastore,
|
|
Err(err) => {
|
|
list.push(DataStoreStatusListItem {
|
|
store: store.clone(),
|
|
total: -1,
|
|
used: -1,
|
|
avail: -1,
|
|
history: None,
|
|
history_start: None,
|
|
history_delta: None,
|
|
estimated_full_date: None,
|
|
error: Some(err.to_string()),
|
|
gc_status: None,
|
|
});
|
|
continue;
|
|
}
|
|
};
|
|
let status = crate::tools::disks::disk_usage(&datastore.base_path())?;
|
|
|
|
let mut entry = DataStoreStatusListItem {
|
|
store: store.clone(),
|
|
total: status.total as i64,
|
|
used: status.used as i64,
|
|
avail: status.avail as i64,
|
|
history: None,
|
|
history_start: None,
|
|
history_delta: None,
|
|
estimated_full_date: None,
|
|
error: None,
|
|
gc_status: Some(datastore.last_gc_status()),
|
|
};
|
|
|
|
let rrd_dir = format!("datastore/{}", store);
|
|
|
|
let get_rrd =
|
|
|what: &str| extract_rrd_data(&rrd_dir, what, RRDTimeFrame::Month, RRDMode::Average);
|
|
|
|
let total_res = get_rrd("total")?;
|
|
let used_res = get_rrd("used")?;
|
|
|
|
if let (Some((start, reso, total_list)), Some((_, _, used_list))) = (total_res, used_res) {
|
|
let mut usage_list: Vec<f64> = Vec::new();
|
|
let mut time_list: Vec<u64> = Vec::new();
|
|
let mut history = Vec::new();
|
|
|
|
for (idx, used) in used_list.iter().enumerate() {
|
|
let total = if idx < total_list.len() {
|
|
total_list[idx]
|
|
} else {
|
|
None
|
|
};
|
|
|
|
match (total, used) {
|
|
(Some(total), Some(used)) if total != 0.0 => {
|
|
time_list.push(start + (idx as u64) * reso);
|
|
let usage = used / total;
|
|
usage_list.push(usage);
|
|
history.push(Some(usage));
|
|
}
|
|
_ => history.push(None),
|
|
}
|
|
}
|
|
|
|
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 => Some(((1.0 - a) / b).floor() as i64),
|
|
Some((_, b)) if b == 0.0 => Some(0), // infinite estimate, set to past for gui to detect
|
|
_ => None,
|
|
};
|
|
}
|
|
}
|
|
|
|
list.push(entry);
|
|
}
|
|
|
|
Ok(list.into())
|
|
}
|
|
|
|
const SUBDIRS: SubdirMap = &[(
|
|
"datastore-usage",
|
|
&Router::new().get(&API_METHOD_DATASTORE_STATUS),
|
|
)];
|
|
|
|
pub const ROUTER: Router = Router::new()
|
|
.get(&list_subdirs_api_method!(SUBDIRS))
|
|
.subdirs(SUBDIRS);
|