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:
parent
08aa5fe7aa
commit
15a9272495
|
@ -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)? {
|
||||||
|
|
|
@ -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)?
|
||||||
Ok(ns) => Some(ns),
|
} else {
|
||||||
Err(err) => {
|
ListNamespacesRecursive::new(Arc::clone(&self), ns)?
|
||||||
log::error!("list groups error on datastore {} - {}", this.name(), err);
|
}
|
||||||
None
|
.filter_map(move |ns| match ns {
|
||||||
}
|
Ok(ns) => Some(ns),
|
||||||
}),
|
Err(err) => {
|
||||||
)
|
log::error!("list groups error on datastore {} - {}", this.name(), err);
|
||||||
|
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)) => {
|
||||||
match ListNamespaces::new(Arc::clone(&self.store), ns.to_owned()) {
|
if state.len() < self.max_depth as usize {
|
||||||
Ok(iter) => state.push(iter),
|
match ListNamespaces::new(Arc::clone(&self.store), ns.to_owned()) {
|
||||||
Err(err) => log::error!("failed to create child namespace iter {err}"),
|
Ok(iter) => state.push(iter),
|
||||||
|
Err(err) => log::error!("failed to create child ns iter {err}"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Some(Ok(ns));
|
return Some(Ok(ns));
|
||||||
}
|
}
|
||||||
|
@ -1547,13 +1568,15 @@ 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);
|
||||||
match ListNamespaces::new(Arc::clone(&self.store), self.ns.to_owned()) {
|
if self.max_depth as usize > 0 {
|
||||||
Ok(list_ns) => state.push(list_ns),
|
match ListNamespaces::new(Arc::clone(&self.store), self.ns.to_owned()) {
|
||||||
Err(err) => {
|
Ok(list_ns) => state.push(list_ns),
|
||||||
// yield the error but set the state to Some to avoid re-try, a future
|
Err(err) => {
|
||||||
// next() will then see the state, and the empty check yield's None
|
// yield the error but set the state to Some to avoid re-try, a future
|
||||||
self.state = Some(state);
|
// next() will then see the state, and the empty check yield's None
|
||||||
return Some(Err(err));
|
self.state = Some(state);
|
||||||
|
return Some(Err(err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.state = Some(state);
|
self.state = Some(state);
|
||||||
|
|
Loading…
Reference in New Issue