diff --git a/src/dbus_api/consts.rs b/src/dbus_api/consts.rs index 7c9d144172f..76bee0b704d 100644 --- a/src/dbus_api/consts.rs +++ b/src/dbus_api/consts.rs @@ -60,6 +60,7 @@ pub const FILESYSTEM_DEVNODE_PROP: &str = "Devnode"; pub const FILESYSTEM_POOL_PROP: &str = "Pool"; pub const FILESYSTEM_CREATED_PROP: &str = "Created"; pub const FILESYSTEM_SIZE_PROP: &str = "Size"; +pub const FILESYSTEM_SIZE_LIMIT_PROP: &str = "SizeLimit"; pub const BLOCKDEV_INTERFACE_NAME_3_0: &str = "org.storage.stratis3.blockdev.r0"; pub const BLOCKDEV_INTERFACE_NAME_3_1: &str = "org.storage.stratis3.blockdev.r1"; diff --git a/src/dbus_api/filesystem/filesystem_3_0/props.rs b/src/dbus_api/filesystem/filesystem_3_0/props.rs index cdd1e395bc7..22fd4203bff 100644 --- a/src/dbus_api/filesystem/filesystem_3_0/props.rs +++ b/src/dbus_api/filesystem/filesystem_3_0/props.rs @@ -5,39 +5,14 @@ use dbus::arg::IterAppend; use dbus_tree::{MTSync, MethodErr, PropInfo}; -use crate::{ - dbus_api::{ - filesystem::shared::{self, filesystem_operation}, - types::TData, - }, - engine::{Filesystem, Name}, -}; - -/// Get a filesystem property and place it on the D-Bus. The property is -/// found by means of the getter method which takes a reference to a -/// Filesystem and obtains the property from the filesystem. -fn get_filesystem_property( - i: &mut IterAppend<'_>, - p: &PropInfo<'_, MTSync, TData>, - getter: F, -) -> Result<(), MethodErr> -where - F: Fn((Name, Name, &dyn Filesystem)) -> Result, - R: dbus::arg::Append, -{ - i.append( - filesystem_operation(p.tree, p.path.get_name(), getter) - .map_err(|ref e| MethodErr::failed(e))?, - ); - Ok(()) -} +use crate::dbus_api::{filesystem::shared, types::TData}; /// Get the devnode for an object path. pub fn get_filesystem_devnode( i: &mut IterAppend<'_>, p: &PropInfo<'_, MTSync, TData>, ) -> Result<(), MethodErr> { - get_filesystem_property(i, p, |(pool_name, fs_name, fs)| { + shared::get_filesystem_property(i, p, |(pool_name, fs_name, fs)| { Ok(shared::fs_devnode_prop(fs, &pool_name, &fs_name)) }) } @@ -46,7 +21,7 @@ pub fn get_filesystem_name( i: &mut IterAppend<'_>, p: &PropInfo<'_, MTSync, TData>, ) -> Result<(), MethodErr> { - get_filesystem_property(i, p, |(_, fs_name, _)| Ok(shared::fs_name_prop(&fs_name))) + shared::get_filesystem_property(i, p, |(_, fs_name, _)| Ok(shared::fs_name_prop(&fs_name))) } /// Get the creation date and time in rfc3339 format. @@ -54,7 +29,7 @@ pub fn get_filesystem_created( i: &mut IterAppend<'_>, p: &PropInfo<'_, MTSync, TData>, ) -> Result<(), MethodErr> { - get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_created_prop(fs))) + shared::get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_created_prop(fs))) } /// Get the size of the filesystem in bytes. @@ -62,7 +37,7 @@ pub fn get_filesystem_size( i: &mut IterAppend<'_>, p: &PropInfo<'_, MTSync, TData>, ) -> Result<(), MethodErr> { - get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_size_prop(fs))) + shared::get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_size_prop(fs))) } /// Get the size of the used portion of the filesystem in bytes. @@ -70,5 +45,5 @@ pub fn get_filesystem_used( i: &mut IterAppend<'_>, p: &PropInfo<'_, MTSync, TData>, ) -> Result<(), MethodErr> { - get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_used_prop(fs))) + shared::get_filesystem_property(i, p, |(_, _, fs)| Ok(shared::fs_used_prop(fs))) } diff --git a/src/dbus_api/filesystem/filesystem_3_6/api.rs b/src/dbus_api/filesystem/filesystem_3_6/api.rs new file mode 100644 index 00000000000..2334730c0e3 --- /dev/null +++ b/src/dbus_api/filesystem/filesystem_3_6/api.rs @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Property}; + +use crate::dbus_api::{ + consts, + filesystem::filesystem_3_6::props::{get_fs_size_limit, set_fs_size_limit}, + types::TData, +}; + +pub fn size_limit_property(f: &Factory, TData>) -> Property, TData> { + f.property::<(bool, String), _>(consts::FILESYSTEM_SIZE_LIMIT_PROP, ()) + .access(Access::ReadWrite) + .emits_changed(EmitsChangedSignal::True) + .auto_emit_on_set(false) + .on_get(get_fs_size_limit) + .on_set(set_fs_size_limit) +} diff --git a/src/dbus_api/filesystem/filesystem_3_6/mod.rs b/src/dbus_api/filesystem/filesystem_3_6/mod.rs new file mode 100644 index 00000000000..322dccc1d57 --- /dev/null +++ b/src/dbus_api/filesystem/filesystem_3_6/mod.rs @@ -0,0 +1,8 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +mod api; +mod props; + +pub use api::size_limit_property; diff --git a/src/dbus_api/filesystem/filesystem_3_6/props.rs b/src/dbus_api/filesystem/filesystem_3_6/props.rs new file mode 100644 index 00000000000..e5d5663a632 --- /dev/null +++ b/src/dbus_api/filesystem/filesystem_3_6/props.rs @@ -0,0 +1,60 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use dbus::arg::{Iter, IterAppend}; +use dbus_tree::{MTSync, MethodErr, PropInfo}; + +use devicemapper::Bytes; + +use crate::{ + dbus_api::{ + consts, + filesystem::shared::{self, get_filesystem_property}, + types::TData, + util::tuple_to_option, + }, + engine::PropChangeAction, +}; + +pub fn get_fs_size_limit( + i: &mut IterAppend<'_>, + p: &PropInfo<'_, MTSync, TData>, +) -> Result<(), MethodErr> { + get_filesystem_property(i, p, |(_, _, f)| Ok(shared::fs_size_limit_prop(f))) +} + +pub fn set_fs_size_limit( + i: &mut Iter<'_>, + p: &PropInfo<'_, MTSync, TData>, +) -> Result<(), MethodErr> { + let size_limit_opt: (bool, &str) = i.get().ok_or_else(|| { + MethodErr::failed("New filesystem limit required as argument to increase it") + })?; + let size_limit_str = tuple_to_option(size_limit_opt); + let size_limit = match size_limit_str { + Some(lim) => Some( + Bytes(lim.parse::().map_err(|e| { + MethodErr::failed(&format!("Failed to parse {lim} as unsigned integer: {e}")) + })?) + .sectors(), + ), + None => None, + }; + + let res = shared::set_fs_property_to_display( + p, + consts::FILESYSTEM_SIZE_LIMIT_PROP, + |(_, uuid, fs)| shared::set_fs_size_limit_prop(uuid, fs, size_limit), + ); + match res { + Ok(PropChangeAction::NewValue(v)) => { + p.tree + .get_data() + .push_fs_size_limit_change(p.path.get_name(), v); + Ok(()) + } + Ok(PropChangeAction::Identity) => Ok(()), + Err(e) => Err(e), + } +} diff --git a/src/dbus_api/filesystem/mod.rs b/src/dbus_api/filesystem/mod.rs index 5eeb0c291a6..80a18aa781a 100644 --- a/src/dbus_api/filesystem/mod.rs +++ b/src/dbus_api/filesystem/mod.rs @@ -14,6 +14,7 @@ use crate::{ }; mod filesystem_3_0; +mod filesystem_3_6; pub mod prop_conv; mod shared; @@ -110,7 +111,8 @@ pub fn create_dbus_filesystem<'a>( .add_p(filesystem_3_0::uuid_property(&f)) .add_p(filesystem_3_0::created_property(&f)) .add_p(filesystem_3_0::size_property(&f)) - .add_p(filesystem_3_0::used_property(&f)), + .add_p(filesystem_3_0::used_property(&f)) + .add_p(filesystem_3_6::size_limit_property(&f)), ); let path = object_path.get_name().to_owned(); @@ -189,7 +191,8 @@ pub fn get_fs_properties( consts::FILESYSTEM_POOL_PROP => parent, consts::FILESYSTEM_CREATED_PROP => shared::fs_created_prop(fs), consts::FILESYSTEM_SIZE_PROP => shared::fs_size_prop(fs), - consts::FILESYSTEM_USED_PROP => shared::fs_used_prop(fs) + consts::FILESYSTEM_USED_PROP => shared::fs_used_prop(fs), + consts::FILESYSTEM_USED_PROP => shared::fs_size_limit_prop(fs) } } } diff --git a/src/dbus_api/filesystem/prop_conv.rs b/src/dbus_api/filesystem/prop_conv.rs index 1b52528220f..c1bdee54ace 100644 --- a/src/dbus_api/filesystem/prop_conv.rs +++ b/src/dbus_api/filesystem/prop_conv.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use devicemapper::Bytes; +use devicemapper::{Bytes, Sectors}; use crate::dbus_api::util::option_to_tuple; @@ -17,3 +17,9 @@ pub fn fs_size_to_prop(size: Bytes) -> String { pub fn fs_used_to_prop(used: Option) -> (bool, String) { option_to_tuple(used.map(|u| (*u).to_string()), String::new()) } + +/// Generate D-Bus representation of used property. +#[inline] +pub fn fs_size_limit_to_prop(limit: Option) -> (bool, String) { + option_to_tuple(limit.map(|u| (*u.bytes()).to_string()), String::new()) +} diff --git a/src/dbus_api/filesystem/shared.rs b/src/dbus_api/filesystem/shared.rs index 1dd65b440d0..37e53098a4a 100644 --- a/src/dbus_api/filesystem/shared.rs +++ b/src/dbus_api/filesystem/shared.rs @@ -3,18 +3,20 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use chrono::SecondsFormat; -use dbus::Path; -use dbus_tree::{MTSync, Tree}; +use dbus::{arg::IterAppend, Path}; +use dbus_tree::{MTSync, MethodErr, PropInfo, Tree}; use futures::executor::block_on; +use devicemapper::Sectors; + use crate::{ dbus_api::{filesystem::prop_conv, types::TData}, - engine::{Filesystem, Name, PoolIdentifier}, + engine::{Filesystem, FilesystemUuid, Name, Pool, PoolIdentifier, PropChangeAction, ToDisplay}, }; /// Get execute a given closure providing a filesystem object and return /// the calculated value -pub fn filesystem_operation( +pub fn filesystem_get_operation( tree: &Tree, TData>, object_path: &Path<'static>, closure: F, @@ -61,6 +63,116 @@ where closure((pool_name, fs_name, fs)) } +/// Get a filesystem property and place it on the D-Bus. The property is +/// found by means of the getter method which takes a reference to a +/// Filesystem and obtains the property from the filesystem. +pub fn get_filesystem_property( + i: &mut IterAppend<'_>, + p: &PropInfo<'_, MTSync, TData>, + getter: F, +) -> Result<(), MethodErr> +where + F: Fn((Name, Name, &dyn Filesystem)) -> Result, + R: dbus::arg::Append, +{ + i.append( + filesystem_get_operation(p.tree, p.path.get_name(), getter) + .map_err(|ref e| MethodErr::failed(e))?, + ); + Ok(()) +} + +/// Set a filesystem property. The property is set by means of +/// the getter method which takes a mutable reference to a +/// Pool for MDV update purposes. +pub fn fs_set_operation( + tree: &Tree, TData>, + object_path: &dbus::Path<'static>, + closure: F, +) -> Result +where + F: Fn((Name, FilesystemUuid, &mut dyn Pool)) -> Result, +{ + let dbus_context = tree.get_data(); + + let fs_path = tree + .get(object_path) + .expect("implicit argument must be in tree"); + let fs_uuid = typed_uuid_string_err!( + fs_path + .get_data() + .as_ref() + .ok_or_else(|| format!("no data for object path {object_path}"))? + .uuid; + Fs + ); + let pool_path = tree + .get( + &fs_path + .get_data() + .as_ref() + .ok_or_else(|| format!("no data for object path {object_path}"))? + .parent, + ) + .ok_or_else(|| "Parent not found in tree".to_string())?; + let pool_uuid = typed_uuid_string_err!( + pool_path + .get_data() + .as_ref() + .ok_or_else(|| format!("no data for object path {object_path}"))? + .uuid; + Pool + ); + + let mut guard = block_on( + dbus_context + .engine + .get_mut_pool(PoolIdentifier::Uuid(pool_uuid)), + ) + .ok_or_else(|| format!("no pool corresponding to uuid {pool_uuid}"))?; + let (name, _) = guard + .get_filesystem(fs_uuid) + .ok_or_else(|| format!("no filesystem corresponding to uuid {fs_uuid}"))?; + + closure((name, fs_uuid, &mut *guard)) +} + +/// Set a filesystem property. The property is found by means of the setter method which +/// takes a mutable reference to a Filesystem and sets the property on the filesystem. +pub fn set_fs_property_to_display( + p: &PropInfo<'_, MTSync, TData>, + prop_name: &str, + setter: F, +) -> Result +where + F: Fn((Name, FilesystemUuid, &mut dyn Pool)) -> Result, + R: ToDisplay, +{ + info!("Setting property {}", prop_name); + let res = + fs_set_operation(p.tree, p.path.get_name(), setter).map_err(|ref e| MethodErr::failed(e)); + let display = res.as_ref().map(|r| r.to_display()); + let _ = handle_action!(display); + res +} + +/// Set the filesystem size limit for a given filesystem. +#[inline] +pub fn fs_size_limit_prop(fs: &dyn Filesystem) -> (bool, String) { + prop_conv::fs_size_limit_to_prop(fs.size_limit()) +} + +/// Set the filesystem size limit for a given filesystem. +#[inline] +pub fn set_fs_size_limit_prop( + uuid: FilesystemUuid, + pool: &mut dyn Pool, + limit: Option, +) -> Result>, String> { + pool.set_fs_size_limit(uuid, limit) + .map_err(|e| e.to_string()) +} + /// Generate D-Bus representation of name property. #[inline] pub fn fs_name_prop(name: &Name) -> String { diff --git a/src/dbus_api/tree.rs b/src/dbus_api/tree.rs index 130525399df..960d2410618 100644 --- a/src/dbus_api/tree.rs +++ b/src/dbus_api/tree.rs @@ -30,7 +30,7 @@ use crate::{ blockdev_user_info_to_prop, }, consts, - filesystem::prop_conv::{fs_size_to_prop, fs_used_to_prop}, + filesystem::prop_conv::{fs_size_limit_to_prop, fs_size_to_prop, fs_used_to_prop}, pool::prop_conv::{ avail_actions_to_prop, clevis_info_to_prop, key_desc_to_prop, pool_alloc_to_prop, pool_size_to_prop, pool_used_to_prop, @@ -766,6 +766,26 @@ impl DbusTreeHandler { } } + /// Send a signal indicating that the filesystem size limit has changed. + fn handle_fs_size_limit_change(&self, path: Path<'static>, new_size_limit: Option) { + let size_limit = fs_size_limit_to_prop(new_size_limit); + if let Err(e) = self.property_changed_invalidated_signal( + &path, + prop_hashmap!( + consts::FILESYSTEM_INTERFACE_NAME_3_6 => { + Vec::new(), + consts::FILESYSTEM_SIZE_LIMIT_PROP.to_string() => + box_variant!(size_limit) + } + ), + ) { + warn!( + "Failed to send a signal over D-Bus indicating filesystem size limit change: {}", + e + ); + } + } + /// Send a signal indicating that the blockdev user info has changed. fn handle_blockdev_user_info_change(&self, path: Path<'static>, new_user_info: Option) { let user_info_prop = blockdev_user_info_to_prop(new_user_info); @@ -1105,6 +1125,10 @@ impl DbusTreeHandler { self.handle_pool_fs_limit_change(path, new_limit); Ok(true) } + DbusAction::FsSizeLimitChange(path, new_limit) => { + self.handle_fs_size_limit_change(path, new_limit); + Ok(true) + } DbusAction::PoolOverprovModeChange(path, new_mode) => { self.handle_pool_overprov_mode_change(path, new_mode); Ok(true) diff --git a/src/dbus_api/types.rs b/src/dbus_api/types.rs index 0e50a77681e..4fdeac12ae1 100644 --- a/src/dbus_api/types.rs +++ b/src/dbus_api/types.rs @@ -106,6 +106,7 @@ pub enum DbusAction { StoppedPoolsChange(StoppedPoolsInfo), BlockdevUserInfoChange(Path<'static>, Option), BlockdevTotalPhysicalSizeChange(Path<'static>, Sectors), + FsSizeLimitChange(Path<'static>, Option), FsBackgroundChange( FilesystemUuid, SignalChange>, @@ -365,6 +366,19 @@ impl DbusContext { } } + /// Send changed signal for pool FsLimit property. + pub fn push_fs_size_limit_change(&self, item: &Path<'static>, new_size_limit: Option) { + if let Err(e) = self + .sender + .send(DbusAction::FsSizeLimitChange(item.clone(), new_size_limit)) + { + warn!( + "D-Bus filesystem size limit change event could not be sent to the processing thread; no signal will be sent out for the size limit change of filesystem with path {}: {}", + item, e, + ) + } + } + /// Send changed signal for pool overprovisioning mode property. pub fn push_pool_overprov_mode_change(&self, item: &Path<'static>, new_mode: bool) { if let Err(e) = self diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 31dfb74cd64..995e6d66113 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -31,7 +31,7 @@ use crate::{ stratis::StratisResult, }; -use super::types::StratBlockDevDiff; +use super::{types::StratBlockDevDiff, PropChangeAction}; pub const DEV_PATH: &str = "/dev/stratis"; /// The maximum size of pool passphrases stored in the kernel keyring @@ -90,6 +90,9 @@ pub trait Filesystem: Debug { /// Get the size of the filesystem in bytes. fn size(&self) -> Bytes; + + /// Get filesystem size limit. + fn size_limit(&self) -> Option; } pub trait BlockDev: Debug { @@ -328,6 +331,13 @@ pub trait Pool: Debug + Send + Sync { pool_uuid: PoolUuid, device: DevUuid, ) -> StratisResult<(GrowAction<(PoolUuid, DevUuid)>, Option)>; + + /// Set filesystem size limit. + fn set_fs_size_limit( + &mut self, + fs: FilesystemUuid, + limit: Option, + ) -> StratisResult>>; } pub type HandleEvents

= ( diff --git a/src/engine/sim_engine/filesystem.rs b/src/engine/sim_engine/filesystem.rs index 3f995073958..68551b31029 100644 --- a/src/engine/sim_engine/filesystem.rs +++ b/src/engine/sim_engine/filesystem.rs @@ -16,6 +16,7 @@ pub struct SimFilesystem { rand: u32, created: DateTime, size: Sectors, + size_limit: Option, } impl SimFilesystem { @@ -24,12 +25,18 @@ impl SimFilesystem { rand: rand::random::(), created: Utc::now(), size, + size_limit: None, } } pub fn size(&self) -> Sectors { self.size } + + /// Set the size limit for the SimFilesystem. + pub fn set_size_limit(&mut self, limit: Option) { + self.size_limit = limit; + } } impl Filesystem for SimFilesystem { @@ -54,6 +61,10 @@ impl Filesystem for SimFilesystem { fn size(&self) -> Bytes { self.size.bytes() } + + fn size_limit(&self) -> Option { + self.size_limit + } } impl<'a> Into for &'a SimFilesystem { @@ -68,6 +79,15 @@ impl<'a> Into for &'a SimFilesystem { .unwrap_or_else(|_| "Unavailable".to_string()), ), ); + json.insert( + "size_limit".to_string(), + Value::from( + self.size_limit + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_else(|| "Not set".to_string()), + ), + ); Value::from(json) } } diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index cc4f258f84b..20d6958d28b 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -27,6 +27,7 @@ use crate::{ PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, }, + PropChangeAction, }, stratis::{StratisError, StratisResult}, }; @@ -691,6 +692,22 @@ impl Pool for SimPool { ) -> StratisResult<(GrowAction<(PoolUuid, DevUuid)>, Option)> { Ok((GrowAction::Identity, None)) } + + fn set_fs_size_limit( + &mut self, + fs_uuid: FilesystemUuid, + limit: Option, + ) -> StratisResult>> { + let (_, fs) = self.filesystems.get_mut_by_uuid(fs_uuid).ok_or_else(|| { + StratisError::Msg(format!("Filesystem with UUID {fs_uuid} not found")) + })?; + if fs.size_limit() == limit { + Ok(PropChangeAction::Identity) + } else { + fs.set_size_limit(limit); + Ok(PropChangeAction::NewValue(limit)) + } + } } #[cfg(test)] diff --git a/src/engine/strat_engine/pool.rs b/src/engine/strat_engine/pool.rs index 2e773bcc19a..1fab983090f 100644 --- a/src/engine/strat_engine/pool.rs +++ b/src/engine/strat_engine/pool.rs @@ -32,6 +32,7 @@ use crate::{ PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, StratFilesystemDiff, StratPoolDiff, }, + PropChangeAction, }, stratis::{StratisError, StratisResult}, }; @@ -1219,6 +1220,19 @@ impl Pool for StratPool { Ok((GrowAction::Identity, None)) } } + + #[pool_mutating_action("NoRequests")] + fn set_fs_size_limit( + &mut self, + fs_uuid: FilesystemUuid, + limit: Option, + ) -> StratisResult>> { + if self.thin_pool.set_fs_size_limit(fs_uuid, limit)? { + Ok(PropChangeAction::NewValue(limit)) + } else { + Ok(PropChangeAction::Identity) + } + } } pub struct StratPoolState { diff --git a/src/engine/strat_engine/thinpool/filesystem.rs b/src/engine/strat_engine/thinpool/filesystem.rs index 2267c6f6b47..e15776bce2d 100644 --- a/src/engine/strat_engine/thinpool/filesystem.rs +++ b/src/engine/strat_engine/thinpool/filesystem.rs @@ -390,7 +390,7 @@ impl StratFilesystem { Ok(ret_vec) } - pub fn set_size_limit(&mut self, limit: Option) -> StratisResult<()> { + pub fn set_size_limit(&mut self, limit: Option) -> StratisResult { match limit { Some(lim) if self.thindev_size() > lim => Err(StratisError::Msg(format!( "Limit requested of {} is smaller than current filesystem size of {}", @@ -398,16 +398,16 @@ impl StratFilesystem { self.thindev_size() ))), Some(_) | None => { - self.size_limit = limit; - Ok(()) + if self.size_limit == limit { + Ok(false) + } else { + self.size_limit = limit; + Ok(true) + } } } } - pub fn size_limit(&self) -> Option { - self.size_limit - } - pub fn thindev_size(&self) -> Sectors { self.thin_dev.size() } @@ -446,6 +446,10 @@ impl Filesystem for StratFilesystem { fn size(&self) -> Bytes { self.thin_dev.size().bytes() } + + fn size_limit(&self) -> Option { + self.size_limit + } } /// Represents the state of the Stratis filesystem at a given moment in time. @@ -523,6 +527,15 @@ impl<'a> Into for &'a StratFilesystem { .unwrap_or_else(|_| "Unavailable".to_string()), ), ); + json.insert( + "size_limit".to_string(), + Value::from( + self.size_limit + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_else(|| "Not set".to_string()), + ), + ); Value::from(json) } } diff --git a/src/engine/strat_engine/thinpool/thinpool.rs b/src/engine/strat_engine/thinpool/thinpool.rs index e274a37492a..9404c8d6387 100644 --- a/src/engine/strat_engine/thinpool/thinpool.rs +++ b/src/engine/strat_engine/thinpool/thinpool.rs @@ -23,7 +23,7 @@ use devicemapper::{ use crate::{ engine::{ - engine::{DumpState, StateDiff}, + engine::{DumpState, Filesystem, StateDiff}, strat_engine::{ backstore::Backstore, cmd::{thin_check, thin_metadata_size, thin_repair}, @@ -1556,6 +1556,27 @@ impl ThinPool { pub fn clear_out_of_meta_flag(&mut self) { self.out_of_meta_space = false; } + + /// Set the filesystem size limit for filesystem with given UUID. + pub fn set_fs_size_limit( + &mut self, + fs_uuid: FilesystemUuid, + limit: Option, + ) -> StratisResult { + let changed = { + let (_, fs) = self.get_mut_filesystem_by_uuid(fs_uuid).ok_or_else(|| { + StratisError::Msg(format!("No filesystem with UUID {fs_uuid} found")) + })?; + fs.set_size_limit(limit)? + }; + let (name, fs) = self + .get_filesystem_by_uuid(fs_uuid) + .ok_or_else(|| StratisError::Msg(format!("No filesystem with UUID {fs_uuid} found")))?; + if changed { + self.mdv.save_fs(&name, fs_uuid, fs)?; + } + Ok(changed) + } } impl<'a> Into for &'a ThinPool { diff --git a/src/engine/types/actions.rs b/src/engine/types/actions.rs index 8d130ec17f7..1e029748d27 100644 --- a/src/engine/types/actions.rs +++ b/src/engine/types/actions.rs @@ -773,7 +773,7 @@ pub enum PropChangeAction { impl ToDisplay for PropChangeAction> where - T: Display, + T: ToString, { type Display = PropChangeAction; @@ -781,7 +781,7 @@ where match self { PropChangeAction::Identity => PropChangeAction::Identity, PropChangeAction::NewValue(Some(v)) => { - PropChangeAction::NewValue(format!("a value of {v}")) + PropChangeAction::NewValue(format!("a value of {}", v.to_string())) } PropChangeAction::NewValue(None) => { PropChangeAction::NewValue("an empty value".to_string())