From 15305aeeddd34704c326ba4537fa6626ae473a67 Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 8 Nov 2024 14:31:11 -0800 Subject: [PATCH] [reconfigurator] add SimRngState to track RNGs (#7019) Currently, each time a component like a system or collection is generated, its RNGs live in their own world. While this is okay for tests, it is somewhat inconvenient for the reconfigurator CLI which would really be able to generate not just one fixed RNG but a succession of generations of RNGs. In some cases, to do incremental work we were reusing existing RNGs (e.g. the sled ID RNG). Unfortunately that led to situations where changing how the example system was generated would also change these downstream IDs. Restructure how the RNGs are managed -- each builder now has its own RNG struct which can be initialized separately (we pass these around rather than `impl Hash` instances for type safety). We also now have a stateful RNG creator which tracks RNG generation numbers and can be used to build downstream RNGs. In an upcoming PR we're going to use this stateful RNG creator in reconfigurator-sim. I've tried to minimize disruption to existing tests, especially those with checked-in fixtures. The way to generate additional sleds does change, though, and because the physical disk and zpool IDs are generated as pure functions of the sled ID, those change too. --- nexus/inventory/src/builder.rs | 42 +++-- nexus/inventory/src/lib.rs | 1 + .../planning/src/blueprint_builder/builder.rs | 37 +++-- .../planning/src/blueprint_builder/zones.rs | 21 ++- nexus/reconfigurator/planning/src/example.rs | 154 ++++++++++++++++-- nexus/reconfigurator/planning/src/planner.rs | 106 ++++++------ .../planning/src/planner/rng.rs | 20 +-- .../output/planner_basic_add_sled_2_3.txt | 64 ++++---- .../output/planner_basic_add_sled_3_5.txt | 104 ++++++------ 9 files changed, 358 insertions(+), 191 deletions(-) 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: