datastore: add max-depth to recursive namespace iter

on depth == 0 we only yield the anchor ns, this simplifies usage in
the API as for, e.g. list-snapthos the depth == 0 means only the
snapshots from the passed namespace, nothing below.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2022-05-05 17:17:02 +02:00
parent 08aa5fe7aa
commit 15a9272495
2 changed files with 52 additions and 21 deletions

View File

@ -7,12 +7,20 @@ use pbs_datastore::DataStore;
fn run() -> Result<(), Error> { fn run() -> Result<(), Error> {
let base: PathBuf = match std::env::args().skip(1).next() { let base: PathBuf = match std::env::args().skip(1).next() {
Some(path) => path.into(), Some(path) => path.into(),
None => bail!("no path passed"), None => bail!("no path passed!\n\nusage: ls-snapshots <path> [<max-depth>]"),
};
let max_depth: Option<usize> = match std::env::args().skip(2).next() {
Some(depth) => match depth.parse::<usize>() {
Ok(depth) if depth < 8 => Some(depth),
Ok(_) => bail!("max-depth must be < 8"),
Err(err) => bail!("couldn't parse max-depth from {depth} - {err}"),
},
None => None,
}; };
let store = unsafe { DataStore::open_path("", &base, None)? }; let store = unsafe { DataStore::open_path("", &base, None)? };
for ns in store.recursive_iter_backup_ns_ok(Default::default())? { for ns in store.recursive_iter_backup_ns_ok(Default::default(), max_depth)? {
println!("found namespace store:/{}", ns); println!("found namespace store:/{}", ns);
for group in store.iter_backup_groups(ns)? { for group in store.iter_backup_groups(ns)? {

View File

@ -749,17 +749,21 @@ impl DataStore {
pub fn recursive_iter_backup_ns_ok( pub fn recursive_iter_backup_ns_ok(
self: &Arc<DataStore>, self: &Arc<DataStore>,
ns: BackupNamespace, ns: BackupNamespace,
max_depth: Option<usize>,
) -> Result<impl Iterator<Item = BackupNamespace> + 'static, Error> { ) -> Result<impl Iterator<Item = BackupNamespace> + 'static, Error> {
let this = Arc::clone(self); let this = Arc::clone(self);
Ok( Ok(if let Some(depth) = max_depth {
ListNamespacesRecursive::new(Arc::clone(&self), ns)?.filter_map(move |ns| match ns { ListNamespacesRecursive::new_max_depth(Arc::clone(&self), ns, depth)?
} else {
ListNamespacesRecursive::new(Arc::clone(&self), ns)?
}
.filter_map(move |ns| match ns {
Ok(ns) => Some(ns), Ok(ns) => Some(ns),
Err(err) => { Err(err) => {
log::error!("list groups error on datastore {} - {}", this.name(), err); log::error!("list groups error on datastore {} - {}", this.name(), err);
None None
} }
}), }))
)
} }
/// Get a streaming iter over top-level backup groups of a datatstore /// Get a streaming iter over top-level backup groups of a datatstore
@ -1504,15 +1508,30 @@ pub struct ListNamespacesRecursive {
store: Arc<DataStore>, store: Arc<DataStore>,
/// the starting namespace we search downward from /// the starting namespace we search downward from
ns: BackupNamespace, ns: BackupNamespace,
/// the maximal recursion depth from the anchor start ns (depth == 0) downwards
max_depth: u8,
state: Option<Vec<ListNamespaces>>, // vector to avoid code recursion state: Option<Vec<ListNamespaces>>, // vector to avoid code recursion
} }
impl ListNamespacesRecursive { impl ListNamespacesRecursive {
/// Creates an recursive namespace iterator. /// Creates an recursive namespace iterator.
pub fn new(store: Arc<DataStore>, ns: BackupNamespace) -> Result<Self, Error> { pub fn new(store: Arc<DataStore>, ns: BackupNamespace) -> Result<Self, Error> {
Self::new_max_depth(store, ns, pbs_api_types::MAX_NAMESPACE_DEPTH)
}
/// Creates an recursive namespace iterator with max_depth
pub fn new_max_depth(
store: Arc<DataStore>,
ns: BackupNamespace,
max_depth: usize,
) -> Result<Self, Error> {
if max_depth > pbs_api_types::MAX_NAMESPACE_DEPTH {
bail!("max_depth must be smaller 8");
}
Ok(ListNamespacesRecursive { Ok(ListNamespacesRecursive {
store: store, store: store,
ns, ns,
max_depth: max_depth as u8,
state: None, state: None,
}) })
} }
@ -1533,9 +1552,11 @@ impl Iterator for ListNamespacesRecursive {
}; };
match iter.next() { match iter.next() {
Some(Ok(ns)) => { Some(Ok(ns)) => {
if state.len() < self.max_depth as usize {
match ListNamespaces::new(Arc::clone(&self.store), ns.to_owned()) { match ListNamespaces::new(Arc::clone(&self.store), ns.to_owned()) {
Ok(iter) => state.push(iter), Ok(iter) => state.push(iter),
Err(err) => log::error!("failed to create child namespace iter {err}"), Err(err) => log::error!("failed to create child ns iter {err}"),
}
} }
return Some(Ok(ns)); return Some(Ok(ns));
} }
@ -1547,6 +1568,7 @@ impl Iterator for ListNamespacesRecursive {
} else { } else {
// first next call ever: initialize state vector and start iterating at our level // first next call ever: initialize state vector and start iterating at our level
let mut state = Vec::with_capacity(pbs_api_types::MAX_NAMESPACE_DEPTH); let mut state = Vec::with_capacity(pbs_api_types::MAX_NAMESPACE_DEPTH);
if self.max_depth as usize > 0 {
match ListNamespaces::new(Arc::clone(&self.store), self.ns.to_owned()) { match ListNamespaces::new(Arc::clone(&self.store), self.ns.to_owned()) {
Ok(list_ns) => state.push(list_ns), Ok(list_ns) => state.push(list_ns),
Err(err) => { Err(err) => {
@ -1556,6 +1578,7 @@ impl Iterator for ListNamespacesRecursive {
return Some(Err(err)); return Some(Err(err));
} }
} }
}
self.state = Some(state); self.state = Some(state);
return Some(Ok(self.ns.to_owned())); // return our anchor ns for convenience return Some(Ok(self.ns.to_owned())); // return our anchor ns for convenience
} }