Ok(snapshots)
}
-// returns a map from type to (group_count, snapshot_count)
-fn get_snaphots_count(store: &DataStore) -> Result<HashMap<String, (usize, usize)>, Error> {
+fn get_snapshots_count(store: &DataStore) -> Result<Counts, Error> {
let base_path = store.base_path();
let backup_list = BackupInfo::list_backups(&base_path)?;
let mut groups = HashSet::new();
- let mut result: HashMap<String, (usize, usize)> = HashMap::new();
+
+ let mut result = Counts {
+ ct: None,
+ host: None,
+ vm: None,
+ other: None,
+ };
+
for info in backup_list {
let group = info.backup_dir.group();
new_id = true;
}
- if let Some(mut counts) = result.get_mut(backup_type) {
- counts.1 += 1;
- if new_id {
- counts.0 +=1;
- }
- } else {
- result.insert(backup_type.to_string(), (1, 1));
+ let mut counts = match backup_type {
+ "ct" => result.ct.take().unwrap_or(Default::default()),
+ "host" => result.host.take().unwrap_or(Default::default()),
+ "vm" => result.vm.take().unwrap_or(Default::default()),
+ _ => result.other.take().unwrap_or(Default::default()),
+ };
+
+ counts.snapshots += 1;
+ if new_id {
+ counts.groups +=1;
+ }
+
+ match backup_type {
+ "ct" => result.ct = Some(counts),
+ "host" => result.host = Some(counts),
+ "vm" => result.vm = Some(counts),
+ _ => result.other = Some(counts),
}
}
},
},
returns: {
- description: "The overall Datastore status and information.",
- type: Object,
- properties: {
- storage: {
- type: StorageStatus,
- },
- counts: {
- description: "Group and Snapshot counts per Type",
- type: Object,
- properties: { },
- },
- "gc-status": {
- type: GarbageCollectionStatus,
- },
- },
+ type: DataStoreStatus,
},
access: {
permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP, true),
store: String,
_info: &ApiMethod,
_rpcenv: &mut dyn RpcEnvironment,
-) -> Result<Value, Error> {
+) -> Result<DataStoreStatus, Error> {
let datastore = DataStore::lookup_datastore(&store)?;
- let storage_status = crate::tools::disks::disk_usage(&datastore.base_path())?;
- let counts = get_snaphots_count(&datastore)?;
+ let storage = crate::tools::disks::disk_usage(&datastore.base_path())?;
+ let counts = get_snapshots_count(&datastore)?;
let gc_status = datastore.last_gc_status();
- let res = json!({
- "storage": storage_status,
- "counts": counts,
- "gc-status": gc_status,
- });
-
- Ok(res)
+ Ok(DataStoreStatus {
+ total: storage.total,
+ used: storage.used,
+ avail: storage.avail,
+ gc_status,
+ counts,
+ })
}
#[api(
pub avail: u64,
}
+#[api()]
+#[derive(Serialize, Deserialize, Default)]
+/// Backup Type group/snapshot counts.
+pub struct TypeCounts {
+ /// The number of groups of the type.
+ pub groups: u64,
+ /// The number of snapshots of the type.
+ pub snapshots: u64,
+}
+
+#[api(
+ properties: {
+ ct: {
+ type: TypeCounts,
+ optional: true,
+ },
+ host: {
+ type: TypeCounts,
+ optional: true,
+ },
+ vm: {
+ type: TypeCounts,
+ optional: true,
+ },
+ other: {
+ type: TypeCounts,
+ optional: true,
+ },
+ },
+)]
+#[derive(Serialize, Deserialize)]
+/// Counts of groups/snapshots per BackupType.
+pub struct Counts {
+ /// The counts for CT backups
+ pub ct: Option<TypeCounts>,
+ /// The counts for Host backups
+ pub host: Option<TypeCounts>,
+ /// The counts for VM backups
+ pub vm: Option<TypeCounts>,
+ /// The counts for other backup types
+ pub other: Option<TypeCounts>,
+}
+
+#[api(
+ properties: {
+ "gc-status": { type: GarbageCollectionStatus, },
+ counts: { type: Counts, }
+ },
+)]
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all="kebab-case")]
+/// Overall Datastore status and useful information.
+pub struct DataStoreStatus {
+ /// Total space (bytes).
+ pub total: u64,
+ /// Used space (bytes).
+ pub used: u64,
+ /// Available space (bytes).
+ pub avail: u64,
+ /// Status of last GC
+ pub gc_status: GarbageCollectionStatus,
+ /// Group/Snapshot counts
+ pub counts: Counts,
+}
+
#[api(
properties: {
upid: { schema: UPID_SCHEMA },
let path = format!("api2/json/admin/datastore/{}/status", repo.store());
let mut result = client.get(&path, None).await?;
- let mut data = result["data"]["storage"].take();
+ let mut data = result["data"].take();
record_repository(&repo);
let vm = me.getViewModel();
let counts = store.getById('counts').data.value;
- let storage = store.getById('storage').data.value;
+ let total = store.getById('total').data.value;
+ let used = store.getById('used').data.value;
- let used = Proxmox.Utils.format_size(storage.used);
- let total = Proxmox.Utils.format_size(storage.total);
- let percent = 100*storage.used/storage.total;
- if (storage.total === 0) {
+ let percent = 100*used/total;
+ if (total === 0) {
percent = 0;
}
let used_percent = `${percent.toFixed(2)}%`;
let usage = used_percent + ' (' +
- Ext.String.format(gettext('{0} of {1}'),
- used, total) + ')';
+ Ext.String.format(
+ gettext('{0} of {1}'),
+ Proxmox.Utils.format_size(used),
+ Proxmox.Utils.format_size(total),
+ ) + ')';
vm.set('usagetext', usage);
- vm.set('usage', storage.used/storage.total);
+ vm.set('usage', used/total);
let gcstatus = store.getById('gc-status').data.value;
(gcstatus['disk-bytes'] || Infinity);
let countstext = function(count) {
- return `${count[0]} ${gettext('Groups')}, ${count[1]} ${gettext('Snapshots')}`;
+ return `${count.groups || 0} ${gettext('Groups')}, ${count.snapshots || 0} ${gettext('Snapshots')}`;
};
- vm.set('ctcount', countstext(counts.ct || [0, 0]));
- vm.set('vmcount', countstext(counts.vm || [0, 0]));
- vm.set('hostcount', countstext(counts.host || [0, 0]));
+ vm.set('ctcount', countstext(counts.ct));
+ vm.set('vmcount', countstext(counts.vm));
+ vm.set('hostcount', countstext(counts.host));
vm.set('deduplication', dedup.toFixed(2));
vm.set('stillbad', gcstatus['still-bad']);
vm.set('removedbytes', Proxmox.Utils.format_size(gcstatus['removed-bytes']));