api-types: introduce BackupType enum and Group/Dir api types

The type is a real enum.

All are API types and implement Display and FromStr. The
ordering is the same as it is in pbs-datastore.

Also, they are now flattened into a few structs instead of
being copied manually.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Wolfgang Bumiller
2022-04-14 15:05:58 +02:00
committed by Thomas Lamprecht
parent 33eb23d57e
commit 988d575dbb
23 changed files with 449 additions and 278 deletions

View File

@ -5,7 +5,8 @@ use std::str::FromStr;
use anyhow::{bail, format_err, Error};
use pbs_api_types::{
GroupFilter, BACKUP_DATE_REGEX, BACKUP_FILE_REGEX, GROUP_PATH_REGEX, SNAPSHOT_PATH_REGEX,
BackupType, GroupFilter, BACKUP_DATE_REGEX, BACKUP_FILE_REGEX, GROUP_PATH_REGEX,
SNAPSHOT_PATH_REGEX,
};
use super::manifest::MANIFEST_BLOB_NAME;
@ -14,7 +15,7 @@ use super::manifest::MANIFEST_BLOB_NAME;
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct BackupGroup {
/// Type of backup
backup_type: String,
backup_type: BackupType,
/// Unique (for this type) ID
backup_id: String,
}
@ -44,15 +45,15 @@ impl std::cmp::PartialOrd for BackupGroup {
}
impl BackupGroup {
pub fn new<T: Into<String>, U: Into<String>>(backup_type: T, backup_id: U) -> Self {
pub fn new<T: Into<String>>(backup_type: BackupType, backup_id: T) -> Self {
Self {
backup_type: backup_type.into(),
backup_type,
backup_id: backup_id.into(),
}
}
pub fn backup_type(&self) -> &str {
&self.backup_type
pub fn backup_type(&self) -> BackupType {
self.backup_type
}
pub fn backup_id(&self) -> &str {
@ -62,7 +63,7 @@ impl BackupGroup {
pub fn group_path(&self) -> PathBuf {
let mut relative_path = PathBuf::new();
relative_path.push(&self.backup_type);
relative_path.push(self.backup_type.as_str());
relative_path.push(&self.backup_id);
@ -85,7 +86,7 @@ impl BackupGroup {
}
let backup_dir =
BackupDir::with_rfc3339(&self.backup_type, &self.backup_id, backup_time)?;
BackupDir::with_rfc3339(self.backup_type, &self.backup_id, backup_time)?;
let files = list_backup_files(l2_fd, backup_time)?;
let protected = backup_dir.is_protected(base_path.to_owned());
@ -162,12 +163,24 @@ impl BackupGroup {
Ok(group) => &group == self,
Err(_) => false, // shouldn't happen if value is schema-checked
},
GroupFilter::BackupType(backup_type) => self.backup_type() == backup_type,
GroupFilter::BackupType(backup_type) => self.backup_type().as_str() == backup_type,
GroupFilter::Regex(regex) => regex.is_match(&self.to_string()),
}
}
}
impl From<&BackupGroup> for pbs_api_types::BackupGroup {
fn from(group: &BackupGroup) -> pbs_api_types::BackupGroup {
(group.backup_type, group.backup_id.clone()).into()
}
}
impl From<BackupGroup> for pbs_api_types::BackupGroup {
fn from(group: BackupGroup) -> pbs_api_types::BackupGroup {
(group.backup_type, group.backup_id).into()
}
}
impl std::fmt::Display for BackupGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let backup_type = self.backup_type();
@ -188,7 +201,7 @@ impl std::str::FromStr for BackupGroup {
.ok_or_else(|| format_err!("unable to parse backup group path '{}'", path))?;
Ok(Self {
backup_type: cap.get(1).unwrap().as_str().to_owned(),
backup_type: cap.get(1).unwrap().as_str().parse()?,
backup_id: cap.get(2).unwrap().as_str().to_owned(),
})
}
@ -208,28 +221,26 @@ pub struct BackupDir {
}
impl BackupDir {
pub fn new<T, U>(backup_type: T, backup_id: U, backup_time: i64) -> Result<Self, Error>
pub fn new<T>(backup_type: BackupType, backup_id: T, backup_time: i64) -> Result<Self, Error>
where
T: Into<String>,
U: Into<String>,
{
let group = BackupGroup::new(backup_type.into(), backup_id.into());
let group = BackupGroup::new(backup_type, backup_id.into());
BackupDir::with_group(group, backup_time)
}
pub fn with_rfc3339<T, U, V>(
backup_type: T,
backup_id: U,
backup_time_string: V,
pub fn with_rfc3339<T, U>(
backup_type: BackupType,
backup_id: T,
backup_time_string: U,
) -> Result<Self, Error>
where
T: Into<String>,
U: Into<String>,
V: Into<String>,
{
let backup_time_string = backup_time_string.into();
let backup_time = proxmox_time::parse_rfc3339(&backup_time_string)?;
let group = BackupGroup::new(backup_type.into(), backup_id.into());
let group = BackupGroup::new(backup_type, backup_id.into());
Ok(Self {
group,
backup_time,
@ -283,6 +294,22 @@ impl BackupDir {
}
}
impl From<&BackupDir> for pbs_api_types::BackupDir {
fn from(dir: &BackupDir) -> pbs_api_types::BackupDir {
(
pbs_api_types::BackupGroup::from(dir.group.clone()),
dir.backup_time,
)
.into()
}
}
impl From<BackupDir> for pbs_api_types::BackupDir {
fn from(dir: BackupDir) -> pbs_api_types::BackupDir {
(pbs_api_types::BackupGroup::from(dir.group), dir.backup_time).into()
}
}
impl std::str::FromStr for BackupDir {
type Err = Error;
@ -295,7 +322,7 @@ impl std::str::FromStr for BackupDir {
.ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
BackupDir::with_rfc3339(
cap.get(1).unwrap().as_str(),
cap.get(1).unwrap().as_str().parse()?,
cap.get(2).unwrap().as_str(),
cap.get(3).unwrap().as_str(),
)

View File

@ -18,8 +18,8 @@ use proxmox_sys::WorkerTaskContext;
use proxmox_sys::{task_log, task_warn};
use pbs_api_types::{
Authid, ChunkOrder, DataStoreConfig, DatastoreTuning, GarbageCollectionStatus, HumanByte,
Operation, BACKUP_DATE_REGEX, BACKUP_ID_REGEX, BACKUP_TYPE_REGEX, UPID,
Authid, BackupType, ChunkOrder, DataStoreConfig, DatastoreTuning, GarbageCollectionStatus,
HumanByte, Operation, BACKUP_DATE_REGEX, BACKUP_ID_REGEX, UPID,
};
use pbs_config::{open_backup_lockfile, BackupLockGuard, ConfigVersionCache};
@ -494,7 +494,7 @@ impl DataStore {
) -> Result<(Authid, DirLockGuard), Error> {
// create intermediate path first:
let mut full_path = self.base_path();
full_path.push(backup_group.backup_type());
full_path.push(backup_group.backup_type().as_str());
std::fs::create_dir_all(&full_path)?;
full_path.push(backup_group.backup_id());
@ -1113,7 +1113,7 @@ impl Iterator for ListSnapshots {
/// A iterator for a (single) level of Backup Groups
pub struct ListGroups {
type_fd: proxmox_sys::fs::ReadDir,
id_state: Option<(String, proxmox_sys::fs::ReadDir)>,
id_state: Option<(BackupType, proxmox_sys::fs::ReadDir)>,
}
impl ListGroups {
@ -1130,7 +1130,7 @@ impl Iterator for ListGroups {
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((ref group_type, ref mut id_fd)) = self.id_state {
if let Some((group_type, ref mut id_fd)) = self.id_state {
let item = match id_fd.next() {
Some(item) => item,
None => {
@ -1162,7 +1162,7 @@ impl Iterator for ListGroups {
Some(nix::dir::Type::Directory) => {} // OK
_ => continue,
}
if BACKUP_TYPE_REGEX.is_match(name) {
if let Ok(group_type) = BackupType::from_str(name) {
// found a backup group type, descend into it to scan all IDs in it
// by switching to the id-state branch
let base_fd = entry.parent_fd();
@ -1170,7 +1170,7 @@ impl Iterator for ListGroups {
Ok(dirfd) => dirfd,
Err(err) => return Some(Err(err.into())),
};
self.id_state = Some((name.to_owned(), id_dirfd));
self.id_state = Some((group_type, id_dirfd));
}
}
continue; // file did not match regex or isn't valid utf-8

View File

@ -6,7 +6,7 @@ use anyhow::{bail, format_err, Error};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use pbs_api_types::{CryptMode, Fingerprint};
use pbs_api_types::{BackupType, CryptMode, Fingerprint};
use pbs_tools::crypt_config::CryptConfig;
use crate::BackupDir;
@ -50,7 +50,7 @@ impl FileInfo {
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BackupManifest {
backup_type: String,
backup_type: BackupType,
backup_id: String,
backup_time: i64,
files: Vec<FileInfo>,
@ -87,7 +87,7 @@ pub fn archive_type<P: AsRef<Path>>(archive_name: P) -> Result<ArchiveType, Erro
impl BackupManifest {
pub fn new(snapshot: BackupDir) -> Self {
Self {
backup_type: snapshot.group().backup_type().into(),
backup_type: snapshot.group().backup_type(),
backup_id: snapshot.group().backup_id().into(),
backup_time: snapshot.backup_time(),
files: Vec::new(),