diff --git a/src/dbus_api/pool/mod.rs b/src/dbus_api/pool/mod.rs index 9bce858b2a..1667b58f63 100644 --- a/src/dbus_api/pool/mod.rs +++ b/src/dbus_api/pool/mod.rs @@ -260,6 +260,7 @@ pub fn create_dbus_pool<'a>( .add_m(pool_3_0::rename_method(&f)) .add_m(pool_3_3::grow_physical_device_method(&f)) .add_m(pool_3_7::get_metadata_method(&f)) + .add_m(pool_3_7::get_fs_metadata_method(&f)) .add_p(pool_3_0::name_property(&f)) .add_p(pool_3_0::uuid_property(&f)) .add_p(pool_3_0::encrypted_property(&f)) diff --git a/src/dbus_api/pool/pool_3_7/api.rs b/src/dbus_api/pool/pool_3_7/api.rs index 59603f4e34..d6e4f4cfb8 100644 --- a/src/dbus_api/pool/pool_3_7/api.rs +++ b/src/dbus_api/pool/pool_3_7/api.rs @@ -7,7 +7,7 @@ use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property}; use crate::dbus_api::{ consts, pool::pool_3_7::{ - methods::{destroy_filesystems, metadata}, + methods::{destroy_filesystems, fs_metadata, metadata}, props::get_pool_metadata_version, }, types::TData, @@ -47,3 +47,16 @@ pub fn metadata_version_property( .emits_changed(EmitsChangedSignal::Const) .on_get(get_pool_metadata_version) } + +pub fn get_fs_metadata_method(f: &Factory, TData>) -> Method, TData> { + f.method("FilesystemMetadata", (), fs_metadata) + .in_arg(("fs_name", "(bs)")) + .in_arg(("current", "b")) + // A string representing the pool's filesystem metadata in serialized + // JSON format. + // + // Rust representation: String + .out_arg(("results", "s")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} diff --git a/src/dbus_api/pool/pool_3_7/methods.rs b/src/dbus_api/pool/pool_3_7/methods.rs index 1d3bbfbb48..6508e974be 100644 --- a/src/dbus_api/pool/pool_3_7/methods.rs +++ b/src/dbus_api/pool/pool_3_7/methods.rs @@ -11,7 +11,7 @@ use crate::{ dbus_api::{ consts::filesystem_interface_list, types::{DbusErrorEnum, TData, OK_STRING}, - util::{engine_to_dbus_err_tuple, get_next_arg}, + util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option}, }, engine::{EngineAction, FilesystemUuid, StratisUuid}, }; @@ -148,3 +148,47 @@ pub fn metadata(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { }; Ok(vec![msg]) } + +pub fn fs_metadata(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let default_return = String::new(); + + let message: &Message = m.msg; + + let return_message = message.method_return(); + + let dbus_context = m.tree.get_data(); + let object_path = m.path.get_name(); + + let pool_path = m + .tree + .get(object_path) + .expect("implicit argument must be in tree"); + let pool_uuid = typed_uuid!( + get_data!(pool_path; default_return; return_message).uuid; + Pool; + default_return; + return_message + ); + + let mut iter = message.iter_init(); + let filesystem_name: Option<&str> = tuple_to_option(get_next_arg(&mut iter, 0)?); + let current: bool = get_next_arg(&mut iter, 1)?; + + let guard = get_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let (_, _, pool) = guard.as_tuple(); + + let result = if current { + pool.current_fs_metadata(filesystem_name) + } else { + pool.last_fs_metadata(filesystem_name) + }; + + let msg = match result { + Ok(v) => return_message.append3(v, DbusErrorEnum::OK as u16, OK_STRING.to_string()), + Err(err) => { + let (rc, rs) = engine_to_dbus_err_tuple(&err); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} diff --git a/src/dbus_api/pool/pool_3_7/mod.rs b/src/dbus_api/pool/pool_3_7/mod.rs index 834df4053d..037c6a6758 100644 --- a/src/dbus_api/pool/pool_3_7/mod.rs +++ b/src/dbus_api/pool/pool_3_7/mod.rs @@ -6,4 +6,7 @@ mod api; mod methods; mod props; -pub use api::{destroy_filesystems_method, get_metadata_method, metadata_version_property}; +pub use api::{ + destroy_filesystems_method, get_fs_metadata_method, get_metadata_method, + metadata_version_property, +}; diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 66d01ef8fb..5a72a6d9bb 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -350,6 +350,12 @@ pub trait Pool: Debug + Send + Sync { /// Get the metadata version for a given pool. fn metadata_version(&self) -> StratSigblockVersion; + + /// Get the filesystem metadata that would be written if written now. + fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult; + + /// Get the last written filesystem metadata. + fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult; } pub type HandleEvents

= ( diff --git a/src/engine/sim_engine/filesystem.rs b/src/engine/sim_engine/filesystem.rs index 5198f234bf..b6661bab3a 100644 --- a/src/engine/sim_engine/filesystem.rs +++ b/src/engine/sim_engine/filesystem.rs @@ -10,10 +10,25 @@ use serde_json::{Map, Value}; use devicemapper::{Bytes, Sectors}; use crate::{ - engine::{types::FilesystemUuid, Filesystem}, + engine::{ + types::{FilesystemUuid, Name}, + Filesystem, + }, stratis::{StratisError, StratisResult}, }; +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct FilesystemSave { + name: String, + uuid: FilesystemUuid, + size: Sectors, + created: u64, + #[serde(skip_serializing_if = "Option::is_none")] + fs_size_limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + origin: Option, +} + #[derive(Debug)] pub struct SimFilesystem { rand: u32, @@ -73,6 +88,17 @@ impl SimFilesystem { self.origin = None; changed } + + pub fn record(&self, name: &Name, uuid: FilesystemUuid) -> FilesystemSave { + FilesystemSave { + name: name.to_owned(), + uuid, + size: self.size, + created: self.created.timestamp() as u64, + fs_size_limit: self.size_limit, + origin: self.origin, + } + } } impl Filesystem for SimFilesystem { diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index 45f45eb0f6..71e9dc6953 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -758,6 +758,29 @@ impl Pool for SimPool { fn metadata_version(&self) -> StratSigblockVersion { StratSigblockVersion::V2 } + + fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + serde_json::to_string( + &self + .filesystems + .iter() + .filter_map(|(name, uuid, fs)| { + if fs_name.map(|n| *n == **name).unwrap_or(true) { + Some((uuid, fs.record(name, *uuid))) + } else { + None + } + }) + .collect::>(), + ) + .map_err(|e| e.into()) + } + + fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + // The sim pool doesn't write data, so the last fs metadata and the + // current fs metadata are, by definition, the same. + self.current_fs_metadata(fs_name) + } } #[cfg(test)] diff --git a/src/engine/strat_engine/pool/dispatch.rs b/src/engine/strat_engine/pool/dispatch.rs index 3c17b2e361..c1baf3d45f 100644 --- a/src/engine/strat_engine/pool/dispatch.rs +++ b/src/engine/strat_engine/pool/dispatch.rs @@ -348,4 +348,18 @@ impl Pool for AnyPool { AnyPool::V2(p) => p.metadata_version(), } } + + fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + match self { + AnyPool::V1(p) => p.current_fs_metadata(fs_name), + AnyPool::V2(p) => p.current_fs_metadata(fs_name), + } + } + + fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + match self { + AnyPool::V1(p) => p.last_fs_metadata(fs_name), + AnyPool::V2(p) => p.last_fs_metadata(fs_name), + } + } } diff --git a/src/engine/strat_engine/pool/v1.rs b/src/engine/strat_engine/pool/v1.rs index 932dbe3849..6c2506c5c5 100644 --- a/src/engine/strat_engine/pool/v1.rs +++ b/src/engine/strat_engine/pool/v1.rs @@ -1293,6 +1293,14 @@ impl Pool for StratPool { fn metadata_version(&self) -> StratSigblockVersion { StratSigblockVersion::V1 } + + fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + self.thin_pool.current_fs_metadata(fs_name) + } + + fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + self.thin_pool.last_fs_metadata(fs_name) + } } pub struct StratPoolState { diff --git a/src/engine/strat_engine/pool/v2.rs b/src/engine/strat_engine/pool/v2.rs index e82c6cfbca..f8af4f4696 100644 --- a/src/engine/strat_engine/pool/v2.rs +++ b/src/engine/strat_engine/pool/v2.rs @@ -1200,6 +1200,14 @@ impl Pool for StratPool { fn metadata_version(&self) -> StratSigblockVersion { StratSigblockVersion::V2 } + + fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + self.thin_pool.current_fs_metadata(fs_name) + } + + fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + self.thin_pool.last_fs_metadata(fs_name) + } } pub struct StratPoolState { diff --git a/src/engine/strat_engine/thinpool/thinpool.rs b/src/engine/strat_engine/thinpool/thinpool.rs index 1dcfb5e389..97ea5d79df 100644 --- a/src/engine/strat_engine/thinpool/thinpool.rs +++ b/src/engine/strat_engine/thinpool/thinpool.rs @@ -728,6 +728,43 @@ impl ThinPool { pub fn clear_out_of_meta_flag(&mut self) { self.out_of_meta_space = false; } + + /// Calculate filesystem metadata from current state + pub fn current_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + serde_json::to_string( + &self + .filesystems + .iter() + .filter_map(|(name, uuid, fs)| { + if fs_name.map(|n| *n == **name).unwrap_or(true) { + Some((*uuid, fs.record(name, *uuid))) + } else { + None + } + }) + .collect::>(), + ) + .map_err(|e| e.into()) + } + + /// Read filesystem metadata from mdv + pub fn last_fs_metadata(&self, fs_name: Option<&str>) -> StratisResult { + serde_json::to_string( + &self + .mdv + .filesystems()? + .iter() + .filter_map(|fssave| { + if fs_name.map(|n| *n == fssave.name).unwrap_or(true) { + Some((fssave.uuid, fssave)) + } else { + None + } + }) + .collect::>(), + ) + .map_err(|e| e.into()) + } } impl ThinPool { diff --git a/tests/client-dbus/src/stratisd_client_dbus/_introspect.py b/tests/client-dbus/src/stratisd_client_dbus/_introspect.py index 0afe2924d4..973c53a2db 100644 --- a/tests/client-dbus/src/stratisd_client_dbus/_introspect.py +++ b/tests/client-dbus/src/stratisd_client_dbus/_introspect.py @@ -176,6 +176,13 @@ + + + + + + +