Skip to content

Commit

Permalink
Merge pull request #3733 from mulkieran/jbaublitz-metadata-rework-int…
Browse files Browse the repository at this point in the history
…egrity

Allow specifying integrity metadata reservation on pool creation
  • Loading branch information
mulkieran authored Jan 6, 2025
2 parents b652554 + eec882a commit 0706663
Show file tree
Hide file tree
Showing 29 changed files with 825 additions and 250 deletions.
2 changes: 1 addition & 1 deletion src/bin/stratisd-tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fn main() {
match c.run(stripped_args) {
Ok(()) => {}
Err(e) => {
eprintln!("Error encountered: {e}");
eprintln!("{e}");
process::exit(1);
}
}
Expand Down
51 changes: 50 additions & 1 deletion src/bin/utils/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ use std::{
str::FromStr,
};

use clap::{Arg, ArgAction, Command};
use clap::{builder::PossibleValuesParser, Arg, ArgAction, Command};

#[cfg(feature = "systemd_compat")]
use clap::builder::Str;
use log::LevelFilter;
use strum::VariantNames;

use devicemapper::Bytes;

use stratisd::engine::{
IntegritySpec, IntegrityTagSpec, ValidatedIntegritySpec, DEFAULT_INTEGRITY_JOURNAL_SIZE,
DEFAULT_INTEGRITY_TAG_SPEC,
};

use crate::utils::predict_usage;

#[cfg(feature = "systemd_compat")]
Expand Down Expand Up @@ -86,6 +92,31 @@ pool is encrypted, setting this option has no effect on the prediction."),
.value_parser(clap::value_parser!(u128))
.help("Size of filesystem to be made for this pool. May be specified multiple times, one for each filesystem. Units are bytes. Must be at least 512 MiB and less than 4 PiB.")
.next_line_help(true)
)
.arg(
Arg::new("integrity_tag_spec")
.long("integrity-tag-spec")
.num_args(1)
.value_parser(PossibleValuesParser::new(IntegrityTagSpec::VARIANTS))
.default_value(DEFAULT_INTEGRITY_TAG_SPEC.as_ref())
.help("Integrity tag specification defining the size of the tag to store a checksum or other value for each block on a device.")
.next_line_help(true)
)
.arg(
Arg::new("integrity_journal_size")
.long("integrity-journal-size")
.num_args(1)
.value_parser(clap::value_parser!(u64))
.default_value(Box::leak((*DEFAULT_INTEGRITY_JOURNAL_SIZE).to_string().into_boxed_str()) as &'static str)
.help("Size of the integrity journal. Default is 128 MiB. Units are bytes.")
.next_line_help(true)
)
.arg(
Arg::new("no_integrity_superblock")
.action(ArgAction::SetTrue)
.long("no-integrity-superblock")
.help("Do not allocate space for integrity superblock")
.next_line_help(true)
),
Command::new("filesystem")
.about("Predicts the space usage when creating a Stratis filesystem.")
Expand Down Expand Up @@ -126,6 +157,24 @@ impl<'a> UtilCommand<'a> for StratisPredictUsage {
sub_m
.get_many::<u128>("filesystem-size")
.map(|szs| szs.map(|n| Bytes(*n)).collect::<Vec<_>>()),
ValidatedIntegritySpec::try_from(IntegritySpec {
journal_size: Some(
sub_m
.get_one::<u64>("integrity_journal_size")
.map(|n| Bytes::from(*n))
.expect("default specified by parser"),
),
tag_spec: Some(
sub_m
.get_one::<String>("integrity_tag_spec")
.map(|sz| {
IntegrityTagSpec::try_from(sz.as_str())
.expect("parser ensures valid value")
})
.expect("default specified by parser"),
),
allocate_superblock: Some(!sub_m.get_flag("no_integrity_superblock")),
})?,
LevelFilter::from_str(
matches
.get_one::<String>("log-level")
Expand Down
14 changes: 10 additions & 4 deletions src/bin/utils/predict_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use serde_json::{json, Value};

use devicemapper::{Bytes, Sectors};

use stratisd::engine::{crypt_metadata_size, integrity_meta_space, ThinPoolSizeParams, BDA};
use stratisd::engine::{
crypt_metadata_size, integrity_meta_space, ThinPoolSizeParams, ValidatedIntegritySpec, BDA,
};

// 2^FS_SIZE_START_POWER is the logical size of the smallest Stratis
// filesystem for which usage data exists in FSSizeLookup::internal, i.e.,
Expand Down Expand Up @@ -161,14 +163,17 @@ pub fn predict_filesystem_usage(
Ok(())
}

fn predict_pool_metadata_usage(device_sizes: Vec<Sectors>) -> Result<Sectors, Box<dyn Error>> {
fn predict_pool_metadata_usage(
device_sizes: Vec<Sectors>,
integrity_spec: ValidatedIntegritySpec,
) -> Result<Sectors, Box<dyn Error>> {
let stratis_metadata_alloc = BDA::default().extended_size().sectors();
let stratis_avail_sizes = device_sizes
.iter()
.map(|&s| {
info!("Total size of device: {:}", s);

let integrity_deduction = integrity_meta_space(s);
let integrity_deduction = integrity_meta_space(s, integrity_spec);
info!(
"Deduction for stratis metadata: {:}",
stratis_metadata_alloc
Expand Down Expand Up @@ -205,6 +210,7 @@ pub fn predict_pool_usage(
overprovisioned: bool,
device_sizes: Vec<Bytes>,
filesystem_sizes: Option<Vec<Bytes>>,
integrity_spec: ValidatedIntegritySpec,
log_level: LevelFilter,
) -> Result<(), Box<dyn Error>> {
Builder::new().filter(None, log_level).init();
Expand All @@ -216,7 +222,7 @@ pub fn predict_pool_usage(
let device_sizes = device_sizes.iter().map(|s| s.sectors()).collect::<Vec<_>>();
let total_size: Sectors = device_sizes.iter().cloned().sum();

let non_metadata_size = predict_pool_metadata_usage(device_sizes)?;
let non_metadata_size = predict_pool_metadata_usage(device_sizes, integrity_spec)?;

let size_params = ThinPoolSizeParams::new(non_metadata_size)?;
let total_non_data = 2usize * size_params.meta_size() + size_params.mdv_size();
Expand Down
6 changes: 3 additions & 3 deletions src/dbus_api/api/manager_3_0/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use futures::executor::block_on;

use crate::{
dbus_api::{
api::shared::EncryptionParams,
blockdev::create_dbus_blockdev,
consts,
filesystem::create_dbus_filesystem,
Expand All @@ -21,15 +22,13 @@ use crate::{
util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option},
},
engine::{
CreateAction, DeleteAction, EncryptionInfo, EngineAction, KeyDescription,
CreateAction, DeleteAction, EncryptionInfo, EngineAction, IntegritySpec, KeyDescription,
MappingCreateAction, MappingDeleteAction, PoolIdentifier, PoolUuid, SetUnlockAction,
UnlockMethod,
},
stratis::StratisError,
};

type EncryptionParams = (Option<(bool, String)>, Option<(bool, (String, String))>);

pub fn destroy_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
let message: &Message = m.msg;
let mut iter = message.iter_init();
Expand Down Expand Up @@ -329,6 +328,7 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
name,
&devs.map(Path::new).collect::<Vec<&Path>>(),
EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(),
IntegritySpec::default(),
)));
match create_result {
Ok(pool_uuid_action) => match pool_uuid_action {
Expand Down
3 changes: 2 additions & 1 deletion src/dbus_api/api/manager_3_5/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
types::{DbusErrorEnum, TData, OK_STRING},
util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option},
},
engine::{CreateAction, EncryptionInfo, KeyDescription, PoolIdentifier},
engine::{CreateAction, EncryptionInfo, IntegritySpec, KeyDescription, PoolIdentifier},
stratis::StratisError,
};

Expand Down Expand Up @@ -65,6 +65,7 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
name,
&devs.map(Path::new).collect::<Vec<&Path>>(),
EncryptionInfo::from_options((key_desc, clevis_info)).as_ref(),
IntegritySpec::default(),
)));
match create_result {
Ok(pool_uuid_action) => match pool_uuid_action {
Expand Down
57 changes: 56 additions & 1 deletion src/dbus_api/api/manager_3_8/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property};

use crate::dbus_api::{
api::{
manager_3_8::{methods::start_pool, props::get_stopped_pools},
manager_3_8::{
methods::{create_pool, start_pool},
props::get_stopped_pools,
},
prop_conv::StoppedOrLockedPools,
},
consts,
Expand All @@ -31,6 +34,58 @@ pub fn start_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TDa
.out_arg(("return_string", "s"))
}

pub fn create_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, TData> {
f.method("CreatePool", (), create_pool)
.in_arg(("name", "s"))
.in_arg(("devices", "as"))
// Optional key description of key in the kernel keyring
// b: true if the pool should be encrypted and able to be
// unlocked with a passphrase associated with this key description.
// s: key description
//
// Rust representation: (bool, String)
.in_arg(("key_desc", "(bs)"))
// Optional Clevis information for binding on initialization.
// b: true if the pool should be encrypted and able to be unlocked
// using Clevis.
// s: pin name
// s: JSON config for Clevis use
//
// Rust representation: (bool, (String, String))
.in_arg(("clevis_info", "(b(ss))"))
// Optional journal size for integrity metadata reservation.
// b: true if the size should be specified.
// false if the default should be used.
// i: Integer representing journal size in bytes.
//
// Rust representation: (bool, u64)
.in_arg(("journal_size", "(bt)"))
// Optional tag size or specification for integrity metadata
// reservation.
// b: true if the size should be specified.
// false if the default should be used.
// s: Tag size specification.
//
// Rust representation: (bool, String)
.in_arg(("tag_spec", "(bs)"))
// Optionally specify whether to reserve space for integrity
// superblock.
// b: true if the second value is to be read, otherwise false.
// b: true if the superblock reservation is supposed to be done
//
// Rust representation: (bool, bool)
.in_arg(("allocate_superblock", "(bb)"))
// In order from left to right:
// b: true if a pool was created and object paths were returned
// o: Object path for Pool
// a(o): Array of object paths for block devices
//
// Rust representation: (bool, (dbus::Path, Vec<dbus::Path>))
.out_arg(("result", "(b(oao))"))
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn stopped_pools_property(f: &Factory<MTSync<TData>, TData>) -> Property<MTSync<TData>, TData> {
f.property::<StoppedOrLockedPools, _>(consts::STOPPED_POOLS_PROP, ())
.access(Access::Read)
Expand Down
Loading

0 comments on commit 0706663

Please sign in to comment.