diff --git a/nexus/inventory/src/builder.rs b/nexus/inventory/src/builder.rs index 00c4b7045d..7142b4f46c 100644 --- a/nexus/inventory/src/builder.rs +++ b/nexus/inventory/src/builder.rs @@ -69,6 +69,28 @@ impl std::fmt::Display for CollectorBug { } } +/// Random generator of UUIDs for a [`CollectionBuilder`]. +#[derive(Debug, Clone)] +pub struct CollectionBuilderRng { + // We just generate one UUID for each collection. + id_rng: TypedUuidRng, +} + +impl CollectionBuilderRng { + pub fn from_entropy() -> Self { + CollectionBuilderRng { id_rng: TypedUuidRng::from_entropy() } + } + + pub fn from_seed(seed: H) -> Self { + // Important to add some more bytes here, so that builders with the + // same seed but different purposes don't end up with the same UUIDs. + const SEED_EXTRA: &str = "collection-builder"; + CollectionBuilderRng { + id_rng: TypedUuidRng::from_seed(seed, SEED_EXTRA), + } + } +} + /// Build an inventory [`Collection`] /// /// This interface is oriented around the interfaces used by an actual @@ -92,9 +114,10 @@ pub struct CollectionBuilder { sleds: BTreeMap, clickhouse_keeper_cluster_membership: BTreeSet, - - // We just generate one UUID for each collection. - id_rng: TypedUuidRng, + // CollectionBuilderRng is taken by value, rather than passed in as a + // mutable ref, to encourage a tree-like structure where each RNG is + // generally independent. + rng: CollectionBuilderRng, } impl CollectionBuilder { @@ -120,7 +143,7 @@ impl CollectionBuilder { rot_pages_found: BTreeMap::new(), sleds: BTreeMap::new(), clickhouse_keeper_cluster_membership: BTreeSet::new(), - id_rng: TypedUuidRng::from_entropy(), + rng: CollectionBuilderRng::from_entropy(), } } @@ -133,7 +156,7 @@ impl CollectionBuilder { } Collection { - id: self.id_rng.next(), + id: self.rng.id_rng.next(), errors: self.errors.into_iter().map(|e| e.to_string()).collect(), time_started: self.time_started, time_done: now_db_precision(), @@ -151,15 +174,12 @@ impl CollectionBuilder { } } - /// Within tests, set a seeded RNG for deterministic results. + /// Within tests, set an RNG for deterministic results. /// /// This will ensure that tests that use this builder will produce the same /// results each time they are run. - pub fn set_rng_seed(&mut self, seed: H) -> &mut Self { - // Important to add some more bytes here, so that builders with the - // same seed but different purposes don't end up with the same UUIDs. - const SEED_EXTRA: &str = "collection-builder"; - self.id_rng.set_seed(seed, SEED_EXTRA); + pub fn set_rng(&mut self, rng: CollectionBuilderRng) -> &mut Self { + self.rng = rng; self } diff --git a/nexus/inventory/src/lib.rs b/nexus/inventory/src/lib.rs index 6dee7bb7ec..10a74b6edf 100644 --- a/nexus/inventory/src/lib.rs +++ b/nexus/inventory/src/lib.rs @@ -24,6 +24,7 @@ mod sled_agent_enumerator; // only exposed for test code to construct collections pub use builder::CollectionBuilder; +pub use builder::CollectionBuilderRng; pub use builder::CollectorBug; pub use builder::InventoryError; diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs index c1237fb2bf..0ab08cb2c7 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs @@ -75,7 +75,6 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashSet; use std::fmt; -use std::hash::Hash; use std::net::IpAddr; use std::net::Ipv6Addr; use std::net::SocketAddr; @@ -301,18 +300,20 @@ impl<'a> BlueprintBuilder<'a> { sled_ids: impl Iterator, creator: &str, ) -> Blueprint { - Self::build_empty_with_sleds_impl(sled_ids, creator, PlannerRng::new()) + Self::build_empty_with_sleds_impl( + sled_ids, + creator, + PlannerRng::from_entropy(), + ) } /// A version of [`Self::build_empty_with_sleds`] that allows the - /// blueprint ID to be generated from a random seed. - pub fn build_empty_with_sleds_seeded( + /// blueprint ID to be generated from a deterministic RNG. + pub fn build_empty_with_sleds_seeded( sled_ids: impl Iterator, creator: &str, - seed: H, + rng: PlannerRng, ) -> Blueprint { - let mut rng = PlannerRng::new(); - rng.set_seed(seed); Self::build_empty_with_sleds_impl(sled_ids, creator, rng) } @@ -450,7 +451,7 @@ impl<'a> BlueprintBuilder<'a> { creator: creator.to_owned(), operations: Vec::new(), comments: Vec::new(), - rng: PlannerRng::new(), + rng: PlannerRng::from_entropy(), }) } @@ -579,12 +580,12 @@ impl<'a> BlueprintBuilder<'a> { self.sled_state.insert(sled_id, desired_state); } - /// Within tests, set a seeded RNG for deterministic results. + /// Within tests, set an RNG for deterministic results. /// /// This will ensure that tests that use this builder will produce the same /// results each time they are run. - pub fn set_rng_seed(&mut self, seed: H) -> &mut Self { - self.rng.set_seed(seed); + pub fn set_rng(&mut self, rng: PlannerRng) -> &mut Self { + self.rng = rng; self } @@ -2615,6 +2616,7 @@ pub mod test { use super::*; use crate::example::example; use crate::example::ExampleSystemBuilder; + use crate::example::SimRngState; use crate::system::SledBuilder; use expectorate::assert_contents; use nexus_inventory::CollectionBuilder; @@ -2841,8 +2843,13 @@ pub mod test { fn test_basic() { static TEST_NAME: &str = "blueprint_builder_test_basic"; let logctx = test_setup_log(TEST_NAME); - let (mut example, blueprint1) = - ExampleSystemBuilder::new(&logctx.log, TEST_NAME).build(); + + let mut rng = SimRngState::from_seed(TEST_NAME); + let (mut example, blueprint1) = ExampleSystemBuilder::new_with_rng( + &logctx.log, + rng.next_system_rng(), + ) + .build(); verify_blueprint(&blueprint1); let mut builder = BlueprintBuilder::new_based_on( @@ -2878,7 +2885,9 @@ pub mod test { assert_eq!(diff.sleds_modified.len(), 0); // The next step is adding these zones to a new sled. - let new_sled_id = example.sled_rng.next(); + let mut sled_id_rng = rng.next_sled_id_rng(); + let new_sled_id = sled_id_rng.next(); + let _ = example.system.sled(SledBuilder::new().id(new_sled_id)).unwrap(); let input = example.system.to_planning_input_builder().unwrap().build(); diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs b/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs index 2d9194ee52..ebc2ef8f7b 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs @@ -242,7 +242,8 @@ mod tests { use crate::{ blueprint_builder::{test::verify_blueprint, BlueprintBuilder, Ensure}, - example::ExampleSystemBuilder, + example::{ExampleSystemBuilder, SimRngState}, + planner::rng::PlannerRng, }; use super::*; @@ -252,13 +253,21 @@ mod tests { fn test_builder_zones() { static TEST_NAME: &str = "blueprint_test_builder_zones"; let logctx = test_setup_log(TEST_NAME); - let (mut example, blueprint_initial) = - ExampleSystemBuilder::new(&logctx.log, TEST_NAME).build(); + + let mut rng = SimRngState::from_seed(TEST_NAME); + let (example, blueprint_initial) = ExampleSystemBuilder::new_with_rng( + &logctx.log, + rng.next_system_rng(), + ) + .build(); // Add a completely bare sled to the input. let (new_sled_id, input2) = { + let mut sled_id_rng = rng.next_sled_id_rng(); + let new_sled_id = sled_id_rng.next(); + let mut input = example.input.clone().into_builder(); - let new_sled_id = example.sled_rng.next(); + input .add_sled( new_sled_id, @@ -308,7 +317,7 @@ mod tests { "the_test", ) .expect("creating blueprint builder"); - builder.set_rng_seed((TEST_NAME, "bp2")); + builder.set_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))); // Test adding a new sled with an NTP zone. assert_eq!( @@ -486,7 +495,7 @@ mod tests { "the_test", ) .expect("creating blueprint builder"); - builder.set_rng_seed((TEST_NAME, "bp2")); + builder.set_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))); // This call by itself shouldn't bump the generation number. builder.zones.change_sled_zones(existing_sled_id); diff --git a/nexus/reconfigurator/planning/src/example.rs b/nexus/reconfigurator/planning/src/example.rs index 4745d5dc68..d1d36d8c47 100644 --- a/nexus/reconfigurator/planning/src/example.rs +++ b/nexus/reconfigurator/planning/src/example.rs @@ -4,12 +4,16 @@ //! Example blueprints +use std::fmt; +use std::hash::Hash; use std::net::IpAddr; use std::net::Ipv4Addr; use crate::blueprint_builder::BlueprintBuilder; +use crate::planner::rng::PlannerRng; use crate::system::SledBuilder; use crate::system::SystemDescription; +use nexus_inventory::CollectionBuilderRng; use nexus_types::deployment::Blueprint; use nexus_types::deployment::BlueprintZoneFilter; use nexus_types::deployment::OmicronZoneNic; @@ -23,6 +27,121 @@ use omicron_uuid_kinds::SledKind; use omicron_uuid_kinds::VnicUuid; use typed_rng::TypedUuidRng; +/// Stateful PRNG for generating simulated systems. +/// +/// When generating a succession of simulated systems, this stateful PRNG allows +/// for reproducible generation of those systems after setting an initial seed. +/// The PRNGs are structured in tree form as much as possible, so that (for +/// example) if one part of the system decides to change how many sleds are in +/// the system, it can do so without affecting other UUIDs. +/// +/// We have a number of existing tests that manually set seeds for individual +/// RNG instances. The old-style seeds have been kept around for backwards +/// compatibility. Newer tests should use this struct to generate their RNGs +/// instead, since it conveniently tracks generation numbers for each seed. +#[derive(Clone, Debug)] +pub struct SimRngState { + seed: String, + // Generation numbers for each RNG type. + system_rng_gen: u64, + collection_rng_gen: u64, + planner_rng_gen: u64, + // TODO: Currently, sled IDs are used to generate UUIDs to mutate the + // system. This should be replaced in the future with a richer simulation + // framework. + sled_id_rng_gen: u64, +} + +impl SimRngState { + pub fn from_seed(seed: &str) -> Self { + Self { + seed: seed.to_string(), + system_rng_gen: 0, + collection_rng_gen: 0, + planner_rng_gen: 0, + sled_id_rng_gen: 0, + } + } + + pub fn next_system_rng(&mut self) -> ExampleSystemRng { + // Different behavior for the first system_rng_gen is a bit weird, but + // it retains backwards compatibility with existing tests -- it means + // that generated UUIDs particularly in fixtures don't change. + self.system_rng_gen += 1; + if self.system_rng_gen == 1 { + ExampleSystemRng::from_seed(self.seed.as_str()) + } else { + ExampleSystemRng::from_seed(( + self.seed.as_str(), + self.system_rng_gen, + )) + } + } + + pub fn next_collection_rng(&mut self) -> CollectionBuilderRng { + self.collection_rng_gen += 1; + // We don't need to pass in extra bits unique to collections, because + // `CollectionBuilderRng` adds its own. + CollectionBuilderRng::from_seed(( + self.seed.as_str(), + self.collection_rng_gen, + )) + } + + pub fn next_planner_rng(&mut self) -> PlannerRng { + self.planner_rng_gen += 1; + // We don't need to pass in extra bits unique to the planner, because + // `PlannerRng` adds its own. + PlannerRng::from_seed((self.seed.as_str(), self.planner_rng_gen)) + } + + pub fn next_sled_id_rng(&mut self) -> TypedUuidRng { + self.sled_id_rng_gen += 1; + TypedUuidRng::from_seed( + self.seed.as_str(), + ("sled-id-rng", self.sled_id_rng_gen), + ) + } +} + +#[derive(Debug, Clone)] +pub struct ExampleSystemRng { + seed: String, + sled_rng: TypedUuidRng, + collection_rng: CollectionBuilderRng, + // ExampleSystem instances generate two blueprints: create RNGs for both. + blueprint1_rng: PlannerRng, + blueprint2_rng: PlannerRng, +} + +impl ExampleSystemRng { + pub fn from_seed(seed: H) -> Self { + // This is merely "ExampleSystem" for backwards compatibility with + // existing test fixtures. + let sled_rng = TypedUuidRng::from_seed(&seed, "ExampleSystem"); + // We choose to make our own collection and blueprint RNGs rather than + // passing them in via `SimRngState`. This means that `SimRngState` is + // influenced as little as possible by the specifics of how + // `ExampleSystem` instances are generated, and RNG stability is + // maintained. + let collection_rng = CollectionBuilderRng::from_seed(( + &seed, + "ExampleSystem collection", + )); + let blueprint1_rng = + PlannerRng::from_seed((&seed, "ExampleSystem initial")); + let blueprint2_rng = + PlannerRng::from_seed((&seed, "ExampleSystem make_zones")); + Self { + seed: format!("{:?}", seed), + sled_rng, + collection_rng, + blueprint1_rng, + blueprint2_rng, + } + } +} + /// An example generated system, along with a consistent planning input and /// collection. /// @@ -37,14 +156,6 @@ pub struct ExampleSystem { /// The initial blueprint that was used to describe the system. This /// blueprint has sleds but no zones. pub initial_blueprint: Blueprint, - // If we add more types of RNGs than just sleds here, we'll need to - // expand this to be similar to BlueprintBuilderRng where a root RNG - // creates sub-RNGs. - // - // This is currently only used for tests, so it looks unused in normal - // builds. But in the future it could be used by other consumers, too. - #[allow(dead_code)] - pub(crate) sled_rng: TypedUuidRng, } /// Returns a collection, planning input, and blueprint describing a pretty @@ -64,7 +175,7 @@ pub fn example( #[derive(Debug, Clone)] pub struct ExampleSystemBuilder { log: slog::Logger, - test_name: String, + rng: ExampleSystemRng, // TODO: Store a Policy struct instead of these fields: // https://github.com/oxidecomputer/omicron/issues/6803 nsleds: usize, @@ -89,9 +200,17 @@ impl ExampleSystemBuilder { pub const DEFAULT_EXTERNAL_DNS_COUNT: usize = 0; pub fn new(log: &slog::Logger, test_name: &str) -> Self { + let rng = ExampleSystemRng::from_seed(test_name); + Self::new_with_rng(log, rng) + } + + pub fn new_with_rng(log: &slog::Logger, rng: ExampleSystemRng) -> Self { Self { - log: log.new(slog::o!("component" => "ExampleSystem", "test_name" => test_name.to_string())), - test_name: test_name.to_string(), + log: log.new(slog::o!( + "component" => "ExampleSystem", + "rng_seed" => rng.seed.clone(), + )), + rng, nsleds: Self::DEFAULT_N_SLEDS, ndisks_per_sled: SledBuilder::DEFAULT_NPOOLS, nexus_count: None, @@ -229,6 +348,8 @@ impl ExampleSystemBuilder { "create_disks_in_blueprint" => self.create_disks_in_blueprint, ); + let mut rng = self.rng.clone(); + let mut system = SystemDescription::new(); // Update the system's target counts with the counts. (Note that // there's no external DNS count.) @@ -236,10 +357,8 @@ impl ExampleSystemBuilder { .target_nexus_zone_count(nexus_count.0) .target_internal_dns_zone_count(self.internal_dns_count.0) .target_crucible_pantry_zone_count(self.crucible_pantry_count.0); - let mut sled_rng = - TypedUuidRng::from_seed(&self.test_name, "ExampleSystem"); let sled_ids: Vec<_> = - (0..self.nsleds).map(|_| sled_rng.next()).collect(); + (0..self.nsleds).map(|_| rng.sled_rng.next()).collect(); for sled_id in &sled_ids { let _ = system @@ -260,7 +379,7 @@ impl ExampleSystemBuilder { let initial_blueprint = BlueprintBuilder::build_empty_with_sleds_seeded( base_input.all_sled_ids(SledFilter::Commissioned), "test suite", - (&self.test_name, "ExampleSystem initial"), + rng.blueprint1_rng, ); // Start with an empty collection @@ -278,7 +397,7 @@ impl ExampleSystemBuilder { "test suite", ) .unwrap(); - builder.set_rng_seed((&self.test_name, "ExampleSystem make_zones")); + builder.set_rng(rng.blueprint2_rng); // Add as many external IPs as is necessary for external DNS zones. We // pick addresses in the TEST-NET-2 (RFC 5737) range. @@ -426,7 +545,7 @@ impl ExampleSystemBuilder { let mut builder = system.to_collection_builder().expect("failed to build collection"); - builder.set_rng_seed((&self.test_name, "ExampleSystem collection")); + builder.set_rng(rng.collection_rng); // The blueprint evolves separately from the system -- so it's returned // as a separate value. @@ -435,7 +554,6 @@ impl ExampleSystemBuilder { input: input_builder.build(), collection: builder.build(), initial_blueprint, - sled_rng, }; (example, blueprint) } diff --git a/nexus/reconfigurator/planning/src/planner.rs b/nexus/reconfigurator/planning/src/planner.rs index dc2050cb10..a893163faa 100644 --- a/nexus/reconfigurator/planning/src/planner.rs +++ b/nexus/reconfigurator/planning/src/planner.rs @@ -28,11 +28,11 @@ use nexus_types::external_api::views::SledPolicy; use nexus_types::external_api::views::SledState; use nexus_types::inventory::Collection; use omicron_uuid_kinds::SledUuid; +use rng::PlannerRng; use slog::error; use slog::{info, warn, Logger}; use std::collections::BTreeMap; use std::collections::BTreeSet; -use std::hash::Hash; use std::str::FromStr; pub(crate) use self::omicron_zone_placement::DiscretionaryOmicronZone; @@ -82,10 +82,10 @@ impl<'a> Planner<'a> { /// /// This will ensure that tests that use this builder will produce the same /// results each time they are run. - pub fn with_rng_seed(mut self, seed: H) -> Self { - // This is an owned builder because it is almost never going to be - // conditional. - self.blueprint.set_rng_seed(seed); + pub fn with_rng(mut self, rng: PlannerRng) -> Self { + // This is an owned builder (self rather than &mut self) because it is + // almost never going to be conditional. + self.blueprint.set_rng(rng); self } @@ -869,6 +869,8 @@ mod test { use crate::blueprint_builder::EnsureMultiple; use crate::example::example; use crate::example::ExampleSystemBuilder; + use crate::example::SimRngState; + use crate::planner::rng::PlannerRng; use crate::system::SledBuilder; use chrono::NaiveDateTime; use chrono::TimeZone; @@ -918,8 +920,12 @@ mod test { let logctx = test_setup_log(TEST_NAME); // Use our example system. - let (mut example, blueprint1) = - ExampleSystemBuilder::new(&logctx.log, TEST_NAME).build(); + let mut rng = SimRngState::from_seed(TEST_NAME); + let (mut example, blueprint1) = ExampleSystemBuilder::new_with_rng( + &logctx.log, + rng.next_system_rng(), + ) + .build(); verify_blueprint(&blueprint1); println!("{}", blueprint1.display()); @@ -935,7 +941,7 @@ mod test { &example.collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -957,7 +963,8 @@ mod test { verify_blueprint(&blueprint2); // Now add a new sled. - let new_sled_id = example.sled_rng.next(); + let mut sled_id_rng = rng.next_sled_id_rng(); + let new_sled_id = sled_id_rng.next(); let _ = example.system.sled(SledBuilder::new().id(new_sled_id)).unwrap(); let input = example.system.to_planning_input_builder().unwrap().build(); @@ -971,7 +978,7 @@ mod test { &example.collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("failed to plan"); @@ -1011,7 +1018,7 @@ mod test { &example.collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("failed to plan"); let diff = blueprint4.diff_since_blueprint(&blueprint3); @@ -1050,7 +1057,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp5")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp5"))) .plan() .expect("failed to plan"); @@ -1145,7 +1152,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1223,7 +1230,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1333,7 +1340,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1411,7 +1418,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1445,7 +1452,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("failed to plan"); @@ -1593,7 +1600,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1628,7 +1635,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("failed to re-plan"); @@ -1740,7 +1747,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1818,7 +1825,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -1903,7 +1910,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -2032,7 +2039,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -2169,7 +2176,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -2395,7 +2402,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); @@ -2441,7 +2448,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("succeeded in planner"); @@ -2491,7 +2498,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("succeeded in planner"); @@ -2550,7 +2557,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to plan"); assert_eq!(bp2.cockroachdb_fingerprint, "bp2"); @@ -2577,7 +2584,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("failed to plan"); assert_eq!(bp3.cockroachdb_fingerprint, "bp3"); @@ -2602,7 +2609,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("failed to plan"); assert_eq!(bp4.cockroachdb_fingerprint, "bp4"); @@ -2631,7 +2638,10 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, format!("bp5-{}", preserve_downgrade))) + .with_rng(PlannerRng::from_seed(( + TEST_NAME, + format!("bp5-{}", preserve_downgrade), + ))) .plan() .expect("failed to plan"); assert_eq!(bp5.cockroachdb_fingerprint, "bp5"); @@ -2687,7 +2697,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to re-plan"); @@ -2756,7 +2766,7 @@ mod test { &collection, ) .expect("failed to create planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("failed to re-plan"); @@ -2819,7 +2829,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("plan"); @@ -2887,7 +2897,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("plan"); @@ -2928,7 +2938,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("plan"); @@ -2970,7 +2980,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp5")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp5"))) .plan() .expect("plan"); @@ -3011,7 +3021,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp6")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp6"))) .plan() .expect("plan"); @@ -3042,7 +3052,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp7")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp7"))) .plan() .expect("plan"); @@ -3085,7 +3095,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp8")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp8"))) .plan() .expect("plan"); @@ -3138,7 +3148,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("plan"); @@ -3196,7 +3206,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("plan"); @@ -3234,7 +3244,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("plan"); @@ -3259,7 +3269,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp5")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp5"))) .plan() .expect("plan"); @@ -3292,7 +3302,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp6")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp6"))) .plan() .expect("plan"); @@ -3346,7 +3356,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp2")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp2"))) .plan() .expect("plan"); @@ -3393,7 +3403,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp3")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp3"))) .plan() .expect("plan"); @@ -3421,7 +3431,7 @@ mod test { &collection, ) .expect("created planner") - .with_rng_seed((TEST_NAME, "bp4")) + .with_rng(PlannerRng::from_seed((TEST_NAME, "bp4"))) .plan() .expect("plan"); diff --git a/nexus/reconfigurator/planning/src/planner/rng.rs b/nexus/reconfigurator/planning/src/planner/rng.rs index 93c74ee34e..98d8e2378e 100644 --- a/nexus/reconfigurator/planning/src/planner/rng.rs +++ b/nexus/reconfigurator/planning/src/planner/rng.rs @@ -18,8 +18,8 @@ use typed_rng::TypedUuidRng; use typed_rng::UuidRng; use uuid::Uuid; -#[derive(Debug)] -pub(crate) struct PlannerRng { +#[derive(Clone, Debug)] +pub struct PlannerRng { // Have separate RNGs for the different kinds of UUIDs we might add, // generated from the main RNG. This is so that e.g. adding a new network // interface doesn't alter the blueprint or sled UUID. @@ -34,10 +34,17 @@ pub(crate) struct PlannerRng { } impl PlannerRng { - pub fn new() -> Self { + pub fn from_entropy() -> Self { Self::new_from_parent(StdRng::from_entropy()) } + pub fn from_seed(seed: H) -> Self { + // Important to add some more bytes here, so that builders with the + // same seed but different purposes don't end up with the same UUIDs. + const SEED_EXTRA: &str = "blueprint-builder"; + Self::new_from_parent(typed_rng::from_seed(seed, SEED_EXTRA)) + } + pub fn new_from_parent(mut parent: StdRng) -> Self { let blueprint_rng = UuidRng::from_parent_rng(&mut parent, "blueprint"); let zone_rng = TypedUuidRng::from_parent_rng(&mut parent, "zone"); @@ -56,13 +63,6 @@ impl PlannerRng { } } - pub fn set_seed(&mut self, seed: H) { - // Important to add some more bytes here, so that builders with the - // same seed but different purposes don't end up with the same UUIDs. - const SEED_EXTRA: &str = "blueprint-builder"; - *self = Self::new_from_parent(typed_rng::from_seed(seed, SEED_EXTRA)); - } - pub fn next_blueprint(&mut self) -> Uuid { self.blueprint_rng.next() } diff --git a/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_2_3.txt b/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_2_3.txt index 8a8fc4848f..ac6efa48ff 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_2_3.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_2_3.txt @@ -275,49 +275,49 @@ to: blueprint 4171ad05-89dd-474b-846b-b007e4346366 ADDED SLEDS: - sled b59ec570-2abb-4017-80ce-129d94e7a025 (active): + sled ec61eded-c34f-443d-a580-dadf757529c4 (active): physical disks at generation 1: ---------------------------------------------------------------------- vendor model serial ---------------------------------------------------------------------- -+ fake-vendor fake-model serial-1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf -+ fake-vendor fake-model serial-298d1eec-0313-4a42-8af9-0e51299a14ef -+ fake-vendor fake-model serial-2eed666f-a10b-42d0-b626-68335d3270b8 -+ fake-vendor fake-model serial-6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08 -+ fake-vendor fake-model serial-7aad6fd9-b698-4c77-af6b-947be10ba953 -+ fake-vendor fake-model serial-a5a15e51-c48a-40e4-a2d8-1c7198c1d46b -+ fake-vendor fake-model serial-b81d4993-ea5b-4720-b8c8-2360c1121d6e -+ fake-vendor fake-model serial-d0064c4d-f5f7-4c89-9f37-0ca475048e79 -+ fake-vendor fake-model serial-dba739c1-76e4-4b6a-a173-89c938fa13ef -+ fake-vendor fake-model serial-e6f289fe-142e-4778-8629-dc87adb53f06 ++ fake-vendor fake-model serial-28699448-c5d9-49ea-bf7e-627800efe783 ++ fake-vendor fake-model serial-2c490e96-27f2-4a7f-b440-04d4bfd1e4f6 ++ fake-vendor fake-model serial-4c3bb1c7-55b6-49b8-b212-516b8f2c26c2 ++ fake-vendor fake-model serial-5db07562-31a8-43e3-b99e-7c7cb89754b7 ++ fake-vendor fake-model serial-9451a5d5-b358-4719-b6c1-a0d187da217c ++ fake-vendor fake-model serial-bb2e2869-9481-483a-bc49-2bdd62f515f5 ++ fake-vendor fake-model serial-d5a36c66-4b2f-46e6-96f4-b82debee1a4a ++ fake-vendor fake-model serial-f99ec996-ec08-4ccf-9a6e-6c5cab440fb4 ++ fake-vendor fake-model serial-faccbb39-d686-42a1-a50a-0eb59ba74a87 ++ fake-vendor fake-model serial-fdfd067b-1d86-444d-a21f-ed33709f3e4d datasets at generation 1: ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- dataset name dataset uuid quota reservation compression ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -+ oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/debug bbab775c-07b1-48b1-96a3-9aa155396112 100 GiB none off -+ oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/zone 07381a6a-e397-4ff9-a2a6-8f1e46c47eb3 none none off -+ oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/zone/oxz_ntp_2d73d30e-ca47-46a8-9c12-917d4ab824b6 15e9b1ea-8947-48a2-8d78-54665e4d2f03 none none off -+ oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crypt/debug 75c1432c-19e1-4c06-8393-9907a09efcbe 100 GiB none off -+ oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crypt/zone b4c23ac2-86af-4b47-9d8d-35eff7997788 none none off -+ oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crypt/debug 5501c13c-0500-41a6-9c48-85924383fdcc 100 GiB none off -+ oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crypt/zone 32143abb-e501-450b-9398-7d4dbebdf4a1 none none off -+ oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crypt/debug bcdd7a5a-59da-4a47-8a1c-79281428d71b 100 GiB none off -+ oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crypt/zone 6ec35c5c-9d6c-4d72-8bec-af91069ef7a9 none none off -+ oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crypt/debug 8ed9d1a2-3c60-4f38-9e80-5c56d80e67c9 100 GiB none off -+ oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crypt/zone 8529e7f8-8c11-4869-b330-83ddc45ed17a none none off -+ oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crypt/debug 4aef9a3a-0829-4ada-a0e9-a45c91e74249 100 GiB none off -+ oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crypt/zone 940f06cb-822f-4034-8d34-e14bcc6ea998 none none off -+ oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crypt/debug 2b23d885-836a-4270-886d-08640aae90aa 100 GiB none off -+ oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crypt/zone 0507b005-e018-4b69-9d84-50faf61e792f none none off -+ oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crypt/debug feac64a1-ade2-4f88-8c17-64d2863e2be6 100 GiB none off -+ oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crypt/zone 1acaf776-970a-49cf-9f1c-7d8e3146ef11 none none off -+ oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crypt/debug 91edd9a1-178d-4aa5-83a7-b2f4ef1fc44a 100 GiB none off -+ oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crypt/zone 1bd5ab24-2a54-438f-bae0-af1b06a3cc41 none none off -+ oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crypt/debug 8840ccd9-e8c9-48d9-affb-8587e468b204 100 GiB none off -+ oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crypt/zone b501adbf-be36-4724-9409-329f690fb09d none none off ++ oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/debug bbab775c-07b1-48b1-96a3-9aa155396112 100 GiB none off ++ oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/zone 07381a6a-e397-4ff9-a2a6-8f1e46c47eb3 none none off ++ oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/zone/oxz_ntp_2d73d30e-ca47-46a8-9c12-917d4ab824b6 15e9b1ea-8947-48a2-8d78-54665e4d2f03 none none off ++ oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crypt/debug 75c1432c-19e1-4c06-8393-9907a09efcbe 100 GiB none off ++ oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crypt/zone b4c23ac2-86af-4b47-9d8d-35eff7997788 none none off ++ oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crypt/debug 5501c13c-0500-41a6-9c48-85924383fdcc 100 GiB none off ++ oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crypt/zone 32143abb-e501-450b-9398-7d4dbebdf4a1 none none off ++ oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crypt/debug bcdd7a5a-59da-4a47-8a1c-79281428d71b 100 GiB none off ++ oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crypt/zone 6ec35c5c-9d6c-4d72-8bec-af91069ef7a9 none none off ++ oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crypt/debug 8ed9d1a2-3c60-4f38-9e80-5c56d80e67c9 100 GiB none off ++ oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crypt/zone 8529e7f8-8c11-4869-b330-83ddc45ed17a none none off ++ oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crypt/debug 4aef9a3a-0829-4ada-a0e9-a45c91e74249 100 GiB none off ++ oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crypt/zone 940f06cb-822f-4034-8d34-e14bcc6ea998 none none off ++ oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crypt/debug 2b23d885-836a-4270-886d-08640aae90aa 100 GiB none off ++ oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crypt/zone 0507b005-e018-4b69-9d84-50faf61e792f none none off ++ oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crypt/debug feac64a1-ade2-4f88-8c17-64d2863e2be6 100 GiB none off ++ oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crypt/zone 1acaf776-970a-49cf-9f1c-7d8e3146ef11 none none off ++ oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crypt/debug 91edd9a1-178d-4aa5-83a7-b2f4ef1fc44a 100 GiB none off ++ oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crypt/zone 1bd5ab24-2a54-438f-bae0-af1b06a3cc41 none none off ++ oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crypt/debug 8840ccd9-e8c9-48d9-affb-8587e468b204 100 GiB none off ++ oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crypt/zone b501adbf-be36-4724-9409-329f690fb09d none none off omicron zones at generation 2: diff --git a/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_3_5.txt b/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_3_5.txt index baf38dd2a5..3060764842 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_3_5.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_basic_add_sled_3_5.txt @@ -275,69 +275,69 @@ to: blueprint f432fcd5-1284-4058-8b4a-9286a3de6163 MODIFIED SLEDS: - sled b59ec570-2abb-4017-80ce-129d94e7a025 (active): + sled ec61eded-c34f-443d-a580-dadf757529c4 (active): physical disks at generation 1: ---------------------------------------------------------------------- vendor model serial ---------------------------------------------------------------------- - fake-vendor fake-model serial-1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf - fake-vendor fake-model serial-298d1eec-0313-4a42-8af9-0e51299a14ef - fake-vendor fake-model serial-2eed666f-a10b-42d0-b626-68335d3270b8 - fake-vendor fake-model serial-6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08 - fake-vendor fake-model serial-7aad6fd9-b698-4c77-af6b-947be10ba953 - fake-vendor fake-model serial-a5a15e51-c48a-40e4-a2d8-1c7198c1d46b - fake-vendor fake-model serial-b81d4993-ea5b-4720-b8c8-2360c1121d6e - fake-vendor fake-model serial-d0064c4d-f5f7-4c89-9f37-0ca475048e79 - fake-vendor fake-model serial-dba739c1-76e4-4b6a-a173-89c938fa13ef - fake-vendor fake-model serial-e6f289fe-142e-4778-8629-dc87adb53f06 + fake-vendor fake-model serial-28699448-c5d9-49ea-bf7e-627800efe783 + fake-vendor fake-model serial-2c490e96-27f2-4a7f-b440-04d4bfd1e4f6 + fake-vendor fake-model serial-4c3bb1c7-55b6-49b8-b212-516b8f2c26c2 + fake-vendor fake-model serial-5db07562-31a8-43e3-b99e-7c7cb89754b7 + fake-vendor fake-model serial-9451a5d5-b358-4719-b6c1-a0d187da217c + fake-vendor fake-model serial-bb2e2869-9481-483a-bc49-2bdd62f515f5 + fake-vendor fake-model serial-d5a36c66-4b2f-46e6-96f4-b82debee1a4a + fake-vendor fake-model serial-f99ec996-ec08-4ccf-9a6e-6c5cab440fb4 + fake-vendor fake-model serial-faccbb39-d686-42a1-a50a-0eb59ba74a87 + fake-vendor fake-model serial-fdfd067b-1d86-444d-a21f-ed33709f3e4d datasets generation 1 -> 2: ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- dataset name dataset uuid quota reservation compression ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/debug bbab775c-07b1-48b1-96a3-9aa155396112 100 GiB none off - oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/zone 07381a6a-e397-4ff9-a2a6-8f1e46c47eb3 none none off - oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/zone/oxz_ntp_2d73d30e-ca47-46a8-9c12-917d4ab824b6 15e9b1ea-8947-48a2-8d78-54665e4d2f03 none none off - oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crypt/debug 75c1432c-19e1-4c06-8393-9907a09efcbe 100 GiB none off - oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crypt/zone b4c23ac2-86af-4b47-9d8d-35eff7997788 none none off - oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crypt/debug 5501c13c-0500-41a6-9c48-85924383fdcc 100 GiB none off - oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crypt/zone 32143abb-e501-450b-9398-7d4dbebdf4a1 none none off - oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crypt/debug bcdd7a5a-59da-4a47-8a1c-79281428d71b 100 GiB none off - oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crypt/zone 6ec35c5c-9d6c-4d72-8bec-af91069ef7a9 none none off - oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crypt/debug 8ed9d1a2-3c60-4f38-9e80-5c56d80e67c9 100 GiB none off - oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crypt/zone 8529e7f8-8c11-4869-b330-83ddc45ed17a none none off - oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crypt/debug 4aef9a3a-0829-4ada-a0e9-a45c91e74249 100 GiB none off - oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crypt/zone 940f06cb-822f-4034-8d34-e14bcc6ea998 none none off - oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crypt/debug 2b23d885-836a-4270-886d-08640aae90aa 100 GiB none off - oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crypt/zone 0507b005-e018-4b69-9d84-50faf61e792f none none off - oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crypt/debug feac64a1-ade2-4f88-8c17-64d2863e2be6 100 GiB none off - oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crypt/zone 1acaf776-970a-49cf-9f1c-7d8e3146ef11 none none off - oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crypt/debug 91edd9a1-178d-4aa5-83a7-b2f4ef1fc44a 100 GiB none off - oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crypt/zone 1bd5ab24-2a54-438f-bae0-af1b06a3cc41 none none off - oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crypt/debug 8840ccd9-e8c9-48d9-affb-8587e468b204 100 GiB none off - oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crypt/zone b501adbf-be36-4724-9409-329f690fb09d none none off -+ oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crucible c842fd37-6d1b-430c-83c5-bb49523434e3 none none off -+ oxp_1bb5ee5d-c2c6-4eaa-86c4-817d89cf10cf/crypt/zone/oxz_crucible_45556184-7092-4a3d-873f-637976bb133b 88f174e0-09e5-4a04-a21f-4885fc7c776b none none off -+ oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crucible 33b4b373-748a-44ce-bae3-d08a6f760f88 none none off -+ oxp_298d1eec-0313-4a42-8af9-0e51299a14ef/crypt/zone/oxz_crucible_9d75abfe-47ab-434a-93dd-af50dc0dddde be00a599-be07-4eb1-8d83-c53a9cdc66cc none none off -+ oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crucible 6cd2973c-3e14-433f-ba9b-d06dc973814f none none off -+ oxp_2eed666f-a10b-42d0-b626-68335d3270b8/crypt/zone/oxz_crucible_f86e19d2-9145-41cf-be89-6aaa34a73873 889580ca-b8a3-4b65-a162-5f5257f193c8 none none off -+ oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crucible 8f5bdd29-1382-42ea-9e3c-b1ac434b8356 none none off -+ oxp_6cc4d7a7-2a89-4f2f-aa55-5e7a10d0fc08/crypt/zone/oxz_crucible_8215bf7a-10d6-4f40-aeb7-27a196307c37 5acdb519-bb18-4e82-ab44-f2dacfc64ce7 none none off -+ oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crucible 7cab9095-e83c-46d5-8290-db28ed5d6909 none none off -+ oxp_7aad6fd9-b698-4c77-af6b-947be10ba953/crypt/zone/oxz_crucible_f6125d45-b9cc-4721-ba60-ed4dbb177e41 42a16f5e-9510-48ca-82a7-7229c2cda8c2 none none off -+ oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crucible 2503ac08-6839-43c5-a2c3-2b08c234ef5f none none off -+ oxp_a5a15e51-c48a-40e4-a2d8-1c7198c1d46b/crypt/zone/oxz_crucible_a36d291c-7f68-462f-830e-bc29e5841ce2 e079d4a4-8b51-4bf2-9f65-f13cb57584ea none none off -+ oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crucible 47880a38-bb35-4619-80fc-2f4578efb231 none none off -+ oxp_b81d4993-ea5b-4720-b8c8-2360c1121d6e/crypt/zone/oxz_crucible_cf5b636b-a505-4db6-bc32-baf9f53f4371 915d03a8-1902-4f81-9d46-0f1987d7a404 none none off -+ oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crucible f99ff9c5-e110-4972-a6d9-627bb6aae3b8 none none off -+ oxp_d0064c4d-f5f7-4c89-9f37-0ca475048e79/crypt/zone/oxz_crucible_28852beb-d0e5-4cba-9adb-e7f0cd4bb864 bfbfbd9d-c656-4f4c-80cd-c91d38d6bdc9 none none off -+ oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crucible c6f0a0c8-8410-47ef-adb8-86e9edc688e5 none none off -+ oxp_dba739c1-76e4-4b6a-a173-89c938fa13ef/crypt/zone/oxz_crucible_b3a4d434-aaee-4752-8c99-69d88fbcb8c5 7a364d04-c4a2-4e2c-8081-c24a276621c5 none none off -+ oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crucible 9aab84cf-3764-4611-892b-76e0570a1699 none none off -+ oxp_e6f289fe-142e-4778-8629-dc87adb53f06/crypt/zone/oxz_crucible_1a20ee3c-f66e-4fca-ab85-2a248aa3d79d 9bbbccf0-a3e1-4d6c-becd-c74a91eef9e8 none none off + oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/debug bbab775c-07b1-48b1-96a3-9aa155396112 100 GiB none off + oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/zone 07381a6a-e397-4ff9-a2a6-8f1e46c47eb3 none none off + oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/zone/oxz_ntp_2d73d30e-ca47-46a8-9c12-917d4ab824b6 15e9b1ea-8947-48a2-8d78-54665e4d2f03 none none off + oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crypt/debug 75c1432c-19e1-4c06-8393-9907a09efcbe 100 GiB none off + oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crypt/zone b4c23ac2-86af-4b47-9d8d-35eff7997788 none none off + oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crypt/debug 5501c13c-0500-41a6-9c48-85924383fdcc 100 GiB none off + oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crypt/zone 32143abb-e501-450b-9398-7d4dbebdf4a1 none none off + oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crypt/debug bcdd7a5a-59da-4a47-8a1c-79281428d71b 100 GiB none off + oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crypt/zone 6ec35c5c-9d6c-4d72-8bec-af91069ef7a9 none none off + oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crypt/debug 8ed9d1a2-3c60-4f38-9e80-5c56d80e67c9 100 GiB none off + oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crypt/zone 8529e7f8-8c11-4869-b330-83ddc45ed17a none none off + oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crypt/debug 4aef9a3a-0829-4ada-a0e9-a45c91e74249 100 GiB none off + oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crypt/zone 940f06cb-822f-4034-8d34-e14bcc6ea998 none none off + oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crypt/debug 2b23d885-836a-4270-886d-08640aae90aa 100 GiB none off + oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crypt/zone 0507b005-e018-4b69-9d84-50faf61e792f none none off + oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crypt/debug feac64a1-ade2-4f88-8c17-64d2863e2be6 100 GiB none off + oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crypt/zone 1acaf776-970a-49cf-9f1c-7d8e3146ef11 none none off + oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crypt/debug 91edd9a1-178d-4aa5-83a7-b2f4ef1fc44a 100 GiB none off + oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crypt/zone 1bd5ab24-2a54-438f-bae0-af1b06a3cc41 none none off + oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crypt/debug 8840ccd9-e8c9-48d9-affb-8587e468b204 100 GiB none off + oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crypt/zone b501adbf-be36-4724-9409-329f690fb09d none none off ++ oxp_28699448-c5d9-49ea-bf7e-627800efe783/crucible c842fd37-6d1b-430c-83c5-bb49523434e3 none none off ++ oxp_28699448-c5d9-49ea-bf7e-627800efe783/crypt/zone/oxz_crucible_45556184-7092-4a3d-873f-637976bb133b 88f174e0-09e5-4a04-a21f-4885fc7c776b none none off ++ oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crucible 33b4b373-748a-44ce-bae3-d08a6f760f88 none none off ++ oxp_2c490e96-27f2-4a7f-b440-04d4bfd1e4f6/crypt/zone/oxz_crucible_9d75abfe-47ab-434a-93dd-af50dc0dddde be00a599-be07-4eb1-8d83-c53a9cdc66cc none none off ++ oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crucible 6cd2973c-3e14-433f-ba9b-d06dc973814f none none off ++ oxp_4c3bb1c7-55b6-49b8-b212-516b8f2c26c2/crypt/zone/oxz_crucible_f86e19d2-9145-41cf-be89-6aaa34a73873 889580ca-b8a3-4b65-a162-5f5257f193c8 none none off ++ oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crucible 8f5bdd29-1382-42ea-9e3c-b1ac434b8356 none none off ++ oxp_5db07562-31a8-43e3-b99e-7c7cb89754b7/crypt/zone/oxz_crucible_8215bf7a-10d6-4f40-aeb7-27a196307c37 5acdb519-bb18-4e82-ab44-f2dacfc64ce7 none none off ++ oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crucible 7cab9095-e83c-46d5-8290-db28ed5d6909 none none off ++ oxp_9451a5d5-b358-4719-b6c1-a0d187da217c/crypt/zone/oxz_crucible_f6125d45-b9cc-4721-ba60-ed4dbb177e41 42a16f5e-9510-48ca-82a7-7229c2cda8c2 none none off ++ oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crucible 2503ac08-6839-43c5-a2c3-2b08c234ef5f none none off ++ oxp_bb2e2869-9481-483a-bc49-2bdd62f515f5/crypt/zone/oxz_crucible_a36d291c-7f68-462f-830e-bc29e5841ce2 e079d4a4-8b51-4bf2-9f65-f13cb57584ea none none off ++ oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crucible 47880a38-bb35-4619-80fc-2f4578efb231 none none off ++ oxp_d5a36c66-4b2f-46e6-96f4-b82debee1a4a/crypt/zone/oxz_crucible_cf5b636b-a505-4db6-bc32-baf9f53f4371 915d03a8-1902-4f81-9d46-0f1987d7a404 none none off ++ oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crucible f99ff9c5-e110-4972-a6d9-627bb6aae3b8 none none off ++ oxp_f99ec996-ec08-4ccf-9a6e-6c5cab440fb4/crypt/zone/oxz_crucible_28852beb-d0e5-4cba-9adb-e7f0cd4bb864 bfbfbd9d-c656-4f4c-80cd-c91d38d6bdc9 none none off ++ oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crucible c6f0a0c8-8410-47ef-adb8-86e9edc688e5 none none off ++ oxp_faccbb39-d686-42a1-a50a-0eb59ba74a87/crypt/zone/oxz_crucible_b3a4d434-aaee-4752-8c99-69d88fbcb8c5 7a364d04-c4a2-4e2c-8081-c24a276621c5 none none off ++ oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crucible 9aab84cf-3764-4611-892b-76e0570a1699 none none off ++ oxp_fdfd067b-1d86-444d-a21f-ed33709f3e4d/crypt/zone/oxz_crucible_1a20ee3c-f66e-4fca-ab85-2a248aa3d79d 9bbbccf0-a3e1-4d6c-becd-c74a91eef9e8 none none off omicron zones generation 2 -> 3: