Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jbaublitz metadata rework integrity #3733

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
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
Loading