Skip to content

Commit

Permalink
Add ability to report features on stopped pools
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaublitz committed Jul 10, 2024
1 parent 1fa9f76 commit 8924c5c
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 68 deletions.
16 changes: 16 additions & 0 deletions src/dbus_api/api/prop_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ pub fn locked_pools_to_prop(pools: &LockedPoolsInfo) -> StoppedOrLockedPools {
}

/// Convert a stopped pool data structure to a property format.
///
/// if metadata is true show pool V2 D-Bus attributes such as metadata version and enabled features
/// for the stopped pool.
pub fn stopped_pools_to_prop(pools: &StoppedPoolsInfo, metadata: bool) -> StoppedOrLockedPools {
pools
.stopped
Expand Down Expand Up @@ -119,6 +122,19 @@ pub fn stopped_pools_to_prop(pools: &StoppedPoolsInfo, metadata: bool) -> Stoppe
None => Variant(Box::new((false, 0))),
},
);
map.insert(
"features".to_string(),
match stopped.features {
Some(ref f) => {
let mut feat = HashMap::new();
if f.encryption {
feat.insert("encryption".to_string(), true);
}
Variant(Box::new((true, feat)))
}
None => Variant(Box::new((false, HashMap::<String, bool>::new()))),
},
);
}
(uuid_to_string!(u), map)
})
Expand Down
11 changes: 7 additions & 4 deletions src/engine/sim_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ use crate::{
SomeLockWriteGuard, Table,
},
types::{
CreateAction, DeleteAction, DevUuid, EncryptionInfo, FilesystemUuid, LockedPoolsInfo,
Name, PoolDevice, PoolDiff, PoolIdentifier, PoolUuid, RenameAction, ReportType,
SetUnlockAction, StartAction, StopAction, StoppedPoolInfo, StoppedPoolsInfo,
StratFilesystemDiff, UdevEngineEvent, UnlockMethod,
CreateAction, DeleteAction, DevUuid, EncryptionInfo, Features, FilesystemUuid,
LockedPoolsInfo, Name, PoolDevice, PoolDiff, PoolIdentifier, PoolUuid, RenameAction,
ReportType, SetUnlockAction, StartAction, StopAction, StoppedPoolInfo,
StoppedPoolsInfo, StratFilesystemDiff, UdevEngineEvent, UnlockMethod,
},
StratSigblockVersion,
},
Expand Down Expand Up @@ -256,6 +256,9 @@ impl Engine for SimEngine {
})
.collect::<Vec<_>>(),
metadata_version: Some(StratSigblockVersion::V2),
features: Some(Features {
encryption: pool.is_encrypted(),
}),
},
);
st
Expand Down
73 changes: 46 additions & 27 deletions src/engine/strat_engine/liminal/device_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ use crate::{
backstore::blockdev::{v1, v2},
liminal::{
identify::{DeviceInfo, LuksInfo, StratisDevInfo, StratisInfo},
setup::get_name,
setup::{get_feature_set, get_name},
},
metadata::{StratisIdentifiers, BDA},
},
types::{
DevUuid, EncryptionInfo, LockedPoolInfo, MaybeInconsistent, Name, PoolDevice,
DevUuid, EncryptionInfo, Features, LockedPoolInfo, MaybeInconsistent, Name, PoolDevice,
PoolEncryptionInfo, PoolUuid, StoppedPoolInfo, StratSigblockVersion,
},
},
Expand Down Expand Up @@ -611,19 +611,28 @@ impl DeviceSet {
)
}

/// Get the name, if available, of the pool formed by the devices
/// Get the required pool level metadata information, if available, of the pool formed by the devices
/// in this DeviceSet.
pub fn pool_name(&self) -> StratisResult<MaybeInconsistent<Option<Name>>> {
pub fn pool_level_metadata_info(
&self,
) -> StratisResult<(MaybeInconsistent<Option<Name>>, Option<Features>)> {
match self.as_opened_set() {
Some(set) => get_name(set).map(MaybeInconsistent::No),
Some(set) => get_name(&set).map(MaybeInconsistent::No).and_then(|name| {
get_feature_set(&set).map(|feat| (name, feat.map(Features::from)))
}),
None => gather_pool_name(
self.internal.len(),
self.internal.values().map(|info| match info {
LInfo::Stratis(s) => s.luks.as_ref().map(|l| l.pool_name.as_ref()),
LInfo::Luks(l) => Some(l.pool_name.as_ref()),
}),
)
.map(|opt| opt.expect("self.as_opened_set().is_some() if pool is unencrypted")),
.map(|opt| {
(
opt.expect("self.as_opened_set().is_some() if pool is unencrypted"),
Some(Features { encryption: true }),
)
}),
}
}

Expand Down Expand Up @@ -688,27 +697,37 @@ impl DeviceSet {
self.internal.values().map(|info| info.encryption_info()),
)
.ok()
.map(|info| StoppedPoolInfo {
info,
devices: self
.internal
.iter()
.map(|(uuid, l)| {
let devnode = match l {
LInfo::Stratis(strat_info) => strat_info
.luks
.as_ref()
.map(|l| l.dev_info.devnode.clone())
.unwrap_or_else(|| strat_info.dev_info.devnode.clone()),
LInfo::Luks(luks_info) => luks_info.dev_info.devnode.clone(),
};
PoolDevice {
devnode,
uuid: *uuid,
}
})
.collect::<Vec<_>>(),
metadata_version: self.metadata_version().ok(),
.map(|info| {
let features = match self.pool_level_metadata_info() {
Ok((_, opt)) => opt,
Err(e) => {
warn!("Failed to read metadata for pool: {e}");
None
}
};
StoppedPoolInfo {
info,
devices: self
.internal
.iter()
.map(|(uuid, l)| {
let devnode = match l {
LInfo::Stratis(strat_info) => strat_info
.luks
.as_ref()
.map(|l| l.dev_info.devnode.clone())
.unwrap_or_else(|| strat_info.dev_info.devnode.clone()),
LInfo::Luks(luks_info) => luks_info.dev_info.devnode.clone(),
};
PoolDevice {
devnode,
uuid: *uuid,
}
})
.collect::<Vec<_>>(),
metadata_version: self.metadata_version().ok(),
features,
}
})
}

Expand Down
46 changes: 15 additions & 31 deletions src/engine/strat_engine/liminal/liminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl LiminalDevices {
fn start_pool_failure(
pools: &Table<PoolUuid, AnyPool>,
pool_uuid: PoolUuid,
luks_info: StratisResult<(Option<PoolEncryptionInfo>, MaybeInconsistent<Option<Name>>)>,
luks_info: StratisResult<Option<PoolEncryptionInfo>>,
infos: &HashMap<DevUuid, LStratisDevInfo>,
bdas: HashMap<DevUuid, BDA>,
meta_res: StratisResult<(DateTime<Utc>, PoolSave)>,
Expand Down Expand Up @@ -276,9 +276,7 @@ impl LiminalDevices {
assert!(pools.get_by_uuid(pool_uuid).is_none());
assert!(!self.stopped_pools.contains_key(&pool_uuid));

let encryption_info = stopped_pool.encryption_info();
let pool_name = stopped_pool.pool_name();
let luks_info = encryption_info.and_then(|ei| pool_name.map(|pn| (ei, pn)));
let luks_info = stopped_pool.encryption_info();
let infos = match stopped_pool.into_opened_set() {
Either::Left(i) => i,
Either::Right(ds) => {
Expand Down Expand Up @@ -768,8 +766,8 @@ impl LiminalDevices {
info_map.process_info_add(info);
}

match info_map.pool_name() {
Ok(MaybeInconsistent::No(Some(name))) => {
match info_map.pool_level_metadata_info() {
Ok((MaybeInconsistent::No(Some(name)), _)) => {
if let Some(maybe_conflict) = self.name_to_uuid.get_mut(&name) {
maybe_conflict.add(*pool_uuid);
if let UuidOrConflict::Conflict(set) = maybe_conflict {
Expand Down Expand Up @@ -847,7 +845,7 @@ impl LiminalDevices {
fn try_setup_started_pool_failure(
pools: &Table<PoolUuid, AnyPool>,
pool_uuid: PoolUuid,
luks_info: StratisResult<(Option<PoolEncryptionInfo>, MaybeInconsistent<Option<Name>>)>,
luks_info: StratisResult<Option<PoolEncryptionInfo>>,
infos: &HashMap<DevUuid, LStratisDevInfo>,
bdas: HashMap<DevUuid, BDA>,
metadata_version: StratisResult<StratSigblockVersion>,
Expand Down Expand Up @@ -895,9 +893,7 @@ impl LiminalDevices {
assert!(!self.stopped_pools.contains_key(&pool_uuid));

let metadata_version = device_set.metadata_version();
let encryption_info = device_set.encryption_info();
let pool_name = device_set.pool_name();
let luks_info = encryption_info.and_then(|ei| pool_name.map(|pn| (ei, pn)));
let luks_info = device_set.encryption_info();
let infos = match device_set.into_opened_set() {
Either::Left(i) => i,
Either::Right(ds) => {
Expand Down Expand Up @@ -1074,8 +1070,8 @@ impl LiminalDevices {
.insert(device_path.to_path_buf(), (pool_uuid, device_uuid));

devices.process_info_add(info);
match devices.pool_name() {
Ok(MaybeInconsistent::No(Some(name))) => {
match devices.pool_level_metadata_info() {
Ok((MaybeInconsistent::No(Some(name)), _)) => {
if let Some(maybe_conflict) = self.name_to_uuid.get_mut(&name) {
maybe_conflict.add(pool_uuid);
if let UuidOrConflict::Conflict(set) = maybe_conflict {
Expand Down Expand Up @@ -1111,8 +1107,8 @@ impl LiminalDevices {

devices.process_info_remove(device_path, pool_uuid, dev_uuid);
self.uuid_lookup.remove(device_path);
match devices.pool_name() {
Ok(MaybeInconsistent::No(Some(name))) => {
match devices.pool_level_metadata_info() {
Ok((MaybeInconsistent::No(Some(name)), _)) => {
if let Some(maybe_conflict) = self.name_to_uuid.get_mut(&name) {
maybe_conflict.add(pool_uuid);
if let UuidOrConflict::Conflict(set) = maybe_conflict {
Expand Down Expand Up @@ -1261,7 +1257,7 @@ fn load_stratis_metadata(
)));
}

match get_metadata(infos) {
match get_metadata(&infos) {
Ok(opt) => opt
.ok_or_else(|| {
StratisError::Msg(format!(
Expand All @@ -1288,7 +1284,7 @@ fn load_stratis_metadata(
fn setup_pool_legacy(
pools: &Table<PoolUuid, AnyPool>,
pool_uuid: PoolUuid,
luks_info: StratisResult<(Option<PoolEncryptionInfo>, MaybeInconsistent<Option<Name>>)>,
luks_info: StratisResult<Option<PoolEncryptionInfo>>,
infos: &HashMap<DevUuid, LStratisDevInfo>,
bdas: HashMap<DevUuid, BDA>,
timestamp: DateTime<Utc>,
Expand Down Expand Up @@ -1317,7 +1313,7 @@ fn setup_pool_legacy(
Ok((datadevs, cachedevs)) => (datadevs, cachedevs),
};

let (pool_einfo, pool_name) = match luks_info {
let pool_einfo = match luks_info {
Ok(inner) => inner,
Err(_) => {
// NOTE: This is not actually a hopeless situation. It may be
Expand All @@ -1336,21 +1332,9 @@ fn setup_pool_legacy(

v1::StratPool::setup(pool_uuid, datadevs, cachedevs, timestamp, &metadata, pool_einfo)
.map(|(name, mut pool)| {
if matches!(pool_name, MaybeInconsistent::Yes | MaybeInconsistent::No(None)) || MaybeInconsistent::No(Some(&name)) != pool_name.as_ref() || pool.blockdevs().iter().map(|(_, _, bd)| {
if pool.blockdevs().iter().map(|(_, _, bd)| {
bd.pool_name()
}).fold(false, |acc, next| {
match next {
Some(Some(name)) => {
if MaybeInconsistent::No(Some(name)) == pool_name.as_ref() {
acc
} else {
true
}
},
Some(None) => true,
None => false,
}
}) {
}).any(|name| name != Some(Some(&Name::new(metadata.name.clone()))) || matches!(name, Some(None))) {
if let Err(e) = pool.rename_pool(&name) {
warn!("Pool will not be able to be started by name; pool name metadata in LUKS2 token is not consistent across all devices: {}", e);
}
Expand Down
60 changes: 55 additions & 5 deletions src/engine/strat_engine/liminal/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
device::blkdev_size,
liminal::device_info::{LStratisDevInfo, LStratisInfo},
metadata::BDA,
serde_structs::{BackstoreSave, BaseBlockDevSave, PoolSave},
serde_structs::{BackstoreSave, BaseBlockDevSave, EnabledPoolFeatures, PoolSave},
shared::{bds_to_bdas, tiers_to_bdas},
types::{BDARecordResult, BDAResult},
},
Expand All @@ -44,7 +44,7 @@ use crate::{
///
/// Precondition: infos and bdas have identical sets of keys
pub fn get_metadata(
infos: HashMap<DevUuid, &LStratisInfo>,
infos: &HashMap<DevUuid, &LStratisInfo>,
) -> StratisResult<Option<(DateTime<Utc>, PoolSave)>> {
// Try to read from all available devnodes that could contain most
// recent metadata. In the event of errors, continue to try until all are
Expand Down Expand Up @@ -82,9 +82,7 @@ pub fn get_metadata(
/// metadata could be written.
/// Returns an error if devices provided don't match the devices recorded in the
/// metadata.
///
/// Precondition: infos and bdas have identical sets of keys
pub fn get_name(infos: HashMap<DevUuid, &LStratisInfo>) -> StratisResult<Option<Name>> {
pub fn get_name(infos: &HashMap<DevUuid, &LStratisInfo>) -> StratisResult<Option<Name>> {
let found_uuids = infos.keys().copied().collect::<HashSet<_>>();
match get_metadata(infos)? {
Some((_, pool)) => {
Expand Down Expand Up @@ -128,6 +126,58 @@ pub fn get_name(infos: HashMap<DevUuid, &LStratisInfo>) -> StratisResult<Option<
}
}

/// Get the feature set from the most recent metadata from a set of devices.
/// Returns None if no metadata found for this pool on any device. This can
/// happen if the pool was constructed but failed in the interval before the
/// metadata could be written.
/// Returns an error if devices provided don't match the devices recorded in the
/// metadata.
pub fn get_feature_set(
infos: &HashMap<DevUuid, &LStratisInfo>,
) -> StratisResult<Option<EnabledPoolFeatures>> {
let found_uuids = infos.keys().copied().collect::<HashSet<_>>();
match get_metadata(infos)? {
Some((_, pool)) => {
let v = [];
let meta_uuids = pool
.backstore
.data_tier
.blockdev
.devs
.iter()
.map(|bd| bd.uuid)
.chain(
pool.backstore
.cache_tier
.as_ref()
.map(|ct| ct.blockdev.devs.iter())
.unwrap_or_else(|| v.iter())
.map(|bd| bd.uuid),
)
.collect::<HashSet<_>>();

if found_uuids != meta_uuids {
return Err(StratisError::Msg(format!(
"UUIDs in metadata ({}) did not match UUIDs found ({})",
Itertools::intersperse(
meta_uuids.into_iter().map(|u| u.to_string()),
", ".to_string(),
)
.collect::<String>(),
Itertools::intersperse(
found_uuids.into_iter().map(|u| u.to_string()),
", ".to_string(),
)
.collect::<String>(),
)));
}

Ok(Some(pool.features))
}
None => Ok(None),
}
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices. Sort the blockdevs in the order in which they were
/// recorded in the metadata.
Expand Down
10 changes: 9 additions & 1 deletion src/engine/strat_engine/serde_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use serde::{Serialize, Serializer};

use devicemapper::{Sectors, ThinDevId};

use crate::engine::types::{DevUuid, FilesystemUuid};
use crate::engine::types::{DevUuid, Features, FilesystemUuid};

const MAXIMUM_STRING_SIZE: usize = 255;

Expand Down Expand Up @@ -102,6 +102,14 @@ impl EnabledPoolFeatures {
}
}

impl From<EnabledPoolFeatures> for Features {
fn from(enabled: EnabledPoolFeatures) -> Self {
Features {
encryption: enabled.contains(PoolFeatures::Encryption),
}
}
}

// ALL structs that represent variable length metadata in pre-order
// depth-first traversal order. Note that when organized by types rather than
// values the structure is a DAG not a tree. This just means that there are
Expand Down
Loading

0 comments on commit 8924c5c

Please sign in to comment.