From a583f7519a87b297c829ee05e45bc920da2546a4 Mon Sep 17 00:00:00 2001 From: "Andrew J. Stone" Date: Thu, 16 May 2024 14:13:36 -0400 Subject: [PATCH] [omdb] Show physical disks in blueprints and inventory (#5745) This PR changes the construction and display of blueprint diffs significantly. The need for the changes arose out of the tight coupling between zones and sleds. When adding disks to the mix we needed to also take them into account when determininig when a sled was modified. We also want to display physical disks as tables along with the zone tables under the sleds. This turned out to be somewhat trickier than necesary, and so I changed how the tables were constructed and rendered. Hopefully this will also make it easier to add new tables in the future. One possibly controversial change is that I changed the way zone modifications are rendered. They are no longer three lines long. Currently only `disposition` is allowed to change, and so I used an arrow from "old" to "new" versions. Additionally, I removed the (added/ modified/removed) suffixes as they seem redundant to me, make the lines longer, and make things harder to read IMO. I'm open to discussion about both of these changes. My guess is that eventually, we'll want to be able to do row filtering, and I plan to also add some colored output, also in this PR most likely. It should be noted that the output mechanism is decoupled from the representation of the tables, `BpSledSubtable`. This allows output in other formats if desired in the future. As for the inventory collection output, I added a requirement when filtering on sled-id, that we also filter on collection-id, because filtering on sled-id does a full table scan and requires a new index. I can add the index instead if we feel it's important. Fixes #5624 --- Cargo.lock | 2 +- clients/sled-agent-client/src/lib.rs | 2 +- common/src/disk.rs | 2 +- dev-tools/omdb/src/bin/omdb/db.rs | 73 + dev-tools/omdb/src/bin/omdb/nexus.rs | 2 +- dev-tools/omdb/tests/successes.out | 125 +- dev-tools/reconfigurator-cli/src/main.rs | 8 +- .../tests/output/cmd-stdout | 20 +- .../db-queries/src/db/datastore/deployment.rs | 6 +- .../planning/src/blueprint_builder/builder.rs | 89 +- .../planning/src/blueprint_builder/zones.rs | 78 +- nexus/reconfigurator/planning/src/example.rs | 1 + nexus/reconfigurator/planning/src/planner.rs | 245 ++-- nexus/reconfigurator/planning/src/system.rs | 5 +- .../output/blueprint_builder_initial_diff.txt | 164 ++- .../output/planner_basic_add_sled_2_3.txt | 197 ++- .../output/planner_basic_add_sled_3_5.txt | 217 ++- .../planner_decommissions_sleds_1_2.txt | 195 +-- .../planner_decommissions_sleds_bp2.txt | 156 ++- .../output/planner_nonprovisionable_1_2.txt | 305 ++-- .../output/planner_nonprovisionable_2_2a.txt | 315 +++-- .../output/planner_nonprovisionable_bp2.txt | 253 ++-- nexus/types/Cargo.toml | 2 +- nexus/types/src/deployment.rs | 1244 +++-------------- nexus/types/src/deployment/blueprint_diff.rs | 854 +++++++++++ .../types/src/deployment/blueprint_display.rs | 331 +++++ nexus/types/src/lib.rs | 1 - nexus/types/src/sectioned_table.rs | 357 ----- .../tests/output/self-stat-schema.json | 6 +- sled-storage/src/disk.rs | 11 +- 30 files changed, 2992 insertions(+), 2274 deletions(-) create mode 100644 nexus/types/src/deployment/blueprint_diff.rs create mode 100644 nexus/types/src/deployment/blueprint_display.rs delete mode 100644 nexus/types/src/sectioned_table.rs diff --git a/Cargo.lock b/Cargo.lock index 3bdac72238..9ead6007ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4892,6 +4892,7 @@ dependencies = [ "chrono", "clap", "derive-where", + "derive_more", "dns-service-client", "futures", "gateway-client", @@ -4914,7 +4915,6 @@ dependencies = [ "slog-error-chain", "steno", "strum", - "tabled", "test-strategy", "thiserror", "uuid", diff --git a/clients/sled-agent-client/src/lib.rs b/clients/sled-agent-client/src/lib.rs index bfb97ec9cd..a0145af910 100644 --- a/clients/sled-agent-client/src/lib.rs +++ b/clients/sled-agent-client/src/lib.rs @@ -35,6 +35,7 @@ progenitor::generate_api!( PortConfigV1 = { derives = [PartialEq, Eq, Hash, Serialize, Deserialize] }, RouteConfig = { derives = [PartialEq, Eq, Hash, Serialize, Deserialize] }, IpNet = { derives = [PartialEq, Eq, Hash, Serialize, Deserialize] }, + OmicronPhysicalDiskConfig = { derives = [Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord] } }, //TODO trade the manual transformations later in this file for the // replace directives below? @@ -62,7 +63,6 @@ progenitor::generate_api!( // We cannot easily configure progenitor to derive `Eq` on all the client- // generated types because some have floats and other types that can't impl // `Eq`. We impl it explicitly for a few types on which we need it. -impl Eq for types::OmicronPhysicalDiskConfig {} impl Eq for types::OmicronPhysicalDisksConfig {} impl Eq for types::OmicronZonesConfig {} impl Eq for types::OmicronZoneConfig {} diff --git a/common/src/disk.rs b/common/src/disk.rs index 0cf9b6e073..c6d60c5140 100644 --- a/common/src/disk.rs +++ b/common/src/disk.rs @@ -22,6 +22,6 @@ use serde::{Deserialize, Serialize}; )] pub struct DiskIdentity { pub vendor: String, - pub serial: String, pub model: String, + pub serial: String, } diff --git a/dev-tools/omdb/src/bin/omdb/db.rs b/dev-tools/omdb/src/bin/omdb/db.rs index 0bbb232d55..5b029b0908 100644 --- a/dev-tools/omdb/src/bin/omdb/db.rs +++ b/dev-tools/omdb/src/bin/omdb/db.rs @@ -51,6 +51,7 @@ use nexus_db_model::ExternalIp; use nexus_db_model::HwBaseboardId; use nexus_db_model::Instance; use nexus_db_model::InvCollection; +use nexus_db_model::InvPhysicalDisk; use nexus_db_model::IpAttachState; use nexus_db_model::IpKind; use nexus_db_model::NetworkInterface; @@ -98,6 +99,7 @@ use omicron_common::api::external::InstanceState; use omicron_common::api::external::MacAddr; use omicron_uuid_kinds::CollectionUuid; use omicron_uuid_kinds::GenericUuid; +use omicron_uuid_kinds::SledUuid; use sled_agent_client::types::VolumeConstructionRequest; use std::borrow::Cow; use std::cmp::Ordering; @@ -381,6 +383,8 @@ enum InventoryCommands { Cabooses, /// list and show details from particular collections Collections(CollectionsArgs), + /// show all physical disks every found + PhysicalDisks(PhysicalDisksArgs), /// list all root of trust pages ever found RotPages, } @@ -408,6 +412,15 @@ struct CollectionsShowArgs { show_long_strings: bool, } +#[derive(Debug, Args, Clone, Copy)] +struct PhysicalDisksArgs { + #[clap(long)] + collection_id: Option, + + #[clap(long, requires("collection_id"))] + sled_id: Option, +} + #[derive(Debug, Args)] struct ReconfiguratorSaveArgs { /// where to save the output @@ -2652,6 +2665,9 @@ async fn cmd_db_inventory( ) .await } + InventoryCommands::PhysicalDisks(args) => { + cmd_db_inventory_physical_disks(&conn, limit, args).await + } InventoryCommands::RotPages => { cmd_db_inventory_rot_pages(&conn, limit).await } @@ -2736,6 +2752,63 @@ async fn cmd_db_inventory_cabooses( Ok(()) } +async fn cmd_db_inventory_physical_disks( + conn: &DataStoreConnection<'_>, + limit: NonZeroU32, + args: PhysicalDisksArgs, +) -> Result<(), anyhow::Error> { + #[derive(Tabled)] + #[tabled(rename_all = "SCREAMING_SNAKE_CASE")] + struct DiskRow { + inv_collection_id: Uuid, + sled_id: Uuid, + slot: i64, + vendor: String, + model: String, + serial: String, + variant: String, + } + + use db::schema::inv_physical_disk::dsl; + let mut query = dsl::inv_physical_disk.into_boxed(); + query = query.limit(i64::from(u32::from(limit))); + + if let Some(collection_id) = args.collection_id { + query = query.filter( + dsl::inv_collection_id.eq(collection_id.into_untyped_uuid()), + ); + } + + if let Some(sled_id) = args.sled_id { + query = query.filter(dsl::sled_id.eq(sled_id.into_untyped_uuid())); + } + + let disks = query + .select(InvPhysicalDisk::as_select()) + .load_async(&**conn) + .await + .context("loading physical disks")?; + + let rows = disks.into_iter().map(|disk| DiskRow { + inv_collection_id: disk.inv_collection_id.into_untyped_uuid(), + sled_id: disk.sled_id.into_untyped_uuid(), + slot: disk.slot, + vendor: disk.vendor, + model: disk.model.clone(), + serial: disk.model.clone(), + variant: format!("{:?}", disk.variant), + }); + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("{}", table); + + Ok(()) +} + async fn cmd_db_inventory_rot_pages( conn: &DataStoreConnection<'_>, limit: NonZeroU32, diff --git a/dev-tools/omdb/src/bin/omdb/nexus.rs b/dev-tools/omdb/src/bin/omdb/nexus.rs index 1a12cbdf35..22fe1894cf 100644 --- a/dev-tools/omdb/src/bin/omdb/nexus.rs +++ b/dev-tools/omdb/src/bin/omdb/nexus.rs @@ -1171,7 +1171,7 @@ async fn cmd_nexus_blueprints_diff( args.blueprint2_id.resolve_to_blueprint(client), ) .await?; - let diff = b2.diff_since_blueprint(&b1).context("diffing blueprints")?; + let diff = b2.diff_since_blueprint(&b1); println!("{}", diff.display()); Ok(()) } diff --git a/dev-tools/omdb/tests/successes.out b/dev-tools/omdb/tests/successes.out index 8c68b0f431..c4c28460b8 100644 --- a/dev-tools/omdb/tests/successes.out +++ b/dev-tools/omdb/tests/successes.out @@ -501,27 +501,28 @@ stdout: blueprint ............. parent: - ----------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP - ----------------------------------------------------------------------------------------- - - sled .....................: blueprint zones at generation 2 - (no zones) - - sled .....................: blueprint zones at generation 2 - clickhouse ..................... in service ::1 - cockroach_db ..................... in service ::1 - crucible_pantry ..................... in service ::1 - external_dns ..................... in service ::1 - internal_dns ..................... in service ::1 - nexus ..................... in service ::ffff:127.0.0.1 - -METADATA: - created by: nexus-test-utils - created at: - comment: initial test blueprint - internal DNS version: 1 - external DNS version: 2 +!..................... +WARNING: Zones exist without physical disks! + omicron zones at generation 2: + --------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + --------------------------------------------------------------------------------------- + clickhouse ..................... in service ::1 + cockroach_db ..................... in service ::1 + crucible_pantry ..................... in service ::1 + external_dns ..................... in service ::1 + internal_dns ..................... in service ::1 + nexus ..................... in service ::ffff:127.0.0.1 + + + + METADATA: + created by::::::::::: nexus-test-utils + created at::::::::::: + comment:::::::::::::: initial test blueprint + internal DNS version: 1 + external DNS version: 2 + --------------------------------------------- stderr: @@ -534,27 +535,28 @@ stdout: blueprint ............. parent: - ----------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP - ----------------------------------------------------------------------------------------- - - sled .....................: blueprint zones at generation 2 - (no zones) - - sled .....................: blueprint zones at generation 2 - clickhouse ..................... in service ::1 - cockroach_db ..................... in service ::1 - crucible_pantry ..................... in service ::1 - external_dns ..................... in service ::1 - internal_dns ..................... in service ::1 - nexus ..................... in service ::ffff:127.0.0.1 - -METADATA: - created by: nexus-test-utils - created at: - comment: initial test blueprint - internal DNS version: 1 - external DNS version: 2 +!..................... +WARNING: Zones exist without physical disks! + omicron zones at generation 2: + --------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + --------------------------------------------------------------------------------------- + clickhouse ..................... in service ::1 + cockroach_db ..................... in service ::1 + crucible_pantry ..................... in service ::1 + external_dns ..................... in service ::1 + internal_dns ..................... in service ::1 + nexus ..................... in service ::ffff:127.0.0.1 + + + + METADATA: + created by::::::::::: nexus-test-utils + created at::::::::::: + comment:::::::::::::: initial test blueprint + internal DNS version: 1 + external DNS version: 2 + --------------------------------------------- stderr: @@ -567,23 +569,28 @@ stdout: from: blueprint ............. to: blueprint ............. - --------------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP status - --------------------------------------------------------------------------------------------------- - - UNCHANGED SLEDS: - - sled .....................: blueprint zones at generation 2 - clickhouse ..................... in service ::1 - cockroach_db ..................... in service ::1 - crucible_pantry ..................... in service ::1 - external_dns ..................... in service ::1 - internal_dns ..................... in service ::1 - nexus ..................... in service ::ffff:127.0.0.1 - - METADATA: - internal DNS version: 1 (unchanged) - external DNS version: 2 (unchanged) + UNCHANGED SLEDS: + + sled .....................: + + sled .....................: + + omicron zones at generation 2: + --------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + --------------------------------------------------------------------------------------- + clickhouse ..................... in service ::1 + cockroach_db ..................... in service ::1 + crucible_pantry ..................... in service ::1 + external_dns ..................... in service ::1 + internal_dns ..................... in service ::1 + nexus ..................... in service ::ffff:127.0.0.1 + + + METADATA: + internal DNS version: 1 (unchanged) + external DNS version: 2 (unchanged) + --------------------------------------------- stderr: diff --git a/dev-tools/reconfigurator-cli/src/main.rs b/dev-tools/reconfigurator-cli/src/main.rs index f088c9d97d..1c9d9866a8 100644 --- a/dev-tools/reconfigurator-cli/src/main.rs +++ b/dev-tools/reconfigurator-cli/src/main.rs @@ -780,9 +780,7 @@ fn cmd_blueprint_diff( let blueprint1 = sim.blueprint_lookup(blueprint1_id)?; let blueprint2 = sim.blueprint_lookup(blueprint2_id)?; - let sled_diff = blueprint2 - .diff_since_blueprint(&blueprint1) - .context("failed to diff blueprints")?; + let sled_diff = blueprint2.diff_since_blueprint(&blueprint1); swriteln!(rv, "{}", sled_diff.display()); // Diff'ing DNS is a little trickier. First, compute what DNS should be for @@ -897,9 +895,7 @@ fn cmd_blueprint_diff_inventory( anyhow!("no such inventory collection: {}", collection_id) })?; let blueprint = sim.blueprint_lookup(blueprint_id)?; - let diff = blueprint - .diff_since_collection(&collection) - .context("failed to diff blueprint from inventory collection")?; + let diff = blueprint.diff_since_collection(&collection); Ok(Some(diff.display().to_string())) } diff --git a/dev-tools/reconfigurator-cli/tests/output/cmd-stdout b/dev-tools/reconfigurator-cli/tests/output/cmd-stdout index 273e847a86..a2d6d3d17b 100644 --- a/dev-tools/reconfigurator-cli/tests/output/cmd-stdout +++ b/dev-tools/reconfigurator-cli/tests/output/cmd-stdout @@ -24,25 +24,25 @@ sled ..................... subnet fd00:1122:3344:101::/64 zpools (10): ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } ..................... (zpool) - ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", serial: "serial-.....................", model: "fake-model" }, disk_id: ..................... (physical_disk), policy: InService, state: Active } + ↳ SledDisk { disk_identity: DiskIdentity { vendor: "fake-vendor", model: "fake-model", serial: "serial-....................." }, disk_id: ..................... (physical_disk), policy: InService, state: Active } > sled-add ..................... diff --git a/nexus/db-queries/src/db/datastore/deployment.rs b/nexus/db-queries/src/db/datastore/deployment.rs index 09bc2eef0f..003b64fd78 100644 --- a/nexus/db-queries/src/db/datastore/deployment.rs +++ b/nexus/db-queries/src/db/datastore/deployment.rs @@ -1658,7 +1658,7 @@ mod tests { let blueprint2 = builder.build(); let authz_blueprint2 = authz_blueprint_from_id(blueprint2.id); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!("b1 -> b2: {}", diff.display()); println!("b1 disks: {:?}", blueprint1.blueprint_disks); println!("b2 disks: {:?}", blueprint2.blueprint_disks); @@ -1699,9 +1699,7 @@ mod tests { .blueprint_read(&opctx, &authz_blueprint2) .await .expect("failed to read collection back"); - let diff = blueprint_read - .diff_since_blueprint(&blueprint2) - .expect("failed to diff blueprints"); + let diff = blueprint_read.diff_since_blueprint(&blueprint2); println!("diff: {}", diff.display()); assert_eq!(blueprint2, blueprint_read); assert_eq!(blueprint2.internal_dns_version, new_internal_dns_version); diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs index 7e34bf9691..8c8f4f3e29 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs @@ -45,7 +45,7 @@ use omicron_common::api::external::MacAddr; use omicron_common::api::external::Vni; use omicron_common::api::internal::shared::NetworkInterface; use omicron_common::api::internal::shared::NetworkInterfaceKind; -use omicron_uuid_kinds::ExternalIpUuid; +use omicron_uuid_kinds::ExternalIpKind; use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::OmicronZoneKind; use omicron_uuid_kinds::OmicronZoneUuid; @@ -807,7 +807,7 @@ impl<'a> BlueprintBuilder<'a> { for _ in 0..num_nexus_to_add { let nexus_id = self.rng.zone_rng.next(); let external_ip = OmicronZoneExternalFloatingIp { - id: ExternalIpUuid::new_v4(), + id: self.rng.external_ip_rng.next(), ip: self .available_external_ips .next() @@ -992,6 +992,7 @@ struct BlueprintBuilderRng { blueprint_rng: UuidRng, zone_rng: TypedUuidRng, network_interface_rng: UuidRng, + external_ip_rng: TypedUuidRng, } impl BlueprintBuilderRng { @@ -1004,8 +1005,15 @@ impl BlueprintBuilderRng { let zone_rng = TypedUuidRng::from_parent_rng(&mut parent, "zone"); let network_interface_rng = UuidRng::from_parent_rng(&mut parent, "network_interface"); - - BlueprintBuilderRng { blueprint_rng, zone_rng, network_interface_rng } + let external_ip_rng = + TypedUuidRng::from_parent_rng(&mut parent, "external_ip"); + + BlueprintBuilderRng { + blueprint_rng, + zone_rng, + network_interface_rng, + external_ip_rng, + } } fn set_seed(&mut self, seed: H) { @@ -1220,6 +1228,7 @@ pub mod test { use crate::example::ExampleSystem; use crate::system::SledBuilder; use expectorate::assert_contents; + use nexus_types::deployment::BlueprintOrCollectionZoneConfig; use nexus_types::deployment::BlueprintZoneFilter; use nexus_types::external_api::views::SledPolicy; use omicron_common::address::IpRange; @@ -1260,8 +1269,7 @@ pub mod test { example(&logctx.log, TEST_NAME, DEFAULT_N_SLEDS); verify_blueprint(&blueprint_initial); - let diff = - blueprint_initial.diff_since_collection(&collection).unwrap(); + let diff = blueprint_initial.diff_since_collection(&collection); // There are some differences with even a no-op diff between a // collection and a blueprint, such as new data being added to // blueprints like DNS generation numbers. @@ -1274,9 +1282,9 @@ pub mod test { "tests/output/blueprint_builder_initial_diff.txt", &diff.display().to_string(), ); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); // Test a no-op blueprint. let builder = BlueprintBuilder::new_based_on( @@ -1288,14 +1296,14 @@ pub mod test { .expect("failed to create builder"); let blueprint = builder.build(); verify_blueprint(&blueprint); - let diff = blueprint.diff_since_blueprint(&blueprint_initial).unwrap(); + let diff = blueprint.diff_since_blueprint(&blueprint_initial); println!( "initial blueprint -> next blueprint (expected no changes):\n{}", diff.display() ); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); logctx.cleanup_successful(); } @@ -1331,14 +1339,14 @@ pub mod test { let blueprint2 = builder.build(); verify_blueprint(&blueprint2); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!( "initial blueprint -> next blueprint (expected no changes):\n{}", diff.display() ); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + 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(); @@ -1361,35 +1369,43 @@ pub mod test { let blueprint3 = builder.build(); verify_blueprint(&blueprint3); - let diff = blueprint3.diff_since_blueprint(&blueprint2).unwrap(); + let diff = blueprint3.diff_since_blueprint(&blueprint2); println!("expecting new NTP and Crucible zones:\n{}", diff.display()); // No sleds were changed or removed. - assert_eq!(diff.sleds_modified().count(), 0); - assert_eq!(diff.sleds_removed().len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); // One sled was added. - let sleds: Vec<_> = diff.sleds_added().collect(); - assert_eq!(sleds.len(), 1); - let (sled_id, new_sled_zones) = sleds[0]; - assert_eq!(sled_id, new_sled_id); + assert_eq!(diff.sleds_added.len(), 1); + let sled_id = diff.sleds_added.first().unwrap(); + let new_sled_zones = diff.zones.added.get(sled_id).unwrap(); + assert_eq!(*sled_id, new_sled_id); // The generation number should be newer than the initial default. - assert!(new_sled_zones.generation > Generation::new()); + assert!(new_sled_zones.generation_after.unwrap() > Generation::new()); // All zones' underlay addresses ought to be on the sled's subnet. for z in &new_sled_zones.zones { assert!(new_sled_resources .subnet .net() - .contains(z.underlay_address)); + .contains(z.underlay_address())); } // Check for an NTP zone. Its sockaddr's IP should also be on the // sled's subnet. assert!(new_sled_zones.zones.iter().any(|z| { - if let BlueprintZoneType::InternalNtp( - blueprint_zone_type::InternalNtp { address, .. }, - ) = &z.zone_type + if let BlueprintOrCollectionZoneConfig::Blueprint( + BlueprintZoneConfig { + zone_type: + BlueprintZoneType::InternalNtp( + blueprint_zone_type::InternalNtp { + address, .. + }, + ), + .. + }, + ) = &z { assert!(new_sled_resources .subnet @@ -1404,9 +1420,18 @@ pub mod test { .zones .iter() .filter_map(|z| { - if let BlueprintZoneType::Crucible( - blueprint_zone_type::Crucible { address, dataset }, - ) = &z.zone_type + if let BlueprintOrCollectionZoneConfig::Blueprint( + BlueprintZoneConfig { + zone_type: + BlueprintZoneType::Crucible( + blueprint_zone_type::Crucible { + address, + dataset, + }, + ), + .. + }, + ) = &z { let ip = address.ip(); assert!(new_sled_resources.subnet.net().contains(*ip)); diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs b/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs index a2e577f80c..68e2b9c2a2 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/zones.rs @@ -346,16 +346,6 @@ mod tests { .expect("new zone ID should be present"); } - // Also call change_sled_zones without making any changes. This - // currently bumps the generation number, but in the future might - // become smarter and not do so (in which case this test will break). - let control_sled_id = example - .input - .all_sled_ids(SledFilter::Commissioned) - .nth(2) - .expect("at least 2 sleds present"); - _ = builder.zones.change_sled_zones(control_sled_id); - // Attempt to expunge the newly added Oximeter zone. This should fail // because we only support expunging zones that are unchanged from the // parent blueprint. @@ -376,52 +366,44 @@ mod tests { // above are present. let blueprint = builder.build(); verify_blueprint(&blueprint); - let diff = blueprint.diff_since_blueprint(&blueprint_initial).unwrap(); + let diff = blueprint.diff_since_blueprint(&blueprint_initial); println!("expecting new NTP and Oximeter zones:\n{}", diff.display()); // No sleds were removed. - assert_eq!(diff.sleds_removed().len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); // One sled was added. - let sleds: Vec<_> = diff.sleds_added().collect(); - assert_eq!(sleds.len(), 1); - let (sled_id, new_sled_zones) = sleds[0]; - assert_eq!(sled_id, new_sled_id); + assert_eq!(diff.sleds_added.len(), 1); + let sled_id = diff.sleds_added.first().unwrap(); + assert_eq!(*sled_id, new_sled_id); + let new_sled_zones = diff.zones.added.get(sled_id).unwrap(); // The generation number should be newer than the initial default. - assert_eq!(new_sled_zones.generation, Generation::new().next()); + assert_eq!( + new_sled_zones.generation_after.unwrap(), + Generation::new().next() + ); assert_eq!(new_sled_zones.zones.len(), 1); - // Two sleds were modified: existing_sled_id and control_sled_id. - let sleds = diff.sleds_modified(); - assert_eq!(sleds.len(), 2, "2 sleds modified"); - for (sled_id, sled_modified) in sleds { - if sled_id == existing_sled_id { - assert_eq!( - sled_modified.generation_after, - sled_modified.generation_before.next() - ); - assert_eq!(sled_modified.zones_added().len(), 1); - let added_zone = sled_modified.zones_added().next().unwrap(); - assert_eq!(added_zone.id, new_zone_id); - - assert_eq!(sled_modified.zones_removed().len(), 0); - assert_eq!(sled_modified.zones_modified().count(), 1); - let modified_zone = - sled_modified.zones_modified().next().unwrap(); - assert_eq!(modified_zone.zone_before.id(), existing_zone_id); - } else { - assert_eq!(sled_id, control_sled_id); - - // The generation number is bumped, but nothing else. - assert_eq!( - sled_modified.generation_after, - sled_modified.generation_before.next(), - "control sled has generation number bumped" - ); - assert_eq!(sled_modified.zones_added().len(), 0); - assert_eq!(sled_modified.zones_removed().len(), 0); - assert_eq!(sled_modified.zones_modified().count(), 0); - } + // TODO: AJS - See comment above - we don't actually use the control sled anymore + // so the comparison was changed. + // One sled was modified: existing_sled_id + assert_eq!(diff.sleds_modified.len(), 1, "1 sled modified"); + for sled_id in &diff.sleds_modified { + assert_eq!(*sled_id, existing_sled_id); + let added = diff.zones.added.get(sled_id).unwrap(); + assert_eq!( + added.generation_after.unwrap(), + added.generation_before.unwrap().next() + ); + assert_eq!(added.zones.len(), 1); + let added_zone = &added.zones[0]; + assert_eq!(added_zone.id(), new_zone_id); + + assert!(!diff.zones.removed.contains_key(sled_id)); + let modified = diff.zones.modified.get(sled_id).unwrap(); + assert_eq!(modified.zones.len(), 1); + let modified_zone = &modified.zones[0]; + assert_eq!(modified_zone.zone.id(), existing_zone_id); } logctx.cleanup_successful(); diff --git a/nexus/reconfigurator/planning/src/example.rs b/nexus/reconfigurator/planning/src/example.rs index f8748be758..e52fe3fc4b 100644 --- a/nexus/reconfigurator/planning/src/example.rs +++ b/nexus/reconfigurator/planning/src/example.rs @@ -79,6 +79,7 @@ impl ExampleSystem { vec![], ) .unwrap(); + let _ = builder.sled_ensure_disks(sled_id, sled_resources).unwrap(); for pool_name in sled_resources.zpools.keys() { let _ = builder .sled_ensure_zone_crucible(sled_id, *pool_name) diff --git a/nexus/reconfigurator/planning/src/planner.rs b/nexus/reconfigurator/planning/src/planner.rs index 5535b28910..0c7ee8f5cb 100644 --- a/nexus/reconfigurator/planning/src/planner.rs +++ b/nexus/reconfigurator/planning/src/planner.rs @@ -500,10 +500,10 @@ mod test { use expectorate::assert_contents; use nexus_inventory::now_db_precision; use nexus_types::deployment::blueprint_zone_type; + use nexus_types::deployment::BlueprintDiff; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneFilter; use nexus_types::deployment::BlueprintZoneType; - use nexus_types::deployment::DiffSledModified; use nexus_types::external_api::views::SledPolicy; use nexus_types::external_api::views::SledProvisionPolicy; use nexus_types::external_api::views::SledState; @@ -512,8 +512,10 @@ mod test { use omicron_common::disk::DiskIdentity; use omicron_test_utils::dev::test_setup_log; use omicron_uuid_kinds::PhysicalDiskUuid; + use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; - use std::collections::HashMap; + use sled_agent_client::ZoneKind; + use typed_rng::TypedUuidRng; /// Runs through a basic sequence of blueprints for adding a sled #[test] @@ -527,6 +529,8 @@ mod test { let blueprint1 = &example.blueprint; verify_blueprint(blueprint1); + println!("{}", blueprint1.display()); + // Now run the planner. It should do nothing because our initial // system didn't have any issues that the planner currently knows how to // fix. @@ -542,11 +546,17 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint2.diff_since_blueprint(blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(blueprint1); println!("1 -> 2 (expected no changes):\n{}", diff.display()); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); + assert_eq!(diff.zones.added.len(), 0); + assert_eq!(diff.zones.removed.len(), 0); + assert_eq!(diff.zones.modified.len(), 0); + assert_eq!(diff.zones.errors.len(), 0); + assert_eq!(diff.physical_disks.added.len(), 0); + assert_eq!(diff.physical_disks.removed.len(), 0); verify_blueprint(&blueprint2); // Now add a new sled. @@ -568,7 +578,7 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint3.diff_since_blueprint(&blueprint2).unwrap(); + let diff = blueprint3.diff_since_blueprint(&blueprint2); println!( "2 -> 3 (expect new NTP zone on new sled):\n{}", diff.display() @@ -577,20 +587,18 @@ mod test { "tests/output/planner_basic_add_sled_2_3.txt", &diff.display().to_string(), ); - let sleds = diff.sleds_added().collect::>(); - let (sled_id, sled_zones) = sleds[0]; + assert_eq!(diff.sleds_added.len(), 1); + let sled_id = *diff.sleds_added.first().unwrap(); + let sled_zones = diff.zones.added.get(&sled_id).unwrap(); // We have defined elsewhere that the first generation contains no // zones. So the first one with zones must be newer. See // OmicronZonesConfig::INITIAL_GENERATION. - assert!(sled_zones.generation > Generation::new()); + assert!(sled_zones.generation_after.unwrap() > Generation::new()); assert_eq!(sled_id, new_sled_id); assert_eq!(sled_zones.zones.len(), 1); - assert!(matches!( - sled_zones.zones[0].zone_type, - BlueprintZoneType::InternalNtp(_), - )); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert!(matches!(sled_zones.zones[0].kind(), ZoneKind::InternalNtp)); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); verify_blueprint(&blueprint3); // Check that with no change in inventory, the planner makes no changes. @@ -607,11 +615,11 @@ mod test { .with_rng_seed((TEST_NAME, "bp4")) .plan() .expect("failed to plan"); - let diff = blueprint4.diff_since_blueprint(&blueprint3).unwrap(); + let diff = blueprint4.diff_since_blueprint(&blueprint3); println!("3 -> 4 (expected no changes):\n{}", diff.display()); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); verify_blueprint(&blueprint4); // Now update the inventory to have the requested NTP zone. @@ -648,28 +656,30 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint5.diff_since_blueprint(&blueprint3).unwrap(); + let diff = blueprint5.diff_since_blueprint(&blueprint3); println!("3 -> 5 (expect Crucible zones):\n{}", diff.display()); assert_contents( "tests/output/planner_basic_add_sled_3_5.txt", &diff.display().to_string(), ); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - let sleds = diff.sleds_modified().collect::>(); - assert_eq!(sleds.len(), 1); - let (sled_id, sled_changes) = &sleds[0]; + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 1); + let sled_id = diff.sleds_modified.first().unwrap(); + assert_eq!(*sled_id, new_sled_id); + // No removed or modified zones on this sled + assert!(!diff.zones.removed.contains_key(sled_id)); + assert!(!diff.zones.modified.contains_key(sled_id)); + // 10 crucible zones addeed + let zones_added = diff.zones.added.get(sled_id).unwrap(); assert_eq!( - sled_changes.generation_after, - sled_changes.generation_before.next() + zones_added.generation_after.unwrap(), + zones_added.generation_before.unwrap().next() ); - assert_eq!(*sled_id, new_sled_id); - assert_eq!(sled_changes.zones_removed().len(), 0); - assert_eq!(sled_changes.zones_modified().count(), 0); - let zones = sled_changes.zones_added().collect::>(); - assert_eq!(zones.len(), 10); - for zone in &zones { - if !zone.zone_type.is_crucible() { + + assert_eq!(zones_added.zones.len(), 10); + for zone in &zones_added.zones { + if zone.kind() != ZoneKind::Crucible { panic!("unexpectedly added a non-Crucible zone: {zone:?}"); } } @@ -688,11 +698,11 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint6.diff_since_blueprint(&blueprint5).unwrap(); + let diff = blueprint6.diff_since_blueprint(&blueprint5); println!("5 -> 6 (expect no changes):\n{}", diff.display()); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - assert_eq!(diff.sleds_modified().count(), 0); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); verify_blueprint(&blueprint6); logctx.cleanup_successful(); @@ -722,6 +732,7 @@ mod test { assert_eq!(collection.sled_agents.len(), 1); assert_eq!(collection.omicron_zones.len(), 1); blueprint.blueprint_zones.retain(|k, _v| keep_sled_id == *k); + blueprint.blueprint_disks.retain(|k, _v| keep_sled_id == *k); (keep_sled_id, blueprint, collection, builder.build()) }; @@ -758,21 +769,24 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!("1 -> 2 (added additional Nexus zones):\n{}", diff.display()); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - let mut sleds = diff.sleds_modified().collect::>(); - assert_eq!(sleds.len(), 1); - let (changed_sled_id, sled_changes) = sleds.pop().unwrap(); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 1); + let changed_sled_id = diff.sleds_modified.first().unwrap(); + // TODO-cleanup use `TypedUuid` everywhere - assert_eq!(changed_sled_id, sled_id); - assert_eq!(sled_changes.zones_removed().len(), 0); - assert_eq!(sled_changes.zones_modified().count(), 0); - let zones = sled_changes.zones_added().collect::>(); - assert_eq!(zones.len(), input.target_nexus_zone_count() - 1); - for zone in &zones { - if !zone.zone_type.is_nexus() { + assert_eq!(*changed_sled_id, sled_id); + assert_eq!(diff.zones.removed.len(), 0); + assert_eq!(diff.zones.modified.len(), 0); + let zones_added = diff.zones.added.get(changed_sled_id).unwrap(); + assert_eq!( + zones_added.zones.len(), + input.target_nexus_zone_count() - 1 + ); + for zone in &zones_added.zones { + if zone.kind() != ZoneKind::Nexus { panic!("unexpectedly added a non-Nexus zone: {zone:?}"); } } @@ -821,22 +835,21 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!("1 -> 2 (added additional Nexus zones):\n{}", diff.display()); - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - let sleds = diff.sleds_modified().collect::>(); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 3); // All 3 sleds should get additional Nexus zones. We expect a total of // 11 new Nexus zones, which should be spread evenly across the three // sleds (two should get 4 and one should get 3). - assert_eq!(sleds.len(), 3); let mut total_new_nexus_zones = 0; - for (sled_id, sled_changes) in sleds { - assert_eq!(sled_changes.zones_removed().len(), 0); - assert_eq!(sled_changes.zones_modified().count(), 0); - let zones = sled_changes.zones_added().collect::>(); - match zones.len() { + for sled_id in diff.sleds_modified { + assert!(!diff.zones.removed.contains_key(&sled_id)); + assert!(!diff.zones.modified.contains_key(&sled_id)); + let zones_added = &diff.zones.added.get(&sled_id).unwrap().zones; + match zones_added.len() { n @ (3 | 4) => { total_new_nexus_zones += n; } @@ -844,8 +857,8 @@ mod test { panic!("unexpected number of zones added to {sled_id}: {n}") } } - for zone in &zones { - if !zone.zone_type.is_nexus() { + for zone in zones_added { + if zone.kind() != ZoneKind::Nexus { panic!("unexpectedly added a non-Nexus zone: {zone:?}"); } } @@ -871,13 +884,16 @@ mod test { // one. builder.policy_mut().target_nexus_zone_count = 1; - let new_sled_disk = |policy| nexus_types::deployment::SledDisk { + // Make generated disk ids deterministic + let mut disk_rng = + TypedUuidRng::from_seed(TEST_NAME, "NewPhysicalDisks"); + let mut new_sled_disk = |policy| nexus_types::deployment::SledDisk { disk_identity: DiskIdentity { vendor: "test-vendor".to_string(), serial: "test-serial".to_string(), model: "test-model".to_string(), }, - disk_id: PhysicalDiskUuid::new_v4(), + disk_id: PhysicalDiskUuid::from(disk_rng.next()), policy, state: nexus_types::external_api::views::PhysicalDiskState::Active, }; @@ -892,15 +908,16 @@ mod test { const NEW_IN_SERVICE_DISKS: usize = 2; const NEW_EXPUNGED_DISKS: usize = 1; + let mut zpool_rng = TypedUuidRng::from_seed(TEST_NAME, "NewZpools"); for _ in 0..NEW_IN_SERVICE_DISKS { sled_details.resources.zpools.insert( - ZpoolUuid::new_v4(), + ZpoolUuid::from(zpool_rng.next()), new_sled_disk(nexus_types::external_api::views::PhysicalDiskPolicy::InService), ); } for _ in 0..NEW_EXPUNGED_DISKS { sled_details.resources.zpools.insert( - ZpoolUuid::new_v4(), + ZpoolUuid::from(zpool_rng.next()), new_sled_disk(nexus_types::external_api::views::PhysicalDiskPolicy::Expunged), ); } @@ -919,15 +936,17 @@ mod test { .plan() .expect("failed to plan"); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!("1 -> 2 (some new disks, one expunged):\n{}", diff.display()); - let mut modified_sleds = diff.sleds_modified(); - assert_eq!(modified_sleds.len(), 1); - let (_, diff_modified) = modified_sleds.next().unwrap(); + assert_eq!(diff.sleds_modified.len(), 1); + let sled_id = diff.sleds_modified.first().unwrap(); // We should be adding a Crucible zone for each new in-service disk. - assert_eq!(diff_modified.zones_added().count(), NEW_IN_SERVICE_DISKS); - assert_eq!(diff_modified.zones_removed().len(), 0); + assert_eq!( + diff.zones.added.get(sled_id).unwrap().zones.len(), + NEW_IN_SERVICE_DISKS + ); + assert!(!diff.zones.removed.contains_key(sled_id)); logctx.cleanup_successful(); } @@ -1031,7 +1050,7 @@ mod test { &blueprint2.display().to_string(), ); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!( "1 -> 2 (added additional Nexus zones, take 2 sleds out of service):\n{}", diff.display() @@ -1049,28 +1068,30 @@ mod test { // cleanup, and we aren't performing garbage collection on zones or // sleds at the moment. - assert_eq!(diff.sleds_added().len(), 0); - assert_eq!(diff.sleds_removed().len(), 0); - let mut sleds = diff.sleds_modified().collect::>(); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); - let expunged_modified = sleds.remove(&expunged_sled_id).unwrap(); - assert_all_zones_expunged(&expunged_modified, "expunged sled"); + assert_all_zones_expunged(&diff, expunged_sled_id, "expunged sled"); // Only 2 of the 3 remaining sleds (not the non-provisionable sled) // should get additional Nexus zones. We expect a total of 6 new Nexus // zones, which should be split evenly between the two sleds, while the // non-provisionable sled should be unchanged. - assert_eq!(sleds.len(), 2); + let mut remaining_modified_sleds = diff.sleds_modified.clone(); + remaining_modified_sleds.remove(&expunged_sled_id); + remaining_modified_sleds.remove(&decommissioned_sled_id); + + assert_eq!(remaining_modified_sleds.len(), 2); let mut total_new_nexus_zones = 0; - for (sled_id, sled_changes) in sleds { + for sled_id in remaining_modified_sleds { assert!(sled_id != nonprovisionable_sled_id); assert!(sled_id != expunged_sled_id); assert!(sled_id != decommissioned_sled_id); - assert_eq!(sled_changes.zones_removed().len(), 0); - assert_eq!(sled_changes.zones_modified().count(), 0); - let zones = sled_changes.zones_added().collect::>(); - for zone in &zones { - let BlueprintZoneType::Nexus(_) = zone.zone_type else { + assert!(!diff.zones.removed.contains_key(&sled_id)); + assert!(!diff.zones.modified.contains_key(&sled_id)); + let zones = &diff.zones.added.get(&sled_id).unwrap().zones; + for zone in zones { + if ZoneKind::Nexus != zone.kind() { panic!("unexpectedly added a non-Crucible zone: {zone:?}"); }; } @@ -1161,7 +1182,7 @@ mod test { blueprint2a.external_dns_version = blueprint2a.external_dns_version.next(); - let diff = blueprint2a.diff_since_blueprint(&blueprint2).unwrap(); + let diff = blueprint2a.diff_since_blueprint(&blueprint2); println!("2 -> 2a (manually modified zones):\n{}", diff.display()); assert_contents( "tests/output/planner_nonprovisionable_2_2a.txt", @@ -1173,16 +1194,13 @@ mod test { logctx.cleanup_successful(); } - fn assert_all_zones_expunged(modified: &DiffSledModified, desc: &str) { - assert_eq!( - modified.generation_before.next(), - modified.generation_after, - "for {desc}, generation should have been bumped" - ); - - assert_eq!( - modified.zones_added().count(), - 0, + fn assert_all_zones_expunged( + diff: &BlueprintDiff, + expunged_sled_id: SledUuid, + desc: &str, + ) { + assert!( + !diff.zones.added.contains_key(&expunged_sled_id), "for {desc}, no zones should have been added to blueprint" ); @@ -1191,20 +1209,27 @@ mod test { // zone removal will be part of some future garbage collection // process that isn't currently defined. - assert_eq!( - modified.zones_removed().len(), - 0, + assert!( + !diff.zones.removed.contains_key(&expunged_sled_id), "for {desc}, no zones should have been removed from blueprint" ); // Run through all the common zones and ensure that all of them // have been marked expunged. - for zone in modified.zones_modified() { + let modified_zones = + diff.zones.modified.get(&expunged_sled_id).unwrap(); + assert_eq!( + modified_zones.generation_before.next(), + modified_zones.generation_after, + "for {desc}, generation should have been bumped" + ); + + for modified_zone in &modified_zones.zones { assert_eq!( - zone.zone_after.disposition, + modified_zone.zone.disposition(), BlueprintZoneDisposition::Expunged, "for {desc}, zone {} should have been marked expunged", - zone.zone_after.id + modified_zone.zone.id() ); } } @@ -1248,7 +1273,7 @@ mod test { "tests/output/planner_decommissions_sleds_bp2.txt", &blueprint2.display().to_string(), ); - let diff = blueprint2.diff_since_blueprint(&blueprint1).unwrap(); + let diff = blueprint2.diff_since_blueprint(&blueprint1); println!("1 -> 2 (expunged {expunged_sled_id}):\n{}", diff.display()); assert_contents( "tests/output/planner_decommissions_sleds_1_2.txt", @@ -1285,15 +1310,15 @@ mod test { // collect zones, so we should still have the sled's expunged zones // (even though the sled itself is no longer present in the list of // commissioned sleds). - let diff = blueprint3.diff_since_blueprint(&blueprint2).unwrap(); + let diff = blueprint3.diff_since_blueprint(&blueprint2); println!( "2 -> 3 (decommissioned {expunged_sled_id}):\n{}", diff.display() ); - assert_eq!(diff.sleds_added().count(), 0); - assert_eq!(diff.sleds_removed().count(), 0); - assert_eq!(diff.sleds_modified().count(), 0); - assert_eq!(diff.sleds_unchanged().count(), DEFAULT_N_SLEDS); + assert_eq!(diff.sleds_added.len(), 0); + assert_eq!(diff.sleds_removed.len(), 0); + assert_eq!(diff.sleds_modified.len(), 0); + assert_eq!(diff.sleds_unchanged.len(), DEFAULT_N_SLEDS); logctx.cleanup_successful(); } diff --git a/nexus/reconfigurator/planning/src/system.rs b/nexus/reconfigurator/planning/src/system.rs index 15aefb7344..e28b96dda5 100644 --- a/nexus/reconfigurator/planning/src/system.rs +++ b/nexus/reconfigurator/planning/src/system.rs @@ -440,13 +440,16 @@ impl Sled { hardware_slot: u16, nzpools: u8, ) -> Sled { + use typed_rng::TypedUuidRng; let unique = unique.unwrap_or_else(|| hardware_slot.to_string()); let model = format!("model{}", unique); let serial = format!("serial{}", unique); let revision = 0; + let mut zpool_rng = + TypedUuidRng::from_seed("SystemSimultatedSled", "ZpoolUuid"); let zpools: BTreeMap<_, _> = (0..nzpools) .map(|_| { - let zpool = ZpoolUuid::new_v4(); + let zpool = ZpoolUuid::from(zpool_rng.next()); let disk = SledDisk { disk_identity: DiskIdentity { vendor: String::from("fake-vendor"), diff --git a/nexus/reconfigurator/planning/tests/output/blueprint_builder_initial_diff.txt b/nexus/reconfigurator/planning/tests/output/blueprint_builder_initial_diff.txt index b421b8f383..8bce7cec98 100644 --- a/nexus/reconfigurator/planning/tests/output/blueprint_builder_initial_diff.txt +++ b/nexus/reconfigurator/planning/tests/output/blueprint_builder_initial_diff.txt @@ -1,54 +1,116 @@ from: collection 094d362b-7d79-49e7-a244-134276cca8fe to: blueprint e4aeb3b3-272f-4967-be34-2d34daa46aa1 + UNCHANGED SLEDS: + + sled 08c7046b-c9c4-4368-881f-19a72df22143: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 44afce85-3377-4b20-a398-517c1579df4d in service fd00:1122:3344:103::23 + crucible 4644ea0c-0ec3-41be-a356-660308e1c3fc in service fd00:1122:3344:103::2c + crucible 55f4d117-0b9d-4256-a2c0-f46d3ed5fff9 in service fd00:1122:3344:103::25 + crucible 5c6a4628-8831-483b-995f-79b9126c4d04 in service fd00:1122:3344:103::28 + crucible 6a01210c-45ed-41a5-9230-8e05ecf5dd8f in service fd00:1122:3344:103::29 + crucible 7004cab9-dfc0-43ba-92d3-58d4ced66025 in service fd00:1122:3344:103::24 + crucible 79552859-fbd3-43bb-a9d3-6baba25558f8 in service fd00:1122:3344:103::26 + crucible 90696819-9b53-485a-9c65-ca63602e843e in service fd00:1122:3344:103::27 + crucible c99525b3-3680-4df6-9214-2ee3e1020e8b in service fd00:1122:3344:103::2a + crucible f42959d3-9eef-4e3b-b404-6177ce3ec7a1 in service fd00:1122:3344:103::2b + internal_ntp c81c9d4a-36d7-4796-9151-f564d3735152 in service fd00:1122:3344:103::21 + nexus b2573120-9c91-4ed7-8b4f-a7bfe8dbc807 in service fd00:1122:3344:103::22 + + + sled 84ac367e-9b03-4e9d-a846-df1a08deee6c: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0faa9350-2c02-47c7-a0a6-9f4afd69152c in service fd00:1122:3344:101::2c + crucible 5b44003e-1a3d-4152-b606-872c72efce0e in service fd00:1122:3344:101::25 + crucible 943fea7a-9458-4935-9dc7-01ee5cfe5a02 in service fd00:1122:3344:101::29 + crucible 95c3b6d1-2592-4252-b5c1-5d0faf3ce9c9 in service fd00:1122:3344:101::24 + crucible a5a0b7a9-37c9-4dbd-8393-ec7748ada3b0 in service fd00:1122:3344:101::2b + crucible a9a6a974-8953-4783-b815-da46884f2c02 in service fd00:1122:3344:101::23 + crucible aa25add8-60b0-4ace-ac60-15adcdd32d50 in service fd00:1122:3344:101::2a + crucible b6f2dd1e-7f98-4a68-9df2-b33c69d1f7ea in service fd00:1122:3344:101::27 + crucible dc22d470-dc46-436b-9750-25c8d7d369e2 in service fd00:1122:3344:101::26 + crucible f7e434f9-6d4a-476b-a9e2-48d6ee28a08e in service fd00:1122:3344:101::28 + internal_ntp 38b047ea-e3de-4859-b8e0-70cac5871446 in service fd00:1122:3344:101::21 + nexus fb36b9dc-273a-4bc3-aaa9-19ee4d0ef552 in service fd00:1122:3344:101::22 + + + sled be7f4375-2a6b-457f-b1a4-3074a715e5fe: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 248db330-56e6-4c7e-b5ff-9cd6cbcb210a in service fd00:1122:3344:102::2c + crucible 353b0aff-4c71-4fae-a6bd-adcb1d2a1a1d in service fd00:1122:3344:102::29 + crucible 4330134c-41b9-4097-aa0b-3eaefa06d473 in service fd00:1122:3344:102::24 + crucible 65d03287-e43f-45f4-902e-0a5e4638f31a in service fd00:1122:3344:102::25 + crucible 6a5901b1-f9d7-425c-8ecb-a786c900f217 in service fd00:1122:3344:102::27 + crucible 9b722fea-a186-4bc3-bc37-ce7f6de6a796 in service fd00:1122:3344:102::23 + crucible b3583b5f-4a62-4471-9be7-41e61578de4c in service fd00:1122:3344:102::2a + crucible bac92034-b9e6-4e8b-9ffb-dbba9caec88d in service fd00:1122:3344:102::28 + crucible d9653001-f671-4905-a410-6a7abc358318 in service fd00:1122:3344:102::2b + crucible edaca77e-5806-446a-b00c-125962cd551d in service fd00:1122:3344:102::26 + internal_ntp aac3ab51-9e2b-4605-9bf6-e3eb3681c2b5 in service fd00:1122:3344:102::21 + nexus 29278a22-1ba1-4117-bfdb-39fcb9ae7fd1 in service fd00:1122:3344:102::22 + + + METADATA: ++ internal DNS version: (not present in collection) -> 1 ++ external DNS version: (not present in collection) -> 1 - ------------------------------------------------------------------------------------------------------ - zone type zone ID disposition underlay IP status - ------------------------------------------------------------------------------------------------------ - - UNCHANGED SLEDS: - - sled 08c7046b-c9c4-4368-881f-19a72df22143: blueprint zones at generation 2 - crucible 44afce85-3377-4b20-a398-517c1579df4d in service fd00:1122:3344:103::23 - crucible 4644ea0c-0ec3-41be-a356-660308e1c3fc in service fd00:1122:3344:103::2c - crucible 55f4d117-0b9d-4256-a2c0-f46d3ed5fff9 in service fd00:1122:3344:103::25 - crucible 5c6a4628-8831-483b-995f-79b9126c4d04 in service fd00:1122:3344:103::28 - crucible 6a01210c-45ed-41a5-9230-8e05ecf5dd8f in service fd00:1122:3344:103::29 - crucible 7004cab9-dfc0-43ba-92d3-58d4ced66025 in service fd00:1122:3344:103::24 - crucible 79552859-fbd3-43bb-a9d3-6baba25558f8 in service fd00:1122:3344:103::26 - crucible 90696819-9b53-485a-9c65-ca63602e843e in service fd00:1122:3344:103::27 - crucible c99525b3-3680-4df6-9214-2ee3e1020e8b in service fd00:1122:3344:103::2a - crucible f42959d3-9eef-4e3b-b404-6177ce3ec7a1 in service fd00:1122:3344:103::2b - internal_ntp c81c9d4a-36d7-4796-9151-f564d3735152 in service fd00:1122:3344:103::21 - nexus b2573120-9c91-4ed7-8b4f-a7bfe8dbc807 in service fd00:1122:3344:103::22 - - sled 84ac367e-9b03-4e9d-a846-df1a08deee6c: blueprint zones at generation 2 - crucible 0faa9350-2c02-47c7-a0a6-9f4afd69152c in service fd00:1122:3344:101::2c - crucible 5b44003e-1a3d-4152-b606-872c72efce0e in service fd00:1122:3344:101::25 - crucible 943fea7a-9458-4935-9dc7-01ee5cfe5a02 in service fd00:1122:3344:101::29 - crucible 95c3b6d1-2592-4252-b5c1-5d0faf3ce9c9 in service fd00:1122:3344:101::24 - crucible a5a0b7a9-37c9-4dbd-8393-ec7748ada3b0 in service fd00:1122:3344:101::2b - crucible a9a6a974-8953-4783-b815-da46884f2c02 in service fd00:1122:3344:101::23 - crucible aa25add8-60b0-4ace-ac60-15adcdd32d50 in service fd00:1122:3344:101::2a - crucible b6f2dd1e-7f98-4a68-9df2-b33c69d1f7ea in service fd00:1122:3344:101::27 - crucible dc22d470-dc46-436b-9750-25c8d7d369e2 in service fd00:1122:3344:101::26 - crucible f7e434f9-6d4a-476b-a9e2-48d6ee28a08e in service fd00:1122:3344:101::28 - internal_ntp 38b047ea-e3de-4859-b8e0-70cac5871446 in service fd00:1122:3344:101::21 - nexus fb36b9dc-273a-4bc3-aaa9-19ee4d0ef552 in service fd00:1122:3344:101::22 - - sled be7f4375-2a6b-457f-b1a4-3074a715e5fe: blueprint zones at generation 2 - crucible 248db330-56e6-4c7e-b5ff-9cd6cbcb210a in service fd00:1122:3344:102::2c - crucible 353b0aff-4c71-4fae-a6bd-adcb1d2a1a1d in service fd00:1122:3344:102::29 - crucible 4330134c-41b9-4097-aa0b-3eaefa06d473 in service fd00:1122:3344:102::24 - crucible 65d03287-e43f-45f4-902e-0a5e4638f31a in service fd00:1122:3344:102::25 - crucible 6a5901b1-f9d7-425c-8ecb-a786c900f217 in service fd00:1122:3344:102::27 - crucible 9b722fea-a186-4bc3-bc37-ce7f6de6a796 in service fd00:1122:3344:102::23 - crucible b3583b5f-4a62-4471-9be7-41e61578de4c in service fd00:1122:3344:102::2a - crucible bac92034-b9e6-4e8b-9ffb-dbba9caec88d in service fd00:1122:3344:102::28 - crucible d9653001-f671-4905-a410-6a7abc358318 in service fd00:1122:3344:102::2b - crucible edaca77e-5806-446a-b00c-125962cd551d in service fd00:1122:3344:102::26 - internal_ntp aac3ab51-9e2b-4605-9bf6-e3eb3681c2b5 in service fd00:1122:3344:102::21 - nexus 29278a22-1ba1-4117-bfdb-39fcb9ae7fd1 in service fd00:1122:3344:102::22 - - METADATA: -+ internal DNS version: (not present in collection) -> 1 -+ external DNS version: (not present in collection) -> 1 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 b135303ead..5b72615bd7 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 @@ -1,59 +1,144 @@ from: blueprint 979ef428-0bdd-4622-8a72-0719e942b415 to: blueprint 4171ad05-89dd-474b-846b-b007e4346366 - ------------------------------------------------------------------------------------------------------ - zone type zone ID disposition underlay IP status - ------------------------------------------------------------------------------------------------------ - - UNCHANGED SLEDS: - - sled 41f45d9f-766e-4ca6-a881-61ee45c80f57: blueprint zones at generation 2 - crucible 322ee9f1-8903-4542-a0a8-a54cefabdeca in service fd00:1122:3344:103::24 - crucible 4ab1650f-32c5-447f-939d-64b8103a7645 in service fd00:1122:3344:103::2a - crucible 64aa65f8-1ccb-4cd6-9953-027aebdac8ff in service fd00:1122:3344:103::27 - crucible 6e811d86-8aa7-4660-935b-84b4b7721b10 in service fd00:1122:3344:103::2b - crucible 747d2426-68bf-4c22-8806-41d290b5d5f5 in service fd00:1122:3344:103::25 - crucible 7fbd2c38-5dc3-48c4-b061-558a2041d70f in service fd00:1122:3344:103::2c - crucible 8e9e923e-62b1-4cbc-9f59-d6397e338b6b in service fd00:1122:3344:103::29 - crucible b14d5478-1a0e-4b90-b526-36b06339dfc4 in service fd00:1122:3344:103::28 - crucible b40f7c7b-526c-46c8-ae33-67280c280eb7 in service fd00:1122:3344:103::23 - crucible be97b92b-38d6-422a-8c76-d37060f75bd2 in service fd00:1122:3344:103::26 - internal_ntp 267ed614-92af-4b9d-bdba-c2881c2e43a2 in service fd00:1122:3344:103::21 - nexus cc816cfe-3869-4dde-b596-397d41198628 in service fd00:1122:3344:103::22 - - sled 43677374-8d2f-4deb-8a41-eeea506db8e0: blueprint zones at generation 2 - crucible 02acbe6a-1c88-47e3-94c3-94084cbde098 in service fd00:1122:3344:101::27 - crucible 07c3c805-8888-4fe5-9543-3d2479dbe6f3 in service fd00:1122:3344:101::26 - crucible 10d98a73-ec88-4aff-a7e8-7db6a87880e6 in service fd00:1122:3344:101::24 - crucible 2a455c35-eb3c-4c73-ab6c-d0a706e25316 in service fd00:1122:3344:101::29 - crucible 3eda924f-22a9-4f3e-9a1b-91d1c47601ab in service fd00:1122:3344:101::23 - crucible 587be699-a320-4c79-b320-128d9ecddc0b in service fd00:1122:3344:101::2b - crucible 6fa06115-4959-4913-8e7b-dd70d7651f07 in service fd00:1122:3344:101::2c - crucible 8f3a1cc5-9195-4a30-ad02-b804278fe639 in service fd00:1122:3344:101::28 - crucible a1696cd4-588c-484a-b95b-66e824c0ce05 in service fd00:1122:3344:101::25 - crucible a2079cbc-a69e-41a1-b1e0-fbcb972d03f6 in service fd00:1122:3344:101::2a - internal_ntp 08c7f8aa-1ea9-469b-8cac-2fdbfc11ebcb in service fd00:1122:3344:101::21 - nexus c66ab6d5-ff7a-46d1-9fd0-70cefa352d25 in service fd00:1122:3344:101::22 - - sled 590e3034-d946-4166-b0e5-2d0034197a07: blueprint zones at generation 2 - crucible 18f8fe40-646e-4962-b17a-20e201f3a6e5 in service fd00:1122:3344:102::2a - crucible 56d5d7cf-db2c-40a3-a775-003241ad4820 in service fd00:1122:3344:102::29 - crucible 6af7f4d6-33b6-4eb3-a146-d8e9e4ae9d66 in service fd00:1122:3344:102::2b - crucible 7a9f60d3-2b66-4547-9b63-7d4f7a8b6382 in service fd00:1122:3344:102::26 - crucible 93f2f40c-5616-4d8d-8519-ec6debdcede0 in service fd00:1122:3344:102::2c - crucible ab7ba6df-d401-40bd-940e-faf57c57aa2a in service fd00:1122:3344:102::28 - crucible af322036-371f-437c-8c08-7f40f3f1403b in service fd00:1122:3344:102::23 - crucible d637264f-6f40-44c2-8b7e-a179430210d2 in service fd00:1122:3344:102::25 - crucible dce226c9-7373-4bfa-8a94-79dc472857a6 in service fd00:1122:3344:102::27 - crucible edabedf3-839c-488d-ad6f-508ffa864674 in service fd00:1122:3344:102::24 - internal_ntp 47199d48-534c-4267-a654-d2d90e64b498 in service fd00:1122:3344:102::21 - nexus 704e1fed-f8d6-4cfa-a470-bad27fdc06d1 in service fd00:1122:3344:102::22 - - ADDED SLEDS: - -+ sled b59ec570-2abb-4017-80ce-129d94e7a025: blueprint zones at generation 2 -+ internal_ntp 2d73d30e-ca47-46a8-9c12-917d4ab824b6 in service fd00:1122:3344:104::21 added - - METADATA: - internal DNS version: 1 (unchanged) - external DNS version: 1 (unchanged) + UNCHANGED SLEDS: + + sled 41f45d9f-766e-4ca6-a881-61ee45c80f57: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 322ee9f1-8903-4542-a0a8-a54cefabdeca in service fd00:1122:3344:103::24 + crucible 4ab1650f-32c5-447f-939d-64b8103a7645 in service fd00:1122:3344:103::2a + crucible 64aa65f8-1ccb-4cd6-9953-027aebdac8ff in service fd00:1122:3344:103::27 + crucible 6e811d86-8aa7-4660-935b-84b4b7721b10 in service fd00:1122:3344:103::2b + crucible 747d2426-68bf-4c22-8806-41d290b5d5f5 in service fd00:1122:3344:103::25 + crucible 7fbd2c38-5dc3-48c4-b061-558a2041d70f in service fd00:1122:3344:103::2c + crucible 8e9e923e-62b1-4cbc-9f59-d6397e338b6b in service fd00:1122:3344:103::29 + crucible b14d5478-1a0e-4b90-b526-36b06339dfc4 in service fd00:1122:3344:103::28 + crucible b40f7c7b-526c-46c8-ae33-67280c280eb7 in service fd00:1122:3344:103::23 + crucible be97b92b-38d6-422a-8c76-d37060f75bd2 in service fd00:1122:3344:103::26 + internal_ntp 267ed614-92af-4b9d-bdba-c2881c2e43a2 in service fd00:1122:3344:103::21 + nexus cc816cfe-3869-4dde-b596-397d41198628 in service fd00:1122:3344:103::22 + + + sled 43677374-8d2f-4deb-8a41-eeea506db8e0: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 02acbe6a-1c88-47e3-94c3-94084cbde098 in service fd00:1122:3344:101::27 + crucible 07c3c805-8888-4fe5-9543-3d2479dbe6f3 in service fd00:1122:3344:101::26 + crucible 10d98a73-ec88-4aff-a7e8-7db6a87880e6 in service fd00:1122:3344:101::24 + crucible 2a455c35-eb3c-4c73-ab6c-d0a706e25316 in service fd00:1122:3344:101::29 + crucible 3eda924f-22a9-4f3e-9a1b-91d1c47601ab in service fd00:1122:3344:101::23 + crucible 587be699-a320-4c79-b320-128d9ecddc0b in service fd00:1122:3344:101::2b + crucible 6fa06115-4959-4913-8e7b-dd70d7651f07 in service fd00:1122:3344:101::2c + crucible 8f3a1cc5-9195-4a30-ad02-b804278fe639 in service fd00:1122:3344:101::28 + crucible a1696cd4-588c-484a-b95b-66e824c0ce05 in service fd00:1122:3344:101::25 + crucible a2079cbc-a69e-41a1-b1e0-fbcb972d03f6 in service fd00:1122:3344:101::2a + internal_ntp 08c7f8aa-1ea9-469b-8cac-2fdbfc11ebcb in service fd00:1122:3344:101::21 + nexus c66ab6d5-ff7a-46d1-9fd0-70cefa352d25 in service fd00:1122:3344:101::22 + + + sled 590e3034-d946-4166-b0e5-2d0034197a07: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 18f8fe40-646e-4962-b17a-20e201f3a6e5 in service fd00:1122:3344:102::2a + crucible 56d5d7cf-db2c-40a3-a775-003241ad4820 in service fd00:1122:3344:102::29 + crucible 6af7f4d6-33b6-4eb3-a146-d8e9e4ae9d66 in service fd00:1122:3344:102::2b + crucible 7a9f60d3-2b66-4547-9b63-7d4f7a8b6382 in service fd00:1122:3344:102::26 + crucible 93f2f40c-5616-4d8d-8519-ec6debdcede0 in service fd00:1122:3344:102::2c + crucible ab7ba6df-d401-40bd-940e-faf57c57aa2a in service fd00:1122:3344:102::28 + crucible af322036-371f-437c-8c08-7f40f3f1403b in service fd00:1122:3344:102::23 + crucible d637264f-6f40-44c2-8b7e-a179430210d2 in service fd00:1122:3344:102::25 + crucible dce226c9-7373-4bfa-8a94-79dc472857a6 in service fd00:1122:3344:102::27 + crucible edabedf3-839c-488d-ad6f-508ffa864674 in service fd00:1122:3344:102::24 + internal_ntp 47199d48-534c-4267-a654-d2d90e64b498 in service fd00:1122:3344:102::21 + nexus 704e1fed-f8d6-4cfa-a470-bad27fdc06d1 in service fd00:1122:3344:102::22 + + + ADDED SLEDS: + + sled b59ec570-2abb-4017-80ce-129d94e7a025: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- ++ fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 ++ fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 ++ fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 ++ fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 ++ fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 ++ fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 ++ fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 ++ fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c ++ fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b ++ fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ ++ internal_ntp 2d73d30e-ca47-46a8-9c12-917d4ab824b6 in service fd00:1122:3344:104::21 + + + METADATA: + internal DNS version: 1 (unchanged) + external DNS version: 1 (unchanged) + 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 89120cf377..468303a56a 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 @@ -1,69 +1,154 @@ from: blueprint 4171ad05-89dd-474b-846b-b007e4346366 to: blueprint f432fcd5-1284-4058-8b4a-9286a3de6163 - ------------------------------------------------------------------------------------------------------ - zone type zone ID disposition underlay IP status - ------------------------------------------------------------------------------------------------------ - - UNCHANGED SLEDS: - - sled 41f45d9f-766e-4ca6-a881-61ee45c80f57: blueprint zones at generation 2 - crucible 322ee9f1-8903-4542-a0a8-a54cefabdeca in service fd00:1122:3344:103::24 - crucible 4ab1650f-32c5-447f-939d-64b8103a7645 in service fd00:1122:3344:103::2a - crucible 64aa65f8-1ccb-4cd6-9953-027aebdac8ff in service fd00:1122:3344:103::27 - crucible 6e811d86-8aa7-4660-935b-84b4b7721b10 in service fd00:1122:3344:103::2b - crucible 747d2426-68bf-4c22-8806-41d290b5d5f5 in service fd00:1122:3344:103::25 - crucible 7fbd2c38-5dc3-48c4-b061-558a2041d70f in service fd00:1122:3344:103::2c - crucible 8e9e923e-62b1-4cbc-9f59-d6397e338b6b in service fd00:1122:3344:103::29 - crucible b14d5478-1a0e-4b90-b526-36b06339dfc4 in service fd00:1122:3344:103::28 - crucible b40f7c7b-526c-46c8-ae33-67280c280eb7 in service fd00:1122:3344:103::23 - crucible be97b92b-38d6-422a-8c76-d37060f75bd2 in service fd00:1122:3344:103::26 - internal_ntp 267ed614-92af-4b9d-bdba-c2881c2e43a2 in service fd00:1122:3344:103::21 - nexus cc816cfe-3869-4dde-b596-397d41198628 in service fd00:1122:3344:103::22 - - sled 43677374-8d2f-4deb-8a41-eeea506db8e0: blueprint zones at generation 2 - crucible 02acbe6a-1c88-47e3-94c3-94084cbde098 in service fd00:1122:3344:101::27 - crucible 07c3c805-8888-4fe5-9543-3d2479dbe6f3 in service fd00:1122:3344:101::26 - crucible 10d98a73-ec88-4aff-a7e8-7db6a87880e6 in service fd00:1122:3344:101::24 - crucible 2a455c35-eb3c-4c73-ab6c-d0a706e25316 in service fd00:1122:3344:101::29 - crucible 3eda924f-22a9-4f3e-9a1b-91d1c47601ab in service fd00:1122:3344:101::23 - crucible 587be699-a320-4c79-b320-128d9ecddc0b in service fd00:1122:3344:101::2b - crucible 6fa06115-4959-4913-8e7b-dd70d7651f07 in service fd00:1122:3344:101::2c - crucible 8f3a1cc5-9195-4a30-ad02-b804278fe639 in service fd00:1122:3344:101::28 - crucible a1696cd4-588c-484a-b95b-66e824c0ce05 in service fd00:1122:3344:101::25 - crucible a2079cbc-a69e-41a1-b1e0-fbcb972d03f6 in service fd00:1122:3344:101::2a - internal_ntp 08c7f8aa-1ea9-469b-8cac-2fdbfc11ebcb in service fd00:1122:3344:101::21 - nexus c66ab6d5-ff7a-46d1-9fd0-70cefa352d25 in service fd00:1122:3344:101::22 - - sled 590e3034-d946-4166-b0e5-2d0034197a07: blueprint zones at generation 2 - crucible 18f8fe40-646e-4962-b17a-20e201f3a6e5 in service fd00:1122:3344:102::2a - crucible 56d5d7cf-db2c-40a3-a775-003241ad4820 in service fd00:1122:3344:102::29 - crucible 6af7f4d6-33b6-4eb3-a146-d8e9e4ae9d66 in service fd00:1122:3344:102::2b - crucible 7a9f60d3-2b66-4547-9b63-7d4f7a8b6382 in service fd00:1122:3344:102::26 - crucible 93f2f40c-5616-4d8d-8519-ec6debdcede0 in service fd00:1122:3344:102::2c - crucible ab7ba6df-d401-40bd-940e-faf57c57aa2a in service fd00:1122:3344:102::28 - crucible af322036-371f-437c-8c08-7f40f3f1403b in service fd00:1122:3344:102::23 - crucible d637264f-6f40-44c2-8b7e-a179430210d2 in service fd00:1122:3344:102::25 - crucible dce226c9-7373-4bfa-8a94-79dc472857a6 in service fd00:1122:3344:102::27 - crucible edabedf3-839c-488d-ad6f-508ffa864674 in service fd00:1122:3344:102::24 - internal_ntp 47199d48-534c-4267-a654-d2d90e64b498 in service fd00:1122:3344:102::21 - nexus 704e1fed-f8d6-4cfa-a470-bad27fdc06d1 in service fd00:1122:3344:102::22 - - MODIFIED SLEDS: - -* sled b59ec570-2abb-4017-80ce-129d94e7a025: blueprint zones at generation: 2 -> 3 - internal_ntp 2d73d30e-ca47-46a8-9c12-917d4ab824b6 in service fd00:1122:3344:104::21 -+ crucible 1a20ee3c-f66e-4fca-ab85-2a248aa3d79d in service fd00:1122:3344:104::2b added -+ crucible 28852beb-d0e5-4cba-9adb-e7f0cd4bb864 in service fd00:1122:3344:104::29 added -+ crucible 45556184-7092-4a3d-873f-637976bb133b in service fd00:1122:3344:104::22 added -+ crucible 8215bf7a-10d6-4f40-aeb7-27a196307c37 in service fd00:1122:3344:104::25 added -+ crucible 9d75abfe-47ab-434a-93dd-af50dc0dddde in service fd00:1122:3344:104::23 added -+ crucible a36d291c-7f68-462f-830e-bc29e5841ce2 in service fd00:1122:3344:104::27 added -+ crucible b3a4d434-aaee-4752-8c99-69d88fbcb8c5 in service fd00:1122:3344:104::2a added -+ crucible cf5b636b-a505-4db6-bc32-baf9f53f4371 in service fd00:1122:3344:104::28 added -+ crucible f6125d45-b9cc-4721-ba60-ed4dbb177e41 in service fd00:1122:3344:104::26 added -+ crucible f86e19d2-9145-41cf-be89-6aaa34a73873 in service fd00:1122:3344:104::24 added - - METADATA: - internal DNS version: 1 (unchanged) - external DNS version: 1 (unchanged) + UNCHANGED SLEDS: + + sled 41f45d9f-766e-4ca6-a881-61ee45c80f57: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 322ee9f1-8903-4542-a0a8-a54cefabdeca in service fd00:1122:3344:103::24 + crucible 4ab1650f-32c5-447f-939d-64b8103a7645 in service fd00:1122:3344:103::2a + crucible 64aa65f8-1ccb-4cd6-9953-027aebdac8ff in service fd00:1122:3344:103::27 + crucible 6e811d86-8aa7-4660-935b-84b4b7721b10 in service fd00:1122:3344:103::2b + crucible 747d2426-68bf-4c22-8806-41d290b5d5f5 in service fd00:1122:3344:103::25 + crucible 7fbd2c38-5dc3-48c4-b061-558a2041d70f in service fd00:1122:3344:103::2c + crucible 8e9e923e-62b1-4cbc-9f59-d6397e338b6b in service fd00:1122:3344:103::29 + crucible b14d5478-1a0e-4b90-b526-36b06339dfc4 in service fd00:1122:3344:103::28 + crucible b40f7c7b-526c-46c8-ae33-67280c280eb7 in service fd00:1122:3344:103::23 + crucible be97b92b-38d6-422a-8c76-d37060f75bd2 in service fd00:1122:3344:103::26 + internal_ntp 267ed614-92af-4b9d-bdba-c2881c2e43a2 in service fd00:1122:3344:103::21 + nexus cc816cfe-3869-4dde-b596-397d41198628 in service fd00:1122:3344:103::22 + + + sled 43677374-8d2f-4deb-8a41-eeea506db8e0: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 02acbe6a-1c88-47e3-94c3-94084cbde098 in service fd00:1122:3344:101::27 + crucible 07c3c805-8888-4fe5-9543-3d2479dbe6f3 in service fd00:1122:3344:101::26 + crucible 10d98a73-ec88-4aff-a7e8-7db6a87880e6 in service fd00:1122:3344:101::24 + crucible 2a455c35-eb3c-4c73-ab6c-d0a706e25316 in service fd00:1122:3344:101::29 + crucible 3eda924f-22a9-4f3e-9a1b-91d1c47601ab in service fd00:1122:3344:101::23 + crucible 587be699-a320-4c79-b320-128d9ecddc0b in service fd00:1122:3344:101::2b + crucible 6fa06115-4959-4913-8e7b-dd70d7651f07 in service fd00:1122:3344:101::2c + crucible 8f3a1cc5-9195-4a30-ad02-b804278fe639 in service fd00:1122:3344:101::28 + crucible a1696cd4-588c-484a-b95b-66e824c0ce05 in service fd00:1122:3344:101::25 + crucible a2079cbc-a69e-41a1-b1e0-fbcb972d03f6 in service fd00:1122:3344:101::2a + internal_ntp 08c7f8aa-1ea9-469b-8cac-2fdbfc11ebcb in service fd00:1122:3344:101::21 + nexus c66ab6d5-ff7a-46d1-9fd0-70cefa352d25 in service fd00:1122:3344:101::22 + + + sled 590e3034-d946-4166-b0e5-2d0034197a07: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 18f8fe40-646e-4962-b17a-20e201f3a6e5 in service fd00:1122:3344:102::2a + crucible 56d5d7cf-db2c-40a3-a775-003241ad4820 in service fd00:1122:3344:102::29 + crucible 6af7f4d6-33b6-4eb3-a146-d8e9e4ae9d66 in service fd00:1122:3344:102::2b + crucible 7a9f60d3-2b66-4547-9b63-7d4f7a8b6382 in service fd00:1122:3344:102::26 + crucible 93f2f40c-5616-4d8d-8519-ec6debdcede0 in service fd00:1122:3344:102::2c + crucible ab7ba6df-d401-40bd-940e-faf57c57aa2a in service fd00:1122:3344:102::28 + crucible af322036-371f-437c-8c08-7f40f3f1403b in service fd00:1122:3344:102::23 + crucible d637264f-6f40-44c2-8b7e-a179430210d2 in service fd00:1122:3344:102::25 + crucible dce226c9-7373-4bfa-8a94-79dc472857a6 in service fd00:1122:3344:102::27 + crucible edabedf3-839c-488d-ad6f-508ffa864674 in service fd00:1122:3344:102::24 + internal_ntp 47199d48-534c-4267-a654-d2d90e64b498 in service fd00:1122:3344:102::21 + nexus 704e1fed-f8d6-4cfa-a470-bad27fdc06d1 in service fd00:1122:3344:102::22 + + + MODIFIED SLEDS: + + sled b59ec570-2abb-4017-80ce-129d94e7a025: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + internal_ntp 2d73d30e-ca47-46a8-9c12-917d4ab824b6 in service fd00:1122:3344:104::21 ++ crucible 1a20ee3c-f66e-4fca-ab85-2a248aa3d79d in service fd00:1122:3344:104::2b ++ crucible 28852beb-d0e5-4cba-9adb-e7f0cd4bb864 in service fd00:1122:3344:104::29 ++ crucible 45556184-7092-4a3d-873f-637976bb133b in service fd00:1122:3344:104::22 ++ crucible 8215bf7a-10d6-4f40-aeb7-27a196307c37 in service fd00:1122:3344:104::25 ++ crucible 9d75abfe-47ab-434a-93dd-af50dc0dddde in service fd00:1122:3344:104::23 ++ crucible a36d291c-7f68-462f-830e-bc29e5841ce2 in service fd00:1122:3344:104::27 ++ crucible b3a4d434-aaee-4752-8c99-69d88fbcb8c5 in service fd00:1122:3344:104::2a ++ crucible cf5b636b-a505-4db6-bc32-baf9f53f4371 in service fd00:1122:3344:104::28 ++ crucible f6125d45-b9cc-4721-ba60-ed4dbb177e41 in service fd00:1122:3344:104::26 ++ crucible f86e19d2-9145-41cf-be89-6aaa34a73873 in service fd00:1122:3344:104::24 + + + METADATA: + internal DNS version: 1 (unchanged) + external DNS version: 1 (unchanged) + diff --git a/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_1_2.txt b/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_1_2.txt index 28f08c9c78..8c94c97188 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_1_2.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_1_2.txt @@ -1,81 +1,120 @@ from: blueprint 516e80a3-b362-4fac-bd3c-4559717120dd to: blueprint 1ac2d88f-27dd-4506-8585-6b2be832528e - -------------------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP status - -------------------------------------------------------------------------------------------------------- - - UNCHANGED SLEDS: - - sled d67ce8f0-a691-4010-b414-420d82e80527: blueprint zones at generation 2 - crucible 15dbaa30-1539-49d6-970d-ba5962960f33 in service fd00:1122:3344:101::27 - crucible 1ec4cc7b-2f00-4d13-8176-3b9815533ae9 in service fd00:1122:3344:101::24 - crucible 2e65b765-5c41-4519-bf4e-e2a68569afc1 in service fd00:1122:3344:101::23 - crucible 3d4143df-e212-4774-9258-7d9b421fac2e in service fd00:1122:3344:101::25 - crucible 5d9d8fa7-8379-470b-90ba-fe84a3c45512 in service fd00:1122:3344:101::2a - crucible 70232a6d-6c9d-4fa6-a34d-9c73d940db33 in service fd00:1122:3344:101::28 - crucible 8567a616-a709-4c8c-a323-4474675dad5c in service fd00:1122:3344:101::2c - crucible 8b0b8623-930a-41af-9f9b-ca28b1b11139 in service fd00:1122:3344:101::29 - crucible cf87d2a3-d323-44a3-a87e-adc4ef6c75f4 in service fd00:1122:3344:101::2b - crucible eac6c0a0-baa5-4490-9cee-65198b7fbd9c in service fd00:1122:3344:101::26 - internal_ntp ad76d200-5675-444b-b19c-684689ff421f in service fd00:1122:3344:101::21 - nexus e9bf2525-5fa0-4c1b-b52d-481225083845 in service fd00:1122:3344:101::22 - - MODIFIED SLEDS: - -* sled a1b477db-b629-48eb-911d-1ccdafca75b9: blueprint zones at generation: 2 -> 3 -- crucible 1e1ed0cc-1adc-410f-943a-d1a3107de619 in service fd00:1122:3344:103::27 modified -+ ├─ expunged fd00:1122:3344:103::27 -* └─ changed: disposition -- crucible 2307bbed-02ba-493b-89e3-46585c74c8fc in service fd00:1122:3344:103::28 modified -+ ├─ expunged fd00:1122:3344:103::28 -* └─ changed: disposition -- crucible 4e36b7ef-5684-4304-b7c3-3c31aaf83d4f in service fd00:1122:3344:103::23 modified -+ ├─ expunged fd00:1122:3344:103::23 -* └─ changed: disposition -- crucible 603e629d-2599-400e-b879-4134d4cc426e in service fd00:1122:3344:103::2c modified -+ ├─ expunged fd00:1122:3344:103::2c -* └─ changed: disposition -- crucible 9179d6dc-387d-424e-8d62-ed59b2c728f6 in service fd00:1122:3344:103::2a modified -+ ├─ expunged fd00:1122:3344:103::2a -* └─ changed: disposition -- crucible c28d7b4b-a259-45ad-945d-f19ca3c6964c in service fd00:1122:3344:103::29 modified -+ ├─ expunged fd00:1122:3344:103::29 -* └─ changed: disposition -- crucible e29998e7-9ed2-46b6-bb70-4118159fe07f in service fd00:1122:3344:103::26 modified -+ ├─ expunged fd00:1122:3344:103::26 -* └─ changed: disposition -- crucible f06e91a1-0c17-4cca-adbc-1c9b67bdb11d in service fd00:1122:3344:103::2b modified -+ ├─ expunged fd00:1122:3344:103::2b -* └─ changed: disposition -- crucible f11f5c60-1ac7-4630-9a3a-a9bc85c75203 in service fd00:1122:3344:103::25 modified -+ ├─ expunged fd00:1122:3344:103::25 -* └─ changed: disposition -- crucible f231e4eb-3fc9-4964-9d71-2c41644852d9 in service fd00:1122:3344:103::24 modified -+ ├─ expunged fd00:1122:3344:103::24 -* └─ changed: disposition -- internal_ntp c62b87b6-b98d-4d22-ba4f-cee4499e2ba8 in service fd00:1122:3344:103::21 modified -+ ├─ expunged fd00:1122:3344:103::21 -* └─ changed: disposition -- nexus 6a70a233-1900-43c0-9c00-aa9d1f7adfbc in service fd00:1122:3344:103::22 modified -+ ├─ expunged fd00:1122:3344:103::22 -* └─ changed: disposition - -* sled fefcf4cf-f7e7-46b3-b629-058526ce440e: blueprint zones at generation: 2 -> 3 - crucible 0e2b035e-1de1-48af-8ac0-5316418e3de1 in service fd00:1122:3344:102::2a - crucible 4f8ce495-21dd-48a1-859c-80d34ce394ed in service fd00:1122:3344:102::23 - crucible 5c78756d-6182-4c27-a507-3419e8dbe76b in service fd00:1122:3344:102::28 - crucible a1ae92ac-e1f1-4654-ab54-5b75ba7c44d6 in service fd00:1122:3344:102::24 - crucible a308d3e1-118c-440a-947a-8b6ab7d833ab in service fd00:1122:3344:102::25 - crucible b7402110-d88f-4ca4-8391-4a2fda6ad271 in service fd00:1122:3344:102::29 - crucible b7ae596e-0c85-40b2-bb47-df9f76db3cca in service fd00:1122:3344:102::2b - crucible c552280f-ba02-4f8d-9049-bd269e6b7845 in service fd00:1122:3344:102::26 - crucible cf13b878-47f1-4ba0-b8c2-9f3e15f2ee87 in service fd00:1122:3344:102::2c - crucible e6d0df1f-9f98-4c5a-9540-8444d1185c7d in service fd00:1122:3344:102::27 - internal_ntp f68846ad-4619-4747-8293-a2b4aeeafc5b in service fd00:1122:3344:102::21 - nexus 99c6401d-9796-4ae1-bf0c-9a097cf21c33 in service fd00:1122:3344:102::22 -+ nexus c8851a11-a4f7-4b21-9281-6182fd15dc8d in service fd00:1122:3344:102::2d added - - METADATA: - internal DNS version: 1 (unchanged) - external DNS version: 1 (unchanged) + UNCHANGED SLEDS: + + sled d67ce8f0-a691-4010-b414-420d82e80527: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 15dbaa30-1539-49d6-970d-ba5962960f33 in service fd00:1122:3344:101::27 + crucible 1ec4cc7b-2f00-4d13-8176-3b9815533ae9 in service fd00:1122:3344:101::24 + crucible 2e65b765-5c41-4519-bf4e-e2a68569afc1 in service fd00:1122:3344:101::23 + crucible 3d4143df-e212-4774-9258-7d9b421fac2e in service fd00:1122:3344:101::25 + crucible 5d9d8fa7-8379-470b-90ba-fe84a3c45512 in service fd00:1122:3344:101::2a + crucible 70232a6d-6c9d-4fa6-a34d-9c73d940db33 in service fd00:1122:3344:101::28 + crucible 8567a616-a709-4c8c-a323-4474675dad5c in service fd00:1122:3344:101::2c + crucible 8b0b8623-930a-41af-9f9b-ca28b1b11139 in service fd00:1122:3344:101::29 + crucible cf87d2a3-d323-44a3-a87e-adc4ef6c75f4 in service fd00:1122:3344:101::2b + crucible eac6c0a0-baa5-4490-9cee-65198b7fbd9c in service fd00:1122:3344:101::26 + internal_ntp ad76d200-5675-444b-b19c-684689ff421f in service fd00:1122:3344:101::21 + nexus e9bf2525-5fa0-4c1b-b52d-481225083845 in service fd00:1122:3344:101::22 + + + MODIFIED SLEDS: + + sled a1b477db-b629-48eb-911d-1ccdafca75b9: + + physical disks from generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- +- fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 +- fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 +- fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 +- fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 +- fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 +- fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 +- fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 +- fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c +- fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b +- fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ----------------------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + ----------------------------------------------------------------------------------------------------- +* crucible 1e1ed0cc-1adc-410f-943a-d1a3107de619 in service -> expunged fd00:1122:3344:103::27 +* crucible 2307bbed-02ba-493b-89e3-46585c74c8fc in service -> expunged fd00:1122:3344:103::28 +* crucible 4e36b7ef-5684-4304-b7c3-3c31aaf83d4f in service -> expunged fd00:1122:3344:103::23 +* crucible 603e629d-2599-400e-b879-4134d4cc426e in service -> expunged fd00:1122:3344:103::2c +* crucible 9179d6dc-387d-424e-8d62-ed59b2c728f6 in service -> expunged fd00:1122:3344:103::2a +* crucible c28d7b4b-a259-45ad-945d-f19ca3c6964c in service -> expunged fd00:1122:3344:103::29 +* crucible e29998e7-9ed2-46b6-bb70-4118159fe07f in service -> expunged fd00:1122:3344:103::26 +* crucible f06e91a1-0c17-4cca-adbc-1c9b67bdb11d in service -> expunged fd00:1122:3344:103::2b +* crucible f11f5c60-1ac7-4630-9a3a-a9bc85c75203 in service -> expunged fd00:1122:3344:103::25 +* crucible f231e4eb-3fc9-4964-9d71-2c41644852d9 in service -> expunged fd00:1122:3344:103::24 +* internal_ntp c62b87b6-b98d-4d22-ba4f-cee4499e2ba8 in service -> expunged fd00:1122:3344:103::21 +* nexus 6a70a233-1900-43c0-9c00-aa9d1f7adfbc in service -> expunged fd00:1122:3344:103::22 + + + sled fefcf4cf-f7e7-46b3-b629-058526ce440e: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0e2b035e-1de1-48af-8ac0-5316418e3de1 in service fd00:1122:3344:102::2a + crucible 4f8ce495-21dd-48a1-859c-80d34ce394ed in service fd00:1122:3344:102::23 + crucible 5c78756d-6182-4c27-a507-3419e8dbe76b in service fd00:1122:3344:102::28 + crucible a1ae92ac-e1f1-4654-ab54-5b75ba7c44d6 in service fd00:1122:3344:102::24 + crucible a308d3e1-118c-440a-947a-8b6ab7d833ab in service fd00:1122:3344:102::25 + crucible b7402110-d88f-4ca4-8391-4a2fda6ad271 in service fd00:1122:3344:102::29 + crucible b7ae596e-0c85-40b2-bb47-df9f76db3cca in service fd00:1122:3344:102::2b + crucible c552280f-ba02-4f8d-9049-bd269e6b7845 in service fd00:1122:3344:102::26 + crucible cf13b878-47f1-4ba0-b8c2-9f3e15f2ee87 in service fd00:1122:3344:102::2c + crucible e6d0df1f-9f98-4c5a-9540-8444d1185c7d in service fd00:1122:3344:102::27 + internal_ntp f68846ad-4619-4747-8293-a2b4aeeafc5b in service fd00:1122:3344:102::21 + nexus 99c6401d-9796-4ae1-bf0c-9a097cf21c33 in service fd00:1122:3344:102::22 ++ nexus c8851a11-a4f7-4b21-9281-6182fd15dc8d in service fd00:1122:3344:102::2d + + + METADATA: + internal DNS version: 1 (unchanged) + external DNS version: 1 (unchanged) + diff --git a/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_bp2.txt b/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_bp2.txt index ca08bd5c33..ec94d5d924 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_bp2.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_decommissions_sleds_bp2.txt @@ -1,56 +1,106 @@ blueprint 1ac2d88f-27dd-4506-8585-6b2be832528e parent: 516e80a3-b362-4fac-bd3c-4559717120dd - -------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP - -------------------------------------------------------------------------------------------- - - sled a1b477db-b629-48eb-911d-1ccdafca75b9: blueprint zones at generation 3 - crucible 1e1ed0cc-1adc-410f-943a-d1a3107de619 expunged fd00:1122:3344:103::27 - crucible 2307bbed-02ba-493b-89e3-46585c74c8fc expunged fd00:1122:3344:103::28 - crucible 4e36b7ef-5684-4304-b7c3-3c31aaf83d4f expunged fd00:1122:3344:103::23 - crucible 603e629d-2599-400e-b879-4134d4cc426e expunged fd00:1122:3344:103::2c - crucible 9179d6dc-387d-424e-8d62-ed59b2c728f6 expunged fd00:1122:3344:103::2a - crucible c28d7b4b-a259-45ad-945d-f19ca3c6964c expunged fd00:1122:3344:103::29 - crucible e29998e7-9ed2-46b6-bb70-4118159fe07f expunged fd00:1122:3344:103::26 - crucible f06e91a1-0c17-4cca-adbc-1c9b67bdb11d expunged fd00:1122:3344:103::2b - crucible f11f5c60-1ac7-4630-9a3a-a9bc85c75203 expunged fd00:1122:3344:103::25 - crucible f231e4eb-3fc9-4964-9d71-2c41644852d9 expunged fd00:1122:3344:103::24 - internal_ntp c62b87b6-b98d-4d22-ba4f-cee4499e2ba8 expunged fd00:1122:3344:103::21 - nexus 6a70a233-1900-43c0-9c00-aa9d1f7adfbc expunged fd00:1122:3344:103::22 - - sled d67ce8f0-a691-4010-b414-420d82e80527: blueprint zones at generation 2 - crucible 15dbaa30-1539-49d6-970d-ba5962960f33 in service fd00:1122:3344:101::27 - crucible 1ec4cc7b-2f00-4d13-8176-3b9815533ae9 in service fd00:1122:3344:101::24 - crucible 2e65b765-5c41-4519-bf4e-e2a68569afc1 in service fd00:1122:3344:101::23 - crucible 3d4143df-e212-4774-9258-7d9b421fac2e in service fd00:1122:3344:101::25 - crucible 5d9d8fa7-8379-470b-90ba-fe84a3c45512 in service fd00:1122:3344:101::2a - crucible 70232a6d-6c9d-4fa6-a34d-9c73d940db33 in service fd00:1122:3344:101::28 - crucible 8567a616-a709-4c8c-a323-4474675dad5c in service fd00:1122:3344:101::2c - crucible 8b0b8623-930a-41af-9f9b-ca28b1b11139 in service fd00:1122:3344:101::29 - crucible cf87d2a3-d323-44a3-a87e-adc4ef6c75f4 in service fd00:1122:3344:101::2b - crucible eac6c0a0-baa5-4490-9cee-65198b7fbd9c in service fd00:1122:3344:101::26 - internal_ntp ad76d200-5675-444b-b19c-684689ff421f in service fd00:1122:3344:101::21 - nexus e9bf2525-5fa0-4c1b-b52d-481225083845 in service fd00:1122:3344:101::22 - - sled fefcf4cf-f7e7-46b3-b629-058526ce440e: blueprint zones at generation 3 - crucible 0e2b035e-1de1-48af-8ac0-5316418e3de1 in service fd00:1122:3344:102::2a - crucible 4f8ce495-21dd-48a1-859c-80d34ce394ed in service fd00:1122:3344:102::23 - crucible 5c78756d-6182-4c27-a507-3419e8dbe76b in service fd00:1122:3344:102::28 - crucible a1ae92ac-e1f1-4654-ab54-5b75ba7c44d6 in service fd00:1122:3344:102::24 - crucible a308d3e1-118c-440a-947a-8b6ab7d833ab in service fd00:1122:3344:102::25 - crucible b7402110-d88f-4ca4-8391-4a2fda6ad271 in service fd00:1122:3344:102::29 - crucible b7ae596e-0c85-40b2-bb47-df9f76db3cca in service fd00:1122:3344:102::2b - crucible c552280f-ba02-4f8d-9049-bd269e6b7845 in service fd00:1122:3344:102::26 - crucible cf13b878-47f1-4ba0-b8c2-9f3e15f2ee87 in service fd00:1122:3344:102::2c - crucible e6d0df1f-9f98-4c5a-9540-8444d1185c7d in service fd00:1122:3344:102::27 - internal_ntp f68846ad-4619-4747-8293-a2b4aeeafc5b in service fd00:1122:3344:102::21 - nexus 99c6401d-9796-4ae1-bf0c-9a097cf21c33 in service fd00:1122:3344:102::22 - nexus c8851a11-a4f7-4b21-9281-6182fd15dc8d in service fd00:1122:3344:102::2d - -METADATA: - created by: test_blueprint2 - created at: 1970-01-01T00:00:00.000Z - comment: sled a1b477db-b629-48eb-911d-1ccdafca75b9 (sled policy is expunged): 12 zones expunged, sled d67ce8f0-a691-4010-b414-420d82e80527: altered disks, sled fefcf4cf-f7e7-46b3-b629-058526ce440e: altered disks - internal DNS version: 1 - external DNS version: 1 + sled: d67ce8f0-a691-4010-b414-420d82e80527 + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 15dbaa30-1539-49d6-970d-ba5962960f33 in service fd00:1122:3344:101::27 + crucible 1ec4cc7b-2f00-4d13-8176-3b9815533ae9 in service fd00:1122:3344:101::24 + crucible 2e65b765-5c41-4519-bf4e-e2a68569afc1 in service fd00:1122:3344:101::23 + crucible 3d4143df-e212-4774-9258-7d9b421fac2e in service fd00:1122:3344:101::25 + crucible 5d9d8fa7-8379-470b-90ba-fe84a3c45512 in service fd00:1122:3344:101::2a + crucible 70232a6d-6c9d-4fa6-a34d-9c73d940db33 in service fd00:1122:3344:101::28 + crucible 8567a616-a709-4c8c-a323-4474675dad5c in service fd00:1122:3344:101::2c + crucible 8b0b8623-930a-41af-9f9b-ca28b1b11139 in service fd00:1122:3344:101::29 + crucible cf87d2a3-d323-44a3-a87e-adc4ef6c75f4 in service fd00:1122:3344:101::2b + crucible eac6c0a0-baa5-4490-9cee-65198b7fbd9c in service fd00:1122:3344:101::26 + internal_ntp ad76d200-5675-444b-b19c-684689ff421f in service fd00:1122:3344:101::21 + nexus e9bf2525-5fa0-4c1b-b52d-481225083845 in service fd00:1122:3344:101::22 + + + + sled: fefcf4cf-f7e7-46b3-b629-058526ce440e + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0e2b035e-1de1-48af-8ac0-5316418e3de1 in service fd00:1122:3344:102::2a + crucible 4f8ce495-21dd-48a1-859c-80d34ce394ed in service fd00:1122:3344:102::23 + crucible 5c78756d-6182-4c27-a507-3419e8dbe76b in service fd00:1122:3344:102::28 + crucible a1ae92ac-e1f1-4654-ab54-5b75ba7c44d6 in service fd00:1122:3344:102::24 + crucible a308d3e1-118c-440a-947a-8b6ab7d833ab in service fd00:1122:3344:102::25 + crucible b7402110-d88f-4ca4-8391-4a2fda6ad271 in service fd00:1122:3344:102::29 + crucible b7ae596e-0c85-40b2-bb47-df9f76db3cca in service fd00:1122:3344:102::2b + crucible c552280f-ba02-4f8d-9049-bd269e6b7845 in service fd00:1122:3344:102::26 + crucible cf13b878-47f1-4ba0-b8c2-9f3e15f2ee87 in service fd00:1122:3344:102::2c + crucible e6d0df1f-9f98-4c5a-9540-8444d1185c7d in service fd00:1122:3344:102::27 + internal_ntp f68846ad-4619-4747-8293-a2b4aeeafc5b in service fd00:1122:3344:102::21 + nexus 99c6401d-9796-4ae1-bf0c-9a097cf21c33 in service fd00:1122:3344:102::22 + nexus c8851a11-a4f7-4b21-9281-6182fd15dc8d in service fd00:1122:3344:102::2d + + + +!a1b477db-b629-48eb-911d-1ccdafca75b9 +WARNING: Zones exist without physical disks! + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 1e1ed0cc-1adc-410f-943a-d1a3107de619 expunged fd00:1122:3344:103::27 + crucible 2307bbed-02ba-493b-89e3-46585c74c8fc expunged fd00:1122:3344:103::28 + crucible 4e36b7ef-5684-4304-b7c3-3c31aaf83d4f expunged fd00:1122:3344:103::23 + crucible 603e629d-2599-400e-b879-4134d4cc426e expunged fd00:1122:3344:103::2c + crucible 9179d6dc-387d-424e-8d62-ed59b2c728f6 expunged fd00:1122:3344:103::2a + crucible c28d7b4b-a259-45ad-945d-f19ca3c6964c expunged fd00:1122:3344:103::29 + crucible e29998e7-9ed2-46b6-bb70-4118159fe07f expunged fd00:1122:3344:103::26 + crucible f06e91a1-0c17-4cca-adbc-1c9b67bdb11d expunged fd00:1122:3344:103::2b + crucible f11f5c60-1ac7-4630-9a3a-a9bc85c75203 expunged fd00:1122:3344:103::25 + crucible f231e4eb-3fc9-4964-9d71-2c41644852d9 expunged fd00:1122:3344:103::24 + internal_ntp c62b87b6-b98d-4d22-ba4f-cee4499e2ba8 expunged fd00:1122:3344:103::21 + nexus 6a70a233-1900-43c0-9c00-aa9d1f7adfbc expunged fd00:1122:3344:103::22 + + + + METADATA: + created by::::::::::: test_blueprint2 + created at::::::::::: 1970-01-01T00:00:00.000Z + comment:::::::::::::: sled a1b477db-b629-48eb-911d-1ccdafca75b9 (sled policy is expunged): 12 zones expunged + internal DNS version: 1 + external DNS version: 1 + diff --git a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_1_2.txt b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_1_2.txt index a87243733f..bc4d2abf71 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_1_2.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_1_2.txt @@ -1,114 +1,197 @@ from: blueprint 4d4e6c38-cd95-4c4e-8f45-6af4d686964b to: blueprint 9f71f5d3-a272-4382-9154-6ea2e171a6c6 - -------------------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP status - -------------------------------------------------------------------------------------------------------- - - UNCHANGED SLEDS: - - sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: blueprint zones at generation 2 - crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service fd00:1122:3344:105::26 - crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c - crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 - crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 - crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 - crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 - crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 - crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a - crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b - crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 - internal_ntp 7f4e9f9f-08f8-4d14-885d-e977c05525ad in service fd00:1122:3344:105::21 - nexus 6dff7633-66bb-4924-a6ff-2c896e66964b in service fd00:1122:3344:105::22 - - sled 68d24ac5-f341-49ea-a92a-0381b52ab387: blueprint zones at generation 2 - crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c - crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 - crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 - crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 - crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 - crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b - crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 - crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 - crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 - crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a - internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 - nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 - - MODIFIED SLEDS: - -* sled 48d95fef-bc9f-4f50-9a53-1e075836291d: blueprint zones at generation: 2 -> 3 -- crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 in service fd00:1122:3344:103::2c modified -+ ├─ expunged fd00:1122:3344:103::2c -* └─ changed: disposition -- crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea in service fd00:1122:3344:103::25 modified -+ ├─ expunged fd00:1122:3344:103::25 -* └─ changed: disposition -- crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f in service fd00:1122:3344:103::27 modified -+ ├─ expunged fd00:1122:3344:103::27 -* └─ changed: disposition -- crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 in service fd00:1122:3344:103::28 modified -+ ├─ expunged fd00:1122:3344:103::28 -* └─ changed: disposition -- crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb in service fd00:1122:3344:103::24 modified -+ ├─ expunged fd00:1122:3344:103::24 -* └─ changed: disposition -- crucible 67622d61-2df4-414d-aa0e-d1277265f405 in service fd00:1122:3344:103::23 modified -+ ├─ expunged fd00:1122:3344:103::23 -* └─ changed: disposition -- crucible b91b271d-8d80-4f49-99a0-34006ae86063 in service fd00:1122:3344:103::2a modified -+ ├─ expunged fd00:1122:3344:103::2a -* └─ changed: disposition -- crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 in service fd00:1122:3344:103::26 modified -+ ├─ expunged fd00:1122:3344:103::26 -* └─ changed: disposition -- crucible e39d7c9e-182b-48af-af87-58079d723583 in service fd00:1122:3344:103::29 modified -+ ├─ expunged fd00:1122:3344:103::29 -* └─ changed: disposition -- crucible f69f92a1-5007-4bb0-a85b-604dc217154b in service fd00:1122:3344:103::2b modified -+ ├─ expunged fd00:1122:3344:103::2b -* └─ changed: disposition -- internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 in service fd00:1122:3344:103::21 modified -+ ├─ expunged fd00:1122:3344:103::21 -* └─ changed: disposition -- nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb in service fd00:1122:3344:103::22 modified -+ ├─ expunged fd00:1122:3344:103::22 -* └─ changed: disposition - -* sled 75bc286f-2b4b-482c-9431-59272af529da: blueprint zones at generation: 2 -> 3 - crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 - crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c - crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 - crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a - crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 - crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 - crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 - crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 - crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 - crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b - internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 - nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 -+ nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d added -+ nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e added -+ nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f added - -* sled affab35f-600a-4109-8ea0-34a067a4e0bc: blueprint zones at generation: 2 -> 3 - crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 - crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 - crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 - crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 - crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 - crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a - crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c - crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 - crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 - crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b - internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 - nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 -+ nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e added -+ nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d added -+ nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f added - - METADATA: - internal DNS version: 1 (unchanged) - external DNS version: 1 (unchanged) + UNCHANGED SLEDS: + + sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service fd00:1122:3344:105::26 + crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c + crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 + crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 + crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 + crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 + crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 + crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a + crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b + crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 + internal_ntp 7f4e9f9f-08f8-4d14-885d-e977c05525ad in service fd00:1122:3344:105::21 + nexus 6dff7633-66bb-4924-a6ff-2c896e66964b in service fd00:1122:3344:105::22 + + + MODIFIED SLEDS: + + sled 48d95fef-bc9f-4f50-9a53-1e075836291d: + + physical disks from generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- +- fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 +- fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 +- fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 +- fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 +- fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 +- fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 +- fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 +- fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c +- fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b +- fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ----------------------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + ----------------------------------------------------------------------------------------------------- +* crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 in service -> expunged fd00:1122:3344:103::2c +* crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea in service -> expunged fd00:1122:3344:103::25 +* crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f in service -> expunged fd00:1122:3344:103::27 +* crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 in service -> expunged fd00:1122:3344:103::28 +* crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb in service -> expunged fd00:1122:3344:103::24 +* crucible 67622d61-2df4-414d-aa0e-d1277265f405 in service -> expunged fd00:1122:3344:103::23 +* crucible b91b271d-8d80-4f49-99a0-34006ae86063 in service -> expunged fd00:1122:3344:103::2a +* crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 in service -> expunged fd00:1122:3344:103::26 +* crucible e39d7c9e-182b-48af-af87-58079d723583 in service -> expunged fd00:1122:3344:103::29 +* crucible f69f92a1-5007-4bb0-a85b-604dc217154b in service -> expunged fd00:1122:3344:103::2b +* internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 in service -> expunged fd00:1122:3344:103::21 +* nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb in service -> expunged fd00:1122:3344:103::22 + + + sled 68d24ac5-f341-49ea-a92a-0381b52ab387: + + physical disks from generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- +- fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 +- fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 +- fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 +- fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 +- fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 +- fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 +- fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 +- fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c +- fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b +- fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c + crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 + crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 + crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 + crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 + crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b + crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 + crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 + crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 + crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a + internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 + nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 + + + sled 75bc286f-2b4b-482c-9431-59272af529da: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 + crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c + crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 + crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a + crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 + crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 + crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 + crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 + crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 + crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b + internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 + nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 ++ nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d ++ nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e ++ nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f + + + sled affab35f-600a-4109-8ea0-34a067a4e0bc: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones generation 2 -> 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 + crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 + crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 + crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 + crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 + crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a + crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c + crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 + crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 + crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b + internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 + nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 ++ nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e ++ nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d ++ nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f + + + METADATA: + internal DNS version: 1 (unchanged) + external DNS version: 1 (unchanged) + diff --git a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_2_2a.txt b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_2_2a.txt index ec6c505c87..c46b3c488e 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_2_2a.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_2_2a.txt @@ -1,99 +1,222 @@ from: blueprint 9f71f5d3-a272-4382-9154-6ea2e171a6c6 to: blueprint 9f71f5d3-a272-4382-9154-6ea2e171a6c6 - -------------------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP status - -------------------------------------------------------------------------------------------------------- - - UNCHANGED SLEDS: - - sled 75bc286f-2b4b-482c-9431-59272af529da: blueprint zones at generation 3 - crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 - crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c - crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 - crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a - crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 - crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 - crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 - crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 - crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 - crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b - internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 - nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d - nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e - nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f - nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 - - sled affab35f-600a-4109-8ea0-34a067a4e0bc: blueprint zones at generation 3 - crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 - crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 - crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 - crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 - crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 - crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a - crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c - crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 - crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 - crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b - internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 - nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 - nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e - nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d - nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f - - REMOVED SLEDS: - -- sled 68d24ac5-f341-49ea-a92a-0381b52ab387: blueprint zones at generation 2 -- crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c removed -- crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 removed -- crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 removed -- crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 removed -- crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 removed -- crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b removed -- crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 removed -- crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 removed -- crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 removed -- crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a removed -- internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 removed -- nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 removed - - MODIFIED SLEDS: - -* sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: blueprint zones at generation: 2 -! warning: generation should have changed - crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 - crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 - crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 - crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 - crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 - crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a - crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b - crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 -- crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c removed -- crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service fd00:1122:3344:105::26 modified -+ ├─ quiesced fd00:1122:3344:105::26 -* └─ changed: disposition -- internal_ntp 7f4e9f9f-08f8-4d14-885d-e977c05525ad in service fd00:1122:3344:105::21 modified -+ ├─ in service fd01:1122:3344:105::21 -* └─ changed: underlay IP -- nexus 6dff7633-66bb-4924-a6ff-2c896e66964b in service fd00:1122:3344:105::22 modified -+ ├─ in service fd00:1122:3344:105::22 -* └─ changed: zone type config - -* sled 48d95fef-bc9f-4f50-9a53-1e075836291d: blueprint zones at generation: 3 -> 4 -- crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 expunged fd00:1122:3344:103::2c removed -- crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea expunged fd00:1122:3344:103::25 removed -- crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f expunged fd00:1122:3344:103::27 removed -- crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 expunged fd00:1122:3344:103::28 removed -- crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb expunged fd00:1122:3344:103::24 removed -- crucible 67622d61-2df4-414d-aa0e-d1277265f405 expunged fd00:1122:3344:103::23 removed -- crucible b91b271d-8d80-4f49-99a0-34006ae86063 expunged fd00:1122:3344:103::2a removed -- crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 expunged fd00:1122:3344:103::26 removed -- crucible e39d7c9e-182b-48af-af87-58079d723583 expunged fd00:1122:3344:103::29 removed -- crucible f69f92a1-5007-4bb0-a85b-604dc217154b expunged fd00:1122:3344:103::2b removed -- internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 expunged fd00:1122:3344:103::21 removed -- nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb expunged fd00:1122:3344:103::22 removed - - METADATA: - internal DNS version: 1 (unchanged) -* external DNS version: 1 -> 2 + UNCHANGED SLEDS: + + sled 75bc286f-2b4b-482c-9431-59272af529da: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 + crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c + crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 + crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a + crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 + crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 + crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 + crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 + crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 + crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b + internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 + nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d + nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e + nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f + nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 + + + sled affab35f-600a-4109-8ea0-34a067a4e0bc: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 + crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 + crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 + crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 + crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 + crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a + crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c + crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 + crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 + crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b + internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 + nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 + nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e + nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d + nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f + + + REMOVED SLEDS: + + sled 68d24ac5-f341-49ea-a92a-0381b52ab387: + + omicron zones from generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ +- crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c +- crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 +- crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 +- crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 +- crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 +- crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b +- crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 +- crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 +- crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 +- crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a +- internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 +- nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 + + + MODIFIED SLEDS: + + sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + -------------------------------------------------------------------------------------------------- + zone type zone id disposition underlay IP + -------------------------------------------------------------------------------------------------- + crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 + crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 + crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 + crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 + crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 + crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a + crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b + crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 +- crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c +* crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service -> quiesced fd00:1122:3344:105::26 + + + sled 48d95fef-bc9f-4f50-9a53-1e075836291d: + + omicron zones generation 3 -> 4: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ +- crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 expunged fd00:1122:3344:103::2c +- crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea expunged fd00:1122:3344:103::25 +- crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f expunged fd00:1122:3344:103::27 +- crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 expunged fd00:1122:3344:103::28 +- crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb expunged fd00:1122:3344:103::24 +- crucible 67622d61-2df4-414d-aa0e-d1277265f405 expunged fd00:1122:3344:103::23 +- crucible b91b271d-8d80-4f49-99a0-34006ae86063 expunged fd00:1122:3344:103::2a +- crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 expunged fd00:1122:3344:103::26 +- crucible e39d7c9e-182b-48af-af87-58079d723583 expunged fd00:1122:3344:103::29 +- crucible f69f92a1-5007-4bb0-a85b-604dc217154b expunged fd00:1122:3344:103::2b +- internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 expunged fd00:1122:3344:103::21 +- nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb expunged fd00:1122:3344:103::22 + + +ERRORS: + + sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9 + + zone diff errors: before gen 2, after gen 2 + + zone id: 6dff7633-66bb-4924-a6ff-2c896e66964b + reason: mismatched zone type: after: Nexus( + Nexus { + internal_address: [fd01:1122:3344:105::22]:12221, + external_ip: OmicronZoneExternalFloatingIp { + id: cd63774a-2e2f-49ce-a3df-33e3b5d02650 (external_ip), + ip: 192.0.2.2, + }, + nic: NetworkInterface { + id: 99402426-92dd-4975-9347-907e130d6b79, + kind: Service { + id: 6dff7633-66bb-4924-a6ff-2c896e66964b, + }, + name: Name( + "nexus-6dff7633-66bb-4924-a6ff-2c896e66964b", + ), + ip: 172.30.2.5, + mac: MacAddr( + MacAddr6( + [ + 168, + 64, + 37, + 255, + 128, + 0, + ], + ), + ), + subnet: V4( + Ipv4Net( + Ipv4Network { + addr: 172.30.2.0, + prefix: 24, + }, + ), + ), + vni: Vni( + 100, + ), + primary: true, + slot: 0, + }, + external_tls: false, + external_dns_servers: [], + }, +) + + zone id: 7f4e9f9f-08f8-4d14-885d-e977c05525ad + reason: mismatched underlay address: before: fd00:1122:3344:105::21, after: fd01:1122:3344:105::21 + + METADATA: + internal DNS version: 1 (unchanged) +* external DNS version: 1 -> 2 + diff --git a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_bp2.txt b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_bp2.txt index 294a12f77a..454ce6779e 100644 --- a/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_bp2.txt +++ b/nexus/reconfigurator/planning/tests/output/planner_nonprovisionable_bp2.txt @@ -1,89 +1,170 @@ blueprint 9f71f5d3-a272-4382-9154-6ea2e171a6c6 parent: 4d4e6c38-cd95-4c4e-8f45-6af4d686964b - -------------------------------------------------------------------------------------------- - zone type zone ID disposition underlay IP - -------------------------------------------------------------------------------------------- - - sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: blueprint zones at generation 2 - crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service fd00:1122:3344:105::26 - crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c - crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 - crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 - crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 - crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 - crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 - crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a - crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b - crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 - internal_ntp 7f4e9f9f-08f8-4d14-885d-e977c05525ad in service fd00:1122:3344:105::21 - nexus 6dff7633-66bb-4924-a6ff-2c896e66964b in service fd00:1122:3344:105::22 - - sled 48d95fef-bc9f-4f50-9a53-1e075836291d: blueprint zones at generation 3 - crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 expunged fd00:1122:3344:103::2c - crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea expunged fd00:1122:3344:103::25 - crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f expunged fd00:1122:3344:103::27 - crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 expunged fd00:1122:3344:103::28 - crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb expunged fd00:1122:3344:103::24 - crucible 67622d61-2df4-414d-aa0e-d1277265f405 expunged fd00:1122:3344:103::23 - crucible b91b271d-8d80-4f49-99a0-34006ae86063 expunged fd00:1122:3344:103::2a - crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 expunged fd00:1122:3344:103::26 - crucible e39d7c9e-182b-48af-af87-58079d723583 expunged fd00:1122:3344:103::29 - crucible f69f92a1-5007-4bb0-a85b-604dc217154b expunged fd00:1122:3344:103::2b - internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 expunged fd00:1122:3344:103::21 - nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb expunged fd00:1122:3344:103::22 - - sled 68d24ac5-f341-49ea-a92a-0381b52ab387: blueprint zones at generation 2 - crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c - crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 - crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 - crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 - crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 - crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b - crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 - crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 - crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 - crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a - internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 - nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 - - sled 75bc286f-2b4b-482c-9431-59272af529da: blueprint zones at generation 3 - crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 - crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c - crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 - crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a - crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 - crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 - crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 - crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 - crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 - crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b - internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 - nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d - nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e - nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f - nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 - - sled affab35f-600a-4109-8ea0-34a067a4e0bc: blueprint zones at generation 3 - crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 - crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 - crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 - crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 - crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 - crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a - crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c - crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 - crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 - crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b - internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 - nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 - nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e - nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d - nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f - -METADATA: - created by: test_blueprint2 - created at: 1970-01-01T00:00:00.000Z - comment: sled 48d95fef-bc9f-4f50-9a53-1e075836291d (sled policy is expunged): 12 zones expunged, sled 2d1cb4f2-cf44-40fc-b118-85036eb732a9: altered disks, sled 75bc286f-2b4b-482c-9431-59272af529da: altered disks, sled affab35f-600a-4109-8ea0-34a067a4e0bc: altered disks - internal DNS version: 1 - external DNS version: 1 + sled: 2d1cb4f2-cf44-40fc-b118-85036eb732a9 + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 19fbc4f8-a683-4f22-8f5a-e74782b935be in service fd00:1122:3344:105::26 + crucible 4f1ce8a2-d3a5-4a38-be4c-9817de52db37 in service fd00:1122:3344:105::2c + crucible 6b53ab2e-d98c-485f-87a3-4d5df595390f in service fd00:1122:3344:105::27 + crucible 93b137a1-a1d6-4b5b-b2cb-21a9f11e2883 in service fd00:1122:3344:105::23 + crucible 9f0abbad-dbd3-4d43-9675-78092217ffd9 in service fd00:1122:3344:105::25 + crucible b0c63f48-01ea-4aae-bb26-fb0dd59d1662 in service fd00:1122:3344:105::28 + crucible c406da50-34b9-4bb4-a460-8f49875d2a6a in service fd00:1122:3344:105::24 + crucible d660d7ed-28c0-45ae-9ace-dc3ecf7e8786 in service fd00:1122:3344:105::2a + crucible e98cc0de-abf6-4da4-a20d-d05c7a9bb1d7 in service fd00:1122:3344:105::2b + crucible f55e6aaf-e8fc-4913-9e3c-8cd1bd4bdad3 in service fd00:1122:3344:105::29 + internal_ntp 7f4e9f9f-08f8-4d14-885d-e977c05525ad in service fd00:1122:3344:105::21 + nexus 6dff7633-66bb-4924-a6ff-2c896e66964b in service fd00:1122:3344:105::22 + + + + sled: 75bc286f-2b4b-482c-9431-59272af529da + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 15bb9def-69b8-4d2e-b04f-9fee1143387c in service fd00:1122:3344:104::25 + crucible 23a8fa2b-ef3e-4017-a43f-f7a83953bd7c in service fd00:1122:3344:104::2c + crucible 621509d6-3772-4009-aca1-35eefd1098fb in service fd00:1122:3344:104::28 + crucible 85b8c68a-160d-461d-94dd-1baf175fa75c in service fd00:1122:3344:104::2a + crucible 996d7570-b0df-46d5-aaa4-0c97697cf484 in service fd00:1122:3344:104::26 + crucible a732c489-d29a-4f75-b900-5966385943af in service fd00:1122:3344:104::29 + crucible b1783e95-9598-451d-b6ba-c50b52b428c3 in service fd00:1122:3344:104::24 + crucible c6dd531e-2d1d-423b-acc8-358533dab78c in service fd00:1122:3344:104::27 + crucible e4b3e159-3dbe-48cb-8497-e3da92a90e5a in service fd00:1122:3344:104::23 + crucible f0ff59e8-4105-4980-a4bb-a1f4c58de1e3 in service fd00:1122:3344:104::2b + internal_ntp 57b96d5c-b71e-43e4-8869-7d514003d00d in service fd00:1122:3344:104::21 + nexus 2ec75441-3d7d-4b4b-9614-af03de5a3666 in service fd00:1122:3344:104::2d + nexus 508abd03-cbfe-4654-9a6d-7f15a1ad32e5 in service fd00:1122:3344:104::2e + nexus 59950bc8-1497-44dd-8cbf-b6502ba921b2 in service fd00:1122:3344:104::2f + nexus b4947d31-f70e-4ee0-8817-0ca6cea9b16b in service fd00:1122:3344:104::22 + + + + sled: affab35f-600a-4109-8ea0-34a067a4e0bc + + physical disks at generation 1: + ---------------------------------------------------------------------- + vendor model serial + ---------------------------------------------------------------------- + fake-vendor fake-model serial-088f76ef-e985-41fd-8630-c321ed8cca37 + fake-vendor fake-model serial-30d0e693-dec4-402f-baa0-d6d9a93c98a7 + fake-vendor fake-model serial-32e90a17-7080-4c33-a94d-05f4bfb5d368 + fake-vendor fake-model serial-44473266-e28a-43fa-9314-c3416b8b3c14 + fake-vendor fake-model serial-53372ece-d666-4f5b-8f25-286e36242088 + fake-vendor fake-model serial-795061c9-db7b-404a-a2a3-0dad5fdfceb1 + fake-vendor fake-model serial-7b8bc126-4ff8-434f-a949-e98eda2709a5 + fake-vendor fake-model serial-b644318e-da11-46e1-b650-47a067e6024c + fake-vendor fake-model serial-bb2b397b-a3f5-4142-a433-4f2ab5fe284b + fake-vendor fake-model serial-bdbf1352-725d-4b17-98d5-4d7105726721 + + + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 0dfbf374-9ef9-430f-b06d-f271bf7f84c4 in service fd00:1122:3344:101::27 + crucible 3aa07966-5899-4789-ace5-f8eeb375c6c3 in service fd00:1122:3344:101::24 + crucible 4ad0e9da-08f8-4d40-b4d3-d17e711b5bbf in service fd00:1122:3344:101::29 + crucible 72c5a909-077d-4ec1-a9d5-ae64ef9d716e in service fd00:1122:3344:101::26 + crucible 95482c25-1e7f-43e8-adf1-e3548a1b3ae0 in service fd00:1122:3344:101::23 + crucible a1c03689-fc62-4ea5-bb72-4d01f5138614 in service fd00:1122:3344:101::2a + crucible a568e92e-4fbd-4b69-acd8-f16277073031 in service fd00:1122:3344:101::2c + crucible bf79a56a-97af-4cc4-94a5-8b20d64c2cda in service fd00:1122:3344:101::28 + crucible c60379ba-4e30-4628-a79a-0ae509aef4c5 in service fd00:1122:3344:101::25 + crucible d47f4996-fac0-4657-bcea-01b1fee6404d in service fd00:1122:3344:101::2b + internal_ntp f1a7b9a7-fc6a-4b23-b829-045ff33117ff in service fd00:1122:3344:101::21 + nexus 15c103f0-ac63-423b-ba5d-1b5fcd563ba3 in service fd00:1122:3344:101::22 + nexus 3ca5292f-8a59-4475-bb72-0f43714d0fff in service fd00:1122:3344:101::2e + nexus 99f6d544-8599-4e2b-a55a-82d9e0034662 in service fd00:1122:3344:101::2d + nexus c26b3bda-5561-44a1-a69f-22103fe209a1 in service fd00:1122:3344:101::2f + + + +!48d95fef-bc9f-4f50-9a53-1e075836291d +WARNING: Zones exist without physical disks! + omicron zones at generation 3: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 094f27af-1acb-4d1e-ba97-1fc1377d4bf2 expunged fd00:1122:3344:103::2c + crucible 0dcfdfc5-481e-4153-b97c-11cf02b648ea expunged fd00:1122:3344:103::25 + crucible 2f5e8010-a94d-43a4-9c5c-3f52832f5f7f expunged fd00:1122:3344:103::27 + crucible 4a9a0a9d-87f0-4f1d-9181-27f6b435e637 expunged fd00:1122:3344:103::28 + crucible 56ac1706-9e2a-49ba-bd6f-a99c44cb2ccb expunged fd00:1122:3344:103::24 + crucible 67622d61-2df4-414d-aa0e-d1277265f405 expunged fd00:1122:3344:103::23 + crucible b91b271d-8d80-4f49-99a0-34006ae86063 expunged fd00:1122:3344:103::2a + crucible d6ee1338-3127-43ec-9aaa-b973ccf05496 expunged fd00:1122:3344:103::26 + crucible e39d7c9e-182b-48af-af87-58079d723583 expunged fd00:1122:3344:103::29 + crucible f69f92a1-5007-4bb0-a85b-604dc217154b expunged fd00:1122:3344:103::2b + internal_ntp 67d913e0-0005-4599-9b28-0abbf6cc2916 expunged fd00:1122:3344:103::21 + nexus 2aa0ea4f-3561-4989-a98c-9ab7d9a240fb expunged fd00:1122:3344:103::22 + + + + +!68d24ac5-f341-49ea-a92a-0381b52ab387 +WARNING: Zones exist without physical disks! + omicron zones at generation 2: + ------------------------------------------------------------------------------------------ + zone type zone id disposition underlay IP + ------------------------------------------------------------------------------------------ + crucible 3b3c14b6-a8e2-4054-a577-8d96cb576230 expunged fd00:1122:3344:102::2c + crucible 47a87c6e-ef45-4d52-9a3e-69cdd96737cc expunged fd00:1122:3344:102::23 + crucible 6464d025-4652-4948-919e-740bec5699b1 expunged fd00:1122:3344:102::24 + crucible 6939ce48-b17c-4616-b176-8a419a7697be expunged fd00:1122:3344:102::29 + crucible 878dfddd-3113-4197-a3ea-e0d4dbe9b476 expunged fd00:1122:3344:102::25 + crucible 8d4d2b28-82bb-4e36-80da-1408d8c35d82 expunged fd00:1122:3344:102::2b + crucible 9fd52961-426f-4e62-a644-b70871103fca expunged fd00:1122:3344:102::26 + crucible b44cdbc0-0ce0-46eb-8b21-a09e113aa1d0 expunged fd00:1122:3344:102::27 + crucible b6b759d0-f60d-42b7-bbbc-9d61c9e895a9 expunged fd00:1122:3344:102::28 + crucible c407795c-6c8b-428e-8ab8-b962913c447f expunged fd00:1122:3344:102::2a + internal_ntp f3f2e4f3-0985-4ef6-8336-ce479382d05d expunged fd00:1122:3344:102::21 + nexus 01d58626-e1b0-480f-96be-ac784863c7dc expunged fd00:1122:3344:102::22 + + + + METADATA: + created by::::::::::: test_blueprint2 + created at::::::::::: 1970-01-01T00:00:00.000Z + comment:::::::::::::: sled 48d95fef-bc9f-4f50-9a53-1e075836291d (sled policy is expunged): 12 zones expunged + internal DNS version: 1 + external DNS version: 1 + diff --git a/nexus/types/Cargo.toml b/nexus/types/Cargo.toml index 8f76633416..802727b1ab 100644 --- a/nexus/types/Cargo.toml +++ b/nexus/types/Cargo.toml @@ -13,6 +13,7 @@ chrono.workspace = true clap.workspace = true base64.workspace = true derive-where.workspace = true +derive_more.workspace = true futures.workspace = true humantime.workspace = true ipnetwork.workspace = true @@ -27,7 +28,6 @@ slog.workspace = true slog-error-chain.workspace = true steno.workspace = true strum.workspace = true -tabled.workspace = true thiserror.workspace = true newtype-uuid.workspace = true uuid.workspace = true diff --git a/nexus/types/src/deployment.rs b/nexus/types/src/deployment.rs index a577c4978c..aafa631320 100644 --- a/nexus/types/src/deployment.rs +++ b/nexus/types/src/deployment.rs @@ -21,8 +21,10 @@ pub use crate::inventory::OmicronZoneType; pub use crate::inventory::OmicronZonesConfig; pub use crate::inventory::SourceNatConfig; pub use crate::inventory::ZpoolName; +use derive_more::From; use newtype_uuid::GenericUuid; use omicron_common::api::external::Generation; +use omicron_common::disk::DiskIdentity; use omicron_uuid_kinds::CollectionUuid; use omicron_uuid_kinds::ExternalIpUuid; use omicron_uuid_kinds::OmicronZoneUuid; @@ -30,10 +32,11 @@ use omicron_uuid_kinds::SledUuid; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use sled_agent_client::types::OmicronPhysicalDisksConfig; use sled_agent_client::ZoneKind; use slog_error_chain::SlogInlineError; use std::collections::BTreeMap; -use std::collections::HashMap; +use std::collections::BTreeSet; use std::fmt; use std::net::AddrParseError; use std::net::Ipv6Addr; @@ -42,6 +45,8 @@ use strum::IntoEnumIterator; use thiserror::Error; use uuid::Uuid; +mod blueprint_diff; +mod blueprint_display; mod network_resources; mod planning_input; mod tri_map; @@ -70,6 +75,14 @@ pub use planning_input::ZpoolFilter; pub use zone_type::blueprint_zone_type; pub use zone_type::BlueprintZoneType; +use blueprint_display::{ + constants::*, BpDiffState, BpGeneration, BpOmicronZonesSubtableSchema, + BpPhysicalDisksSubtableSchema, BpSledSubtable, BpSledSubtableData, + BpSledSubtableRow, KvListWithHeading, +}; + +pub use blueprint_diff::BlueprintDiff; + /// Describes a complete set of software and configuration for the system // Blueprints are a fundamental part of how the system modifies itself. Each // blueprint completely describes all of the software and configuration @@ -206,10 +219,7 @@ impl Blueprint { /// The argument provided is the "before" side, and `self` is the "after" /// side. This matches the order of arguments to /// [`Blueprint::diff_since_collection`]. - pub fn diff_since_blueprint( - &self, - before: &Blueprint, - ) -> Result { + pub fn diff_since_blueprint(&self, before: &Blueprint) -> BlueprintDiff { BlueprintDiff::new( DiffBeforeMetadata::Blueprint(Box::new(before.metadata())), before @@ -219,6 +229,12 @@ impl Blueprint { .collect(), self.metadata(), self.blueprint_zones.clone(), + before + .blueprint_disks + .iter() + .map(|(sled_id, disks)| (*sled_id, disks.clone().into())) + .collect(), + self.blueprint_disks.clone(), ) } @@ -231,10 +247,7 @@ impl Blueprint { /// Note that collections do not include information about zone /// disposition, so it is assumed that all zones in the collection have the /// [`InService`](BlueprintZoneDisposition::InService) disposition. - pub fn diff_since_collection( - &self, - before: &Collection, - ) -> Result { + pub fn diff_since_collection(&self, before: &Collection) -> BlueprintDiff { let before_zones = before .omicron_zones .iter() @@ -243,11 +256,31 @@ impl Blueprint { }) .collect(); + let before_disks = before + .sled_agents + .iter() + .map(|(sled_id, sa)| { + ( + *sled_id, + CollectionPhysicalDisksConfig { + disks: sa + .disks + .iter() + .map(|d| d.identity.clone()) + .collect::>(), + } + .into(), + ) + }) + .collect(); + BlueprintDiff::new( DiffBeforeMetadata::Collection { id: before.id }, before_zones, self.metadata(), self.blueprint_zones.clone(), + before_disks, + self.blueprint_disks.clone(), ) } @@ -258,6 +291,47 @@ impl Blueprint { } } +impl BpSledSubtableData for &OmicronPhysicalDisksConfig { + fn bp_generation(&self) -> BpGeneration { + BpGeneration::Value(self.generation) + } + + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator { + let sorted_disk_ids: BTreeSet = + self.disks.iter().map(|d| d.identity.clone()).collect(); + + sorted_disk_ids.into_iter().map(move |d| { + BpSledSubtableRow::new(state, vec![d.vendor, d.model, d.serial]) + }) + } +} + +impl BpSledSubtableData for BlueprintOrCollectionZonesConfig { + fn bp_generation(&self) -> BpGeneration { + BpGeneration::Value(self.generation()) + } + + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator { + self.zones().map(move |zone| { + BpSledSubtableRow::new( + state, + vec![ + zone.kind().to_string(), + zone.id().to_string(), + zone.disposition().to_string(), + zone.underlay_address().to_string(), + ], + ) + }) + } +} + /// Wrapper to allow a [`Blueprint`] to be displayed with information. /// /// Returned by [`Blueprint::display()`]. @@ -268,6 +342,39 @@ pub struct BlueprintDisplay<'a> { // TODO: add colorization with a stylesheet } +impl<'a> BlueprintDisplay<'a> { + pub(super) fn make_metadata_table(&self) -> KvListWithHeading { + let comment = if self.blueprint.comment.is_empty() { + NONE_PARENS.to_string() + } else { + self.blueprint.comment.clone() + }; + + KvListWithHeading::new_unchanged( + METADATA_HEADING, + vec![ + (CREATED_BY, self.blueprint.creator.clone()), + ( + CREATED_AT, + humantime::format_rfc3339_millis( + self.blueprint.time_created.into(), + ) + .to_string(), + ), + (COMMENT, comment), + ( + INTERNAL_DNS_VERSION, + self.blueprint.internal_dns_version.to_string(), + ), + ( + EXTERNAL_DNS_VERSION, + self.blueprint.external_dns_version.to_string(), + ), + ], + ) + } +} + impl<'a> fmt::Display for BlueprintDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let b = self.blueprint; @@ -280,9 +387,62 @@ impl<'a> fmt::Display for BlueprintDisplay<'a> { .unwrap_or_else(|| String::from("")) )?; - writeln!(f, "\n{}", self.make_zone_table())?; + // Keep track of any sled_ids that have been seen in the first loop. + let mut seen_sleds = BTreeSet::new(); + + // Loop through all sleds that have physical disks and print a table of + // those physical disks. + // + // If there are corresponding zones, print those as well. + for (sled_id, disks) in &self.blueprint.blueprint_disks { + // Construct the disks subtable + let disks_table = BpSledSubtable::new( + BpPhysicalDisksSubtableSchema {}, + disks.bp_generation(), + disks.rows(BpDiffState::Unchanged).collect(), + ); + + // Construct the zones subtable + match self.blueprint.blueprint_zones.get(sled_id) { + Some(zones) => { + let zones = + BlueprintOrCollectionZonesConfig::from(zones.clone()); + let zones_tab = BpSledSubtable::new( + BpOmicronZonesSubtableSchema {}, + zones.bp_generation(), + zones.rows(BpDiffState::Unchanged).collect(), + ); + writeln!( + f, + "\n sled: {sled_id}\n\n{disks_table}\n\n{zones_tab}\n" + )?; + } + None => writeln!(f, "\n sled: {sled_id}\n\n{disks_table}\n")?, + } + seen_sleds.insert(sled_id); + } + + // Now create and display a table of zones on sleds that don't + // yet have physical disks. + // + // This should basically be impossible, so we warn if it occurs. + for (sled_id, zones) in &self.blueprint.blueprint_zones { + if !seen_sleds.contains(sled_id) && !zones.zones.is_empty() { + let zones = + BlueprintOrCollectionZonesConfig::from(zones.clone()); + writeln!( + f, + "\n!{sled_id}\n{}\n{}\n\n", + "WARNING: Zones exist without physical disks!", + BpSledSubtable::new( + BpOmicronZonesSubtableSchema {}, + zones.bp_generation(), + zones.rows(BpDiffState::Unchanged).collect() + ) + )?; + } + } - writeln!(f, "\n{}", table_display::metadata_heading())?; writeln!(f, "{}", self.make_metadata_table())?; Ok(()) @@ -872,244 +1032,6 @@ pub struct BlueprintTargetSet { pub enabled: bool, } -/// Summarizes the differences between two blueprints -#[derive(Debug)] -pub struct BlueprintDiff { - before_meta: DiffBeforeMetadata, - after_meta: BlueprintMetadata, - sleds: DiffSleds, -} - -impl BlueprintDiff { - /// Build a diff with the provided contents, verifying that the provided - /// data is valid. - fn new( - before_meta: DiffBeforeMetadata, - before_zones: BTreeMap, - after_meta: BlueprintMetadata, - after_zones: BTreeMap, - ) -> Result { - let mut errors = Vec::new(); - - let sleds = DiffSleds::new(before_zones, after_zones, &mut errors); - - if errors.is_empty() { - Ok(Self { before_meta, after_meta, sleds }) - } else { - Err(BlueprintDiffError { - before_meta, - after_meta: Box::new(after_meta), - errors, - }) - } - } - - /// Returns metadata about the source of the "before" data. - pub fn before_meta(&self) -> &DiffBeforeMetadata { - &self.before_meta - } - - /// Returns metadata about the source of the "after" data. - pub fn after_meta(&self) -> &BlueprintMetadata { - &self.after_meta - } - - /// Iterate over sleds only present in the second blueprint of a diff - pub fn sleds_added( - &self, - ) -> impl ExactSizeIterator + '_ - { - self.sleds.added.iter().map(|(sled_id, zones)| (*sled_id, zones)) - } - - /// Iterate over sleds only present in the first blueprint of a diff - pub fn sleds_removed( - &self, - ) -> impl ExactSizeIterator< - Item = (SledUuid, &BlueprintOrCollectionZonesConfig), - > + '_ { - self.sleds.removed.iter().map(|(sled_id, zones)| (*sled_id, zones)) - } - - /// Iterate over sleds present in both blueprints in a diff that have - /// changes. - pub fn sleds_modified( - &self, - ) -> impl ExactSizeIterator + '_ { - self.sleds.modified.iter().map(|(sled_id, sled)| (*sled_id, sled)) - } - - /// Iterate over sleds present in both blueprints in a diff that have no - /// changes. - pub fn sleds_unchanged( - &self, - ) -> impl Iterator + '_ { - self.sleds.unchanged.iter().map(|(sled_id, zones)| (*sled_id, zones)) - } - - /// Return a struct that can be used to display the diff. - pub fn display(&self) -> BlueprintDiffDisplay<'_> { - BlueprintDiffDisplay::new(self) - } -} - -#[derive(Debug)] -struct DiffSleds { - added: BTreeMap, - removed: BTreeMap, - modified: BTreeMap, - unchanged: BTreeMap, -} - -impl DiffSleds { - /// Builds added, removed and common maps, verifying that the provided data - /// is valid. - /// - /// The return value only contains the sleds that are present in both - /// blueprints. - fn new( - before: BTreeMap, - mut after: BTreeMap, - errors: &mut Vec, - ) -> Self { - let mut removed = BTreeMap::new(); - let mut modified = BTreeMap::new(); - let mut unchanged = BTreeMap::new(); - - for (sled_id, mut before_z) in before { - if let Some(mut after_z) = after.remove(&sled_id) { - // Sort before_z and after_z so they can be compared directly. - before_z.sort(); - after_z.sort(); - - if before_z == after_z { - unchanged.insert(sled_id, after_z); - } else { - let sled_modified = DiffSledModified::new( - sled_id, before_z, after_z, errors, - ); - modified.insert(sled_id, sled_modified); - } - } else { - removed.insert(sled_id, before_z); - } - } - - // We removed everything common from `after` above, so anything left is - // an added sled. - Self { added: after, removed, modified, unchanged } - } -} - -/// Wrapper to allow a [`BlueprintDiff`] to be displayed. -/// -/// Returned by [`BlueprintDiff::display()`]. -#[derive(Clone, Debug)] -#[must_use = "this struct does nothing unless displayed"] -pub struct BlueprintDiffDisplay<'diff> { - diff: &'diff BlueprintDiff, - // TODO: add colorization with a stylesheet -} - -impl<'diff> BlueprintDiffDisplay<'diff> { - #[inline] - fn new(diff: &'diff BlueprintDiff) -> Self { - Self { diff } - } -} - -impl<'diff> fmt::Display for BlueprintDiffDisplay<'diff> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let diff = self.diff; - - // Print things differently based on whether the diff is between a - // collection and a blueprint, or a blueprint and a blueprint. - match &diff.before_meta { - DiffBeforeMetadata::Collection { id } => { - writeln!( - f, - "from: collection {}\n\ - to: blueprint {}", - id, diff.after_meta.id, - )?; - } - DiffBeforeMetadata::Blueprint(before) => { - writeln!( - f, - "from: blueprint {}\n\ - to: blueprint {}", - before.id, diff.after_meta.id - )?; - } - } - - writeln!(f, "\n{}", self.make_zone_diff_table())?; - - writeln!(f, "\n{}", table_display::metadata_diff_heading())?; - writeln!(f, "{}", self.make_metadata_diff_table())?; - - Ok(()) - } -} - -#[derive(Clone, Debug, Error)] -pub struct BlueprintDiffError { - pub before_meta: DiffBeforeMetadata, - pub after_meta: Box, - pub errors: Vec, -} - -impl fmt::Display for BlueprintDiffError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!( - f, - "errors in diff between {} and {}:", - self.before_meta.display_id(), - self.after_meta.display_id() - )?; - for e in &self.errors { - writeln!(f, " - {}", e)?; - } - Ok(()) - } -} - -/// An individual error within a [`BlueprintDiffError`]. -#[derive(Clone, Debug)] -pub enum BlueprintDiffSingleError { - /// The [`OmicronZoneType`] of a particular zone changed between the before - /// and after blueprints. - /// - /// For a particular zone, the type should never change. - ZoneTypeChanged { - sled_id: SledUuid, - zone_id: Uuid, - before: ZoneKind, - after: ZoneKind, - }, - InvalidOmicronZoneType(InvalidOmicronZoneType), -} - -impl fmt::Display for BlueprintDiffSingleError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BlueprintDiffSingleError::ZoneTypeChanged { - sled_id, - zone_id, - before, - after, - } => write!( - f, - "on sled {sled_id}, zone {zone_id} changed type \ - from {before} to {after}", - ), - BlueprintDiffSingleError::InvalidOmicronZoneType(err) => { - write!(f, "invalid OmicronZoneType in collection: {err}") - } - } - } -} - /// Data about the "before" version within a [`BlueprintDiff`]. #[derive(Clone, Debug)] pub enum DiffBeforeMetadata { @@ -1216,6 +1138,15 @@ impl From for BlueprintOrCollectionZoneConfig { } } +impl PartialEq for BlueprintOrCollectionZoneConfig { + fn eq(&self, other: &BlueprintZoneConfig) -> bool { + self.kind() == other.kind() + && self.disposition() == other.disposition + && self.underlay_address() == other.underlay_address + && self.is_zone_type_equal(&other.zone_type) + } +} + impl BlueprintOrCollectionZoneConfig { pub fn id(&self) -> OmicronZoneUuid { match self { @@ -1266,151 +1197,39 @@ impl BlueprintOrCollectionZoneConfig { } } -/// Describes a sled that appeared on both sides of a diff and is changed. -#[derive(Clone, Debug)] -pub struct DiffSledModified { - /// id of the sled - pub sled_id: SledUuid, - /// generation of the "zones" configuration on the left side - pub generation_before: Generation, - /// generation of the "zones" configuration on the right side - pub generation_after: Generation, - zones_added: Vec, - zones_removed: Vec, - zones_common: Vec, +/// Single sled's disks config for "before" version within a [`BlueprintDiff`]. +#[derive(Clone, Debug, From)] +pub enum BlueprintOrCollectionDisksConfig { + /// The diff was made from a collection. + Collection(CollectionPhysicalDisksConfig), + /// The diff was made from a blueprint. + Blueprint(BlueprintPhysicalDisksConfig), } -impl DiffSledModified { - fn new( - sled_id: SledUuid, - before: BlueprintOrCollectionZonesConfig, - after: BlueprintZonesConfig, - errors: &mut Vec, - ) -> Self { - // Assemble separate summaries of the zones, indexed by zone id. - let before_by_id: HashMap<_, _> = - before.zones().map(|zone| (zone.id(), zone)).collect(); - let mut after_by_id: HashMap<_, _> = - after.zones.into_iter().map(|zone| (zone.id, zone)).collect(); - - let mut zones_removed = Vec::new(); - let mut zones_common = Vec::new(); - - // Now go through each zone and compare them. - for (zone_id, zone_before) in before_by_id { - if let Some(zone_after) = after_by_id.remove(&zone_id) { - let before_kind = zone_before.kind(); - let after_kind = zone_after.zone_type.kind(); - - if before_kind != after_kind { - errors.push(BlueprintDiffSingleError::ZoneTypeChanged { - sled_id, - zone_id: zone_id.into_untyped_uuid(), - before: before_kind, - after: after_kind, - }); - } else { - let common = DiffZoneCommon { zone_before, zone_after }; - zones_common.push(common); - } - } else { - zones_removed.push(zone_before); +impl BlueprintOrCollectionDisksConfig { + pub fn generation(&self) -> Option { + match self { + BlueprintOrCollectionDisksConfig::Collection(_) => None, + BlueprintOrCollectionDisksConfig::Blueprint(c) => { + Some(c.generation) } } - - // Since we removed common zones above, anything else exists only in - // before and was therefore added. - let mut zones_added: Vec<_> = after_by_id.into_values().collect(); - - // Sort for test reproducibility. - zones_added.sort_unstable_by_key(zone_sort_key); - zones_removed.sort_unstable_by_key(zone_sort_key); - zones_common.sort_unstable_by_key(|common| { - // The ID is common by definition, and the zone type was already - // verified to be the same above. So just sort by the sort key for - // the before zone. (In case of errors, the result will be thrown - // away anyway, so this is harmless.) - zone_sort_key(&common.zone_before) - }); - - Self { - sled_id, - generation_before: before.generation(), - generation_after: after.generation, - zones_added, - zones_removed, - zones_common, - } - } - - /// Iterate over zones added between the blueprints - pub fn zones_added( - &self, - ) -> impl ExactSizeIterator + '_ { - self.zones_added.iter() } - /// Iterate over zones removed between the blueprints - pub fn zones_removed( - &self, - ) -> impl ExactSizeIterator + '_ - { - self.zones_removed.iter() - } - - /// Iterate over zones that are common to both blueprints - pub fn zones_in_common( - &self, - ) -> impl ExactSizeIterator + '_ { - self.zones_common.iter() - } - - /// Iterate over zones that changed between the blueprints - pub fn zones_modified(&self) -> impl Iterator + '_ { - self.zones_in_common().filter(|z| z.is_modified()) - } - - /// Iterate over zones that did not change between the blueprints - pub fn zones_unchanged( - &self, - ) -> impl Iterator + '_ { - self.zones_in_common().filter(|z| !z.is_modified()) + pub fn disks(&self) -> BTreeSet { + match self { + BlueprintOrCollectionDisksConfig::Collection(c) => c.disks.clone(), + BlueprintOrCollectionDisksConfig::Blueprint(c) => { + c.disks.iter().map(|d| d.identity.clone()).collect() + } + } } } -/// Describes a zone that was common to both sides of a diff -#[derive(Debug, Clone)] -pub struct DiffZoneCommon { - /// full zone configuration before - pub zone_before: BlueprintOrCollectionZoneConfig, - /// full zone configuration after - pub zone_after: BlueprintZoneConfig, -} - -impl DiffZoneCommon { - /// Returns true if there are any differences between `zone_before` and - /// `zone_after`. - /// - /// This is equivalent to `config_changed() || disposition_changed()`. - #[inline] - pub fn is_modified(&self) -> bool { - // state is smaller and easier to compare than config. - self.disposition_changed() || self.config_changed() - } - - /// Returns true if the zone configuration (excluding the disposition) - /// changed. - #[inline] - pub fn config_changed(&self) -> bool { - self.zone_before.underlay_address() != self.zone_after.underlay_address - || !self.zone_before.is_zone_type_equal(&self.zone_after.zone_type) - } - - /// Returns true if the [`BlueprintZoneDisposition`] for the zone changed. - #[inline] - pub fn disposition_changed(&self) -> bool { - self.zone_before.disposition() != self.zone_after.disposition - } +/// Single sled's disk config for "before" version within a [`BlueprintDiff`]. +#[derive(Clone, Debug, From)] +pub struct CollectionPhysicalDisksConfig { + disks: BTreeSet, } /// Encapsulates Reconfigurator state @@ -1430,658 +1249,3 @@ pub struct UnstableReconfiguratorState { pub silo_names: Vec, pub external_dns_zone_names: Vec, } - -/// Code to generate tables. -/// -/// This is here because `tabled` has a number of generically-named types, and -/// we'd like to avoid name collisions with other types. -mod table_display { - use super::*; - use crate::sectioned_table::SectionSpacing; - use crate::sectioned_table::StBuilder; - use crate::sectioned_table::StSectionBuilder; - use tabled::builder::Builder; - use tabled::settings::object::Columns; - use tabled::settings::Modify; - use tabled::settings::Padding; - use tabled::settings::Style; - use tabled::Table; - - impl<'a> super::BlueprintDisplay<'a> { - pub(super) fn make_zone_table(&self) -> Table { - let blueprint_zones = &self.blueprint.blueprint_zones; - let mut builder = StBuilder::new(); - builder.push_header_row(header_row()); - - for (sled_id, sled_zones) in blueprint_zones { - let heading = format!( - "{SLED_INDENT}sled {sled_id}: blueprint zones at generation {}", - sled_zones.generation - ); - builder.make_section( - SectionSpacing::Always, - heading, - |section| { - for zone in &sled_zones.zones { - add_zone_record( - ZONE_INDENT.to_string(), - &zone.clone().into(), - section, - ); - } - - if section.is_empty() { - section.push_nested_heading( - SectionSpacing::IfNotFirst, - format!("{ZONE_HEAD_INDENT}{NO_ZONES_PARENS}"), - ); - } - }, - ); - } - - builder.build() - } - - pub(super) fn make_metadata_table(&self) -> Table { - let mut builder = Builder::new(); - - // Metadata is presented as a linear (top-to-bottom) table with a - // small indent. - - builder.push_record(vec![ - METADATA_INDENT.to_string(), - linear_table_label(&CREATED_BY), - self.blueprint.creator.clone(), - ]); - - builder.push_record(vec![ - METADATA_INDENT.to_string(), - linear_table_label(&CREATED_AT), - humantime::format_rfc3339_millis( - self.blueprint.time_created.into(), - ) - .to_string(), - ]); - - let comment = if self.blueprint.comment.is_empty() { - NONE_PARENS.to_string() - } else { - self.blueprint.comment.clone() - }; - - builder.push_record(vec![ - METADATA_INDENT.to_string(), - linear_table_label(&COMMENT), - comment, - ]); - - builder.push_record(vec![ - METADATA_INDENT.to_string(), - linear_table_label(&INTERNAL_DNS_VERSION), - self.blueprint.internal_dns_version.to_string(), - ]); - - builder.push_record(vec![ - METADATA_INDENT.to_string(), - linear_table_label(&EXTERNAL_DNS_VERSION), - self.blueprint.external_dns_version.to_string(), - ]); - - let mut table = builder.build(); - apply_linear_table_settings(&mut table); - table - } - } - - impl<'diff> BlueprintDiffDisplay<'diff> { - pub(super) fn make_zone_diff_table(&self) -> Table { - let diff = self.diff; - - // Add the unchanged prefix to the zone indent since the first - // column will be used as the prefix. - let mut builder = StBuilder::new(); - builder.push_header_row(diff_header_row()); - - // The order is: - // - // 1. Unchanged - // 2. Removed - // 3. Modified - // 4. Added - // - // The idea behind the order is to (a) group all changes together - // and (b) put changes towards the bottom, so people have to scroll - // back less. - // - // Zones within a modified sled follow the same order. If you're - // changing the order here, make sure to keep that in sync. - - // First, unchanged sleds. - builder.make_section( - SectionSpacing::Always, - unchanged_sleds_heading(), - |section| { - for (sled_id, sled_zones) in diff.sleds_unchanged() { - add_whole_sled_records( - sled_id, - &sled_zones.clone().into(), - WholeSledKind::Unchanged, - section, - ); - } - }, - ); - - // Then, removed sleds. - builder.make_section( - SectionSpacing::Always, - removed_sleds_heading(), - |section| { - for (sled_id, sled_zones) in diff.sleds_removed() { - add_whole_sled_records( - sled_id, - sled_zones, - WholeSledKind::Removed, - section, - ); - } - }, - ); - - // Then, modified sleds. - builder.make_section( - SectionSpacing::Always, - modified_sleds_heading(), - |section| { - // For sleds that are in common: - for (sled_id, modified) in diff.sleds_modified() { - add_modified_sled_records(sled_id, modified, section); - } - }, - ); - - // Finally, added sleds. - builder.make_section( - SectionSpacing::Always, - added_sleds_heading(), - |section| { - for (sled_id, sled_zones) in diff.sleds_added() { - add_whole_sled_records( - sled_id, - &sled_zones.clone().into(), - WholeSledKind::Added, - section, - ); - } - }, - ); - - builder.build() - } - - pub(super) fn make_metadata_diff_table(&self) -> Table { - let diff = self.diff; - let mut builder = Builder::new(); - - // Metadata is presented as a linear (top-to-bottom) table with a - // small indent. - - match &diff.before_meta { - DiffBeforeMetadata::Collection { .. } => { - // Collections don't have DNS versions, so this is new. - builder.push_record(vec![ - format!("{ADDED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_internal_dns(), - linear_table_modified( - &NOT_PRESENT_IN_COLLECTION_PARENS, - &diff.after_meta.internal_dns_version, - ), - ]); - - builder.push_record(vec![ - format!("{ADDED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_external_dns(), - linear_table_modified( - &NOT_PRESENT_IN_COLLECTION_PARENS, - &diff.after_meta.external_dns_version, - ), - ]); - } - DiffBeforeMetadata::Blueprint(before) => { - if before.internal_dns_version - != diff.after_meta.internal_dns_version - { - builder.push_record(vec![ - format!("{MODIFIED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_internal_dns(), - linear_table_modified( - &before.internal_dns_version, - &diff.after_meta.internal_dns_version, - ), - ]); - } else { - builder.push_record(vec![ - format!("{UNCHANGED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_internal_dns(), - linear_table_unchanged( - &before.internal_dns_version, - ), - ]); - }; - - if before.external_dns_version - != diff.after_meta.external_dns_version - { - builder.push_record(vec![ - format!("{MODIFIED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_external_dns(), - linear_table_modified( - &before.external_dns_version, - &diff.after_meta.external_dns_version, - ), - ]); - } else { - builder.push_record(vec![ - format!("{UNCHANGED_PREFIX}{METADATA_DIFF_INDENT}"), - metadata_table_external_dns(), - linear_table_unchanged( - &before.external_dns_version, - ), - ]); - }; - } - } - - let mut table = builder.build(); - apply_linear_table_settings(&mut table); - table - } - } - - fn add_whole_sled_records( - sled_id: SledUuid, - sled_zones: &BlueprintOrCollectionZonesConfig, - kind: WholeSledKind, - section: &mut StSectionBuilder, - ) { - let heading = format!( - "{}{SLED_INDENT}sled {sled_id}: blueprint zones at generation {}", - kind.prefix(), - sled_zones.generation(), - ); - let prefix = kind.prefix(); - let status = kind.status(); - section.make_subsection(SectionSpacing::Always, heading, |s2| { - // Also add another section for zones. - for zone in sled_zones.zones() { - match status { - Some(status) => { - add_zone_record_with_status( - format!("{prefix}{ZONE_INDENT}"), - &zone, - status, - s2, - ); - } - None => { - add_zone_record( - format!("{prefix}{ZONE_INDENT}"), - &zone, - s2, - ); - } - } - } - }); - } - - fn add_modified_sled_records( - sled_id: SledUuid, - modified: &DiffSledModified, - section: &mut StSectionBuilder, - ) { - let (generation_heading, warning) = - if modified.generation_before != modified.generation_after { - ( - format!( - "blueprint zones at generation: {} -> {}", - modified.generation_before, modified.generation_after, - ), - None, - ) - } else { - // Modified sleds should always see a generation bump. - ( - format!( - "blueprint zones at generation: {}", - modified.generation_before - ), - Some(format!( - "{WARNING_PREFIX}{ZONE_HEAD_INDENT}\ - warning: generation should have changed" - )), - ) - }; - - let sled_heading = - format!("{MODIFIED_PREFIX}{SLED_INDENT}sled {sled_id}: {generation_heading}"); - - section.make_subsection(SectionSpacing::Always, sled_heading, |s2| { - if let Some(warning) = warning { - s2.push_nested_heading(SectionSpacing::Never, warning); - } - - // The order is: - // - // 1. Unchanged - // 2. Removed - // 3. Modified - // 4. Added - // - // The idea behind the order is to (a) group all changes together - // and (b) put changes towards the bottom, so people have to scroll - // back less. - // - // Sleds follow the same order. If you're changing the order here, - // make sure to keep that in sync. - - // First, unchanged zones. - for zone_unchanged in modified.zones_unchanged() { - add_zone_record( - format!("{UNCHANGED_PREFIX}{ZONE_INDENT}"), - &zone_unchanged.zone_before, - s2, - ); - } - - // Then, removed zones. - for zone in modified.zones_removed() { - add_zone_record_with_status( - format!("{REMOVED_PREFIX}{ZONE_INDENT}"), - zone, - REMOVED, - s2, - ); - } - - // Then, modified zones. - for zone_modified in modified.zones_modified() { - add_modified_zone_records(zone_modified, s2); - } - - // Finally, added zones. - for zone in modified.zones_added() { - add_zone_record_with_status( - format!("{ADDED_PREFIX}{ZONE_INDENT}"), - &zone.clone().into(), - ADDED, - s2, - ); - } - - // If no rows were pushed, add a row indicating that for this sled. - if s2.is_empty() { - s2.push_nested_heading( - SectionSpacing::Never, - format!( - "{UNCHANGED_PREFIX}{ZONE_HEAD_INDENT}\ - {NO_ZONES_PARENS}" - ), - ); - } - }); - } - - /// Add a zone record to this section. - /// - /// This is the meat-and-potatoes of the diff display. - fn add_zone_record( - first_column: String, - zone: &BlueprintOrCollectionZoneConfig, - section: &mut StSectionBuilder, - ) { - section.push_record(vec![ - first_column, - zone.kind().to_string(), - zone.id().to_string(), - zone.disposition().to_string(), - zone.underlay_address().to_string(), - ]); - } - - fn add_zone_record_with_status( - first_column: String, - zone: &BlueprintOrCollectionZoneConfig, - status: &str, - section: &mut StSectionBuilder, - ) { - section.push_record(vec![ - first_column, - zone.kind().to_string(), - zone.id().to_string(), - zone.disposition().to_string(), - zone.underlay_address().to_string(), - status.to_string(), - ]); - } - - /// Add a change table for the zone to the section. - /// - /// For diffs, this contains a table of changes between two zone - /// records. - fn add_modified_zone_records( - modified: &DiffZoneCommon, - section: &mut StSectionBuilder, - ) { - // Negative record for the before. - let before = &modified.zone_before; - let after = &modified.zone_after; - - // Before record. - add_zone_record_with_status( - format!("{REMOVED_PREFIX}{ZONE_INDENT}"), - &before, - MODIFIED, - section, - ); - - let mut what_changed = Vec::new(); - if !before.is_zone_type_equal(&after.zone_type) { - what_changed.push(ZONE_TYPE_CONFIG); - } - if before.disposition() != after.disposition { - what_changed.push(DISPOSITION); - } - if before.underlay_address() != after.underlay_address { - what_changed.push(UNDERLAY_IP); - } - debug_assert!( - !what_changed.is_empty(), - "at least something should have changed:\n\ - before = {before:#?}\n\ - after = {after:#?}" - ); - - let record = vec![ - format!("{ADDED_PREFIX}{ZONE_INDENT}"), - // First two columns of data are skipped over since they're - // always the same (verified at diff construction time). - format!(" {SUB_NOT_LAST}"), - "".to_string(), - after.disposition.to_string(), - after.underlay_address.to_string(), - ]; - section.push_record(record); - - section.push_spanned_row(format!( - "{MODIFIED_PREFIX}{ZONE_INDENT} \ - {SUB_LAST} changed: {}", - what_changed.join(", "), - )); - } - - #[derive(Copy, Clone, Debug)] - enum WholeSledKind { - Removed, - Added, - Unchanged, - } - - impl WholeSledKind { - fn prefix(self) -> char { - match self { - WholeSledKind::Removed => REMOVED_PREFIX, - WholeSledKind::Added => ADDED_PREFIX, - WholeSledKind::Unchanged => UNCHANGED_PREFIX, - } - } - - fn status(self) -> Option<&'static str> { - match self { - WholeSledKind::Removed => Some(REMOVED), - WholeSledKind::Added => Some(ADDED), - WholeSledKind::Unchanged => None, - } - } - } - - // Apply settings for a table which has top-to-bottom rows, and a first - // column with indents. - fn apply_linear_table_settings(table: &mut Table) { - table.with(Style::empty()).with(Padding::zero()).with( - Modify::new(Columns::single(1)) - // Add an padding on the right of the label column to make the - // table visually distinctive. - .with(Padding::new(0, 2, 0, 0)), - ); - } - - // --- - // Heading and other definitions - // --- - - // This aligns the heading with the first column of actual text. - const H1_INDENT: &str = " "; - const SLED_HEAD_INDENT: &str = " "; - const SLED_INDENT: &str = " "; - const ZONE_HEAD_INDENT: &str = " "; - // Due to somewhat mysterious reasons with how padding works with tabled, - // this needs to be 3 columns wide rather than 4. - const ZONE_INDENT: &str = " "; - const METADATA_INDENT: &str = " "; - const METADATA_DIFF_INDENT: &str = " "; - - const ADDED_PREFIX: char = '+'; - const REMOVED_PREFIX: char = '-'; - const MODIFIED_PREFIX: char = '*'; - const UNCHANGED_PREFIX: char = ' '; - const WARNING_PREFIX: char = '!'; - - const ARROW: &str = "->"; - const SUB_NOT_LAST: &str = "├─"; - const SUB_LAST: &str = "└─"; - - const ZONE_TYPE: &str = "zone type"; - const ZONE_ID: &str = "zone ID"; - const DISPOSITION: &str = "disposition"; - const UNDERLAY_IP: &str = "underlay IP"; - const ZONE_TYPE_CONFIG: &str = "zone type config"; - const STATUS: &str = "status"; - const REMOVED_SLEDS_HEADING: &str = "REMOVED SLEDS"; - const MODIFIED_SLEDS_HEADING: &str = "MODIFIED SLEDS"; - const UNCHANGED_SLEDS_HEADING: &str = "UNCHANGED SLEDS"; - const ADDED_SLEDS_HEADING: &str = "ADDED SLEDS"; - const REMOVED: &str = "removed"; - const ADDED: &str = "added"; - const MODIFIED: &str = "modified"; - - const METADATA_HEADING: &str = "METADATA"; - const CREATED_BY: &str = "created by"; - const CREATED_AT: &str = "created at"; - const INTERNAL_DNS_VERSION: &str = "internal DNS version"; - const EXTERNAL_DNS_VERSION: &str = "external DNS version"; - const COMMENT: &str = "comment"; - - const UNCHANGED_PARENS: &str = "(unchanged)"; - const NO_ZONES_PARENS: &str = "(no zones)"; - const NONE_PARENS: &str = "(none)"; - const NOT_PRESENT_IN_COLLECTION_PARENS: &str = - "(not present in collection)"; - - fn header_row() -> Vec { - vec![ - // First column is so that the header border aligns with the ZONE - // TABLE section header. - SLED_INDENT.to_string(), - ZONE_TYPE.to_string(), - ZONE_ID.to_string(), - DISPOSITION.to_string(), - UNDERLAY_IP.to_string(), - ] - } - - fn diff_header_row() -> Vec { - vec![ - // First column is so that the header border aligns with the ZONE - // TABLE section header. - SLED_HEAD_INDENT.to_string(), - ZONE_TYPE.to_string(), - ZONE_ID.to_string(), - DISPOSITION.to_string(), - UNDERLAY_IP.to_string(), - STATUS.to_string(), - ] - } - - pub(super) fn metadata_heading() -> String { - format!("{METADATA_HEADING}:") - } - - pub(super) fn metadata_diff_heading() -> String { - format!("{H1_INDENT}{METADATA_HEADING}:") - } - - fn sleds_heading(prefix: char, heading: &'static str) -> String { - format!("{prefix}{SLED_HEAD_INDENT}{heading}:") - } - - fn removed_sleds_heading() -> String { - sleds_heading(UNCHANGED_PREFIX, REMOVED_SLEDS_HEADING) - } - - fn added_sleds_heading() -> String { - sleds_heading(UNCHANGED_PREFIX, ADDED_SLEDS_HEADING) - } - - fn modified_sleds_heading() -> String { - sleds_heading(UNCHANGED_PREFIX, MODIFIED_SLEDS_HEADING) - } - - fn unchanged_sleds_heading() -> String { - sleds_heading(UNCHANGED_PREFIX, UNCHANGED_SLEDS_HEADING) - } - - fn metadata_table_internal_dns() -> String { - linear_table_label(&INTERNAL_DNS_VERSION) - } - - fn metadata_table_external_dns() -> String { - linear_table_label(&EXTERNAL_DNS_VERSION) - } - - fn linear_table_label(value: &dyn fmt::Display) -> String { - format!("{value}:") - } - - fn linear_table_modified( - before: &dyn fmt::Display, - after: &dyn fmt::Display, - ) -> String { - format!("{before} {ARROW} {after}") - } - - fn linear_table_unchanged(value: &dyn fmt::Display) -> String { - format!("{value} {UNCHANGED_PARENS}") - } -} diff --git a/nexus/types/src/deployment/blueprint_diff.rs b/nexus/types/src/deployment/blueprint_diff.rs new file mode 100644 index 0000000000..c3c28a474c --- /dev/null +++ b/nexus/types/src/deployment/blueprint_diff.rs @@ -0,0 +1,854 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Types helpful for diffing [`Blueprints`]. + +use super::blueprint_display::{ + constants::*, linear_table_modified, linear_table_unchanged, BpDiffState, + BpGeneration, BpOmicronZonesSubtableSchema, BpPhysicalDisksSubtableSchema, + BpSledSubtable, BpSledSubtableData, BpSledSubtableRow, KvListWithHeading, + KvPair, +}; +use super::zone_sort_key; +use omicron_common::api::external::Generation; +use omicron_common::disk::DiskIdentity; +use omicron_uuid_kinds::OmicronZoneUuid; +use omicron_uuid_kinds::SledUuid; +use sled_agent_client::ZoneKind; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt; + +use crate::deployment::{ + BlueprintMetadata, BlueprintOrCollectionDisksConfig, + BlueprintOrCollectionZoneConfig, BlueprintOrCollectionZonesConfig, + BlueprintPhysicalDisksConfig, BlueprintZoneConfig, + BlueprintZoneDisposition, BlueprintZonesConfig, DiffBeforeMetadata, + ZoneSortKey, +}; + +/// Diffs for omicron zones on a given sled with a given `BpDiffState` +#[derive(Debug)] +pub struct BpDiffZoneDetails { + pub generation_before: Option, + pub generation_after: Option, + pub zones: Vec, +} + +impl BpSledSubtableData for BpDiffZoneDetails { + fn bp_generation(&self) -> BpGeneration { + BpGeneration::Diff { + before: self.generation_before, + after: self.generation_after, + } + } + + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator { + self.zones.iter().map(move |zone| { + BpSledSubtableRow::new( + state, + vec![ + zone.kind().to_string(), + zone.id().to_string(), + zone.disposition().to_string(), + zone.underlay_address().to_string(), + ], + ) + }) + } +} + +/// A modified omicron zone +/// +/// A zone is considered modified if its `disposition` changes. All +/// modifications to other fields are considered errors, and will be recorded +/// as such. +#[derive(Debug)] +pub struct ModifiedZone { + pub prior_disposition: BlueprintZoneDisposition, + pub zone: BlueprintOrCollectionZoneConfig, +} + +impl ZoneSortKey for ModifiedZone { + fn kind(&self) -> ZoneKind { + self.zone.kind() + } + + fn id(&self) -> OmicronZoneUuid { + self.zone.id() + } +} + +impl ModifiedZone { + #[allow(clippy::result_large_err)] + pub fn new( + before: BlueprintOrCollectionZoneConfig, + after: BlueprintZoneConfig, + ) -> Result { + // Do we have any errors? If so, create a "reason" string. + let mut reason = String::new(); + if before.kind() != after.kind() { + let msg = format!( + "mismatched zone kind: before: {}, after: {}\n", + before.kind(), + after.kind() + ); + reason.push_str(&msg); + } + if before.underlay_address() != after.underlay_address { + let msg = format!( + "mismatched underlay address: before: {}, after: {}\n", + before.underlay_address(), + after.underlay_address + ); + reason.push_str(&msg); + } + if !before.is_zone_type_equal(&after.zone_type) { + let msg = format!( + "mismatched zone type: after: {:#?}\n", + after.zone_type + ); + reason.push_str(&msg); + } + if reason.is_empty() { + Ok(ModifiedZone { + prior_disposition: before.disposition(), + zone: after.into(), + }) + } else { + Err(BpDiffZoneError { + zone_before: before, + zone_after: after.into(), + reason, + }) + } + } +} + +/// Details of modified zones on a given sled +#[derive(Debug)] +pub struct BpDiffZonesModified { + pub generation_before: Generation, + pub generation_after: Generation, + pub zones: Vec, +} + +impl BpSledSubtableData for BpDiffZonesModified { + fn bp_generation(&self) -> BpGeneration { + BpGeneration::Diff { + before: Some(self.generation_before), + after: Some(self.generation_after), + } + } + + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator { + self.zones.iter().map(move |zone| { + let disposition = format!( + "{} {ARROW} {}", + zone.prior_disposition, + zone.zone.disposition() + ); + BpSledSubtableRow::new( + state, + vec![ + zone.zone.kind().to_string(), + zone.zone.id().to_string(), + disposition, + zone.zone.underlay_address().to_string(), + ], + ) + }) + } +} + +#[derive(Debug)] +/// Errors arising from illegally modified zone fields +pub struct BpDiffZoneErrors { + pub generation_before: Generation, + pub generation_after: Generation, + pub errors: Vec, +} + +#[derive(Debug)] +pub struct BpDiffZoneError { + pub zone_before: BlueprintOrCollectionZoneConfig, + pub zone_after: BlueprintOrCollectionZoneConfig, + pub reason: String, +} + +/// All known zones across all known sleds, their various states, and errors +#[derive(Debug, Default)] +pub struct BpDiffZones { + pub added: BTreeMap, + pub removed: BTreeMap, + pub unchanged: BTreeMap, + pub modified: BTreeMap, + pub errors: BTreeMap, +} + +impl BpDiffZones { + pub fn new( + before: BTreeMap, + mut after: BTreeMap, + ) -> Self { + let mut diffs = BpDiffZones::default(); + for (sled_id, before_zones) in before { + let before_generation = before_zones.generation(); + let mut removed = vec![]; + if let Some(after_zones) = after.remove(&sled_id) { + let after_generation = after_zones.generation; + let mut unchanged = vec![]; + let mut modified = vec![]; + let mut errors = vec![]; + let mut added = vec![]; + + // Compare `before_zones` and `after_zones` to look + // for additions, deletions, modifications, and errors. + let before_by_id: BTreeMap<_, BlueprintOrCollectionZoneConfig> = + before_zones.zones().map(|z| (z.id(), z)).collect(); + let mut after_by_id: BTreeMap<_, BlueprintZoneConfig> = + after_zones.zones.into_iter().map(|z| (z.id, z)).collect(); + + for (zone_id, zone_before) in before_by_id { + if let Some(zone_after) = after_by_id.remove(&zone_id) { + // Are the zones equal? + if zone_before == zone_after { + unchanged.push(zone_after.into()); + } else { + // The zones are different. They are only allowed to differ in terms + // of `disposition`, otherwise we have an error. + match ModifiedZone::new(zone_before, zone_after) { + Ok(modified_zone) => { + modified.push(modified_zone) + } + Err(error) => errors.push(error), + } + } + } else { + // This zone doesn't exist in `zone_after` so it must have + // been removed. + removed.push(zone_before); + } + } + // Any remaining zones in `after_by_id` are newly added + for (_, zone_after) in after_by_id { + added.push(zone_after.into()); + } + + // Add all records to `diffs` that come from either `before` or `after` + // for this `sled_id`. + if !unchanged.is_empty() { + unchanged.sort_unstable_by_key(zone_sort_key); + diffs.unchanged.insert( + sled_id, + BpDiffZoneDetails { + generation_before: Some(before_generation), + generation_after: Some(after_generation), + zones: unchanged, + }, + ); + } + if !removed.is_empty() { + removed.sort_unstable_by_key(zone_sort_key); + diffs.removed.insert( + sled_id, + BpDiffZoneDetails { + generation_before: Some(before_generation), + generation_after: Some(after_generation), + zones: removed, + }, + ); + } + if !added.is_empty() { + added.sort_unstable_by_key(zone_sort_key); + diffs.added.insert( + sled_id, + BpDiffZoneDetails { + generation_before: Some(before_generation), + generation_after: Some(after_generation), + zones: added, + }, + ); + } + if !modified.is_empty() { + modified.sort_unstable_by_key(zone_sort_key); + diffs.modified.insert( + sled_id, + BpDiffZonesModified { + generation_before: before_generation, + generation_after: after_generation, + zones: modified, + }, + ); + } + if !errors.is_empty() { + diffs.errors.insert( + sled_id, + BpDiffZoneErrors { + generation_before: before_generation, + generation_after: after_generation, + errors, + }, + ); + } + } else { + // No `after_zones` for this `sled_id`, so `before_zones` are removed + assert!(removed.is_empty()); + for zone in before_zones.zones() { + removed.push(zone); + } + + if !removed.is_empty() { + removed.sort_unstable_by_key(zone_sort_key); + diffs.removed.insert( + sled_id, + BpDiffZoneDetails { + generation_before: Some(before_generation), + generation_after: None, + zones: removed, + }, + ); + } + } + } + + // Any sleds remaining in `after` have just been added, since we remove + // sleds from `after`, that were also in `before`, in the above loop. + for (sled_id, after_zones) in after { + if !after_zones.zones.is_empty() { + diffs.added.insert( + sled_id, + BpDiffZoneDetails { + generation_before: None, + generation_after: Some(after_zones.generation), + zones: after_zones + .zones + .into_iter() + .map(|z| z.into()) + .collect(), + }, + ); + } + } + + diffs + } + + /// Return a [`BpSledSubtable`] for the given `sled_id` + /// + /// We collate all the data from each category to produce a single table. + /// The order is: + /// + /// 1. Unchanged + /// 2. Removed + /// 3. Modified + /// 4. Added + /// + /// The idea behind the order is to (a) group all changes together + /// and (b) put changes towards the bottom, so people have to scroll + /// back less. + /// + /// Errors are printed in a more freeform manner after the table is + /// displayed. + pub fn to_bp_sled_subtable( + &self, + sled_id: &SledUuid, + ) -> Option { + let mut generation = BpGeneration::Diff { before: None, after: None }; + let mut rows = vec![]; + if let Some(diff) = self.unchanged.get(sled_id) { + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Unchanged)); + } + if let Some(diff) = self.removed.get(sled_id) { + // Generations never vary for the same sled, so this is harmless + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Removed)); + } + + if let Some(diff) = self.modified.get(sled_id) { + // Generations never vary for the same sled, so this is harmless + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Modified)); + } + + if let Some(diff) = self.added.get(sled_id) { + // Generations never vary for the same sled, so this is harmless + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Added)); + } + + if rows.is_empty() { + None + } else { + Some(BpSledSubtable::new( + BpOmicronZonesSubtableSchema {}, + generation, + rows, + )) + } + } +} + +#[derive(Debug)] +pub struct DiffPhysicalDisksDetails { + // Disks that come from inventory don't have generation numbers + pub before_generation: Option, + + // Disks that are removed don't have "after" generation numbers + pub after_generation: Option, + + // Disks added, removed, or unmodified + pub disks: BTreeSet, +} + +impl BpSledSubtableData for DiffPhysicalDisksDetails { + fn bp_generation(&self) -> BpGeneration { + BpGeneration::Diff { + before: self.before_generation, + after: self.after_generation, + } + } + + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator { + self.disks.iter().map(move |d| { + BpSledSubtableRow::new( + state, + vec![d.vendor.clone(), d.model.clone(), d.serial.clone()], + ) + }) + } +} + +#[derive(Debug, Default)] +pub struct BpDiffPhysicalDisks { + pub added: BTreeMap, + pub removed: BTreeMap, + pub unchanged: BTreeMap, +} + +impl BpDiffPhysicalDisks { + pub fn new( + before: BTreeMap, + mut after: BTreeMap, + ) -> Self { + let mut diffs = BpDiffPhysicalDisks::default(); + for (sled_id, before_disks) in before { + let before_generation = before_disks.generation(); + if let Some(after_disks) = after.remove(&sled_id) { + let after_generation = Some(after_disks.generation); + let a: BTreeSet = + after_disks.disks.into_iter().map(|d| d.identity).collect(); + let b = before_disks.disks(); + let added: BTreeSet<_> = a.difference(&b).cloned().collect(); + let removed: BTreeSet<_> = b.difference(&a).cloned().collect(); + let unchanged: BTreeSet<_> = + a.intersection(&b).cloned().collect(); + if !added.is_empty() { + diffs.added.insert( + sled_id, + DiffPhysicalDisksDetails { + before_generation, + after_generation, + disks: added, + }, + ); + } + if !removed.is_empty() { + diffs.removed.insert( + sled_id, + DiffPhysicalDisksDetails { + before_generation, + after_generation, + disks: removed, + }, + ); + } + if !unchanged.is_empty() { + diffs.unchanged.insert( + sled_id, + DiffPhysicalDisksDetails { + before_generation, + after_generation, + disks: unchanged, + }, + ); + } + } else { + diffs.removed.insert( + sled_id, + DiffPhysicalDisksDetails { + before_generation, + after_generation: None, + disks: before_disks.disks().into_iter().collect(), + }, + ); + } + } + + // Any sleds remaining in `after` have just been added, since we remove + // sleds from `after`, that were also in `before`, in the above loop. + for (sled_id, after_disks) in after { + let added: BTreeSet = + after_disks.disks.into_iter().map(|d| d.identity).collect(); + if !added.is_empty() { + diffs.added.insert( + sled_id, + DiffPhysicalDisksDetails { + before_generation: None, + after_generation: Some(after_disks.generation), + disks: added, + }, + ); + } + } + + diffs + } + + /// Return a [`BpSledSubtable`] for the given `sled_id` + pub fn to_bp_sled_subtable( + &self, + sled_id: &SledUuid, + ) -> Option { + let mut generation = BpGeneration::Diff { before: None, after: None }; + let mut rows = vec![]; + if let Some(diff) = self.unchanged.get(sled_id) { + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Unchanged)); + } + if let Some(diff) = self.removed.get(sled_id) { + // Generations never vary for the same sled, so this is harmless + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Removed)); + } + + if let Some(diff) = self.added.get(sled_id) { + // Generations never vary for the same sled, so this is harmless + generation = diff.bp_generation(); + rows.extend(diff.rows(BpDiffState::Added)); + } + + if rows.is_empty() { + None + } else { + Some(BpSledSubtable::new( + BpPhysicalDisksSubtableSchema {}, + generation, + rows, + )) + } + } +} + +/// Summarizes the differences between two blueprints +#[derive(Debug)] +pub struct BlueprintDiff { + pub before_meta: DiffBeforeMetadata, + pub after_meta: BlueprintMetadata, + pub zones: BpDiffZones, + pub physical_disks: BpDiffPhysicalDisks, + pub sleds_added: BTreeSet, + pub sleds_removed: BTreeSet, + pub sleds_unchanged: BTreeSet, + pub sleds_modified: BTreeSet, +} + +impl BlueprintDiff { + /// Build a diff with the provided contents, verifying that the provided + /// data is valid. + pub fn new( + before_meta: DiffBeforeMetadata, + before_zones: BTreeMap, + after_meta: BlueprintMetadata, + after_zones: BTreeMap, + before_disks: BTreeMap, + after_disks: BTreeMap, + ) -> Self { + let before_sleds: BTreeSet<_> = + before_zones.keys().chain(before_disks.keys()).collect(); + let after_sleds: BTreeSet<_> = + after_zones.keys().chain(after_disks.keys()).collect(); + let all_sleds: BTreeSet<_> = + before_sleds.union(&after_sleds).map(|&sled_id| *sled_id).collect(); + + // All sleds that have zones or disks in `after_*`, but not `before_*` + // have been added. + let sleds_added: BTreeSet<_> = after_sleds + .difference(&before_sleds) + .map(|&sled_id| *sled_id) + .collect(); + + // All sleds that have zones or disks in `before_*`, but not `after_*` + // have been removed. + let sleds_removed: BTreeSet<_> = before_sleds + .difference(&after_sleds) + .map(|&sled_id| *sled_id) + .collect(); + + let zones = BpDiffZones::new(before_zones, after_zones); + let physical_disks = + BpDiffPhysicalDisks::new(before_disks, after_disks); + + // Sleds that haven't been added or removed are either unchanged or + // modified. + let sleds_unchanged_or_modified: BTreeSet<_> = all_sleds + .iter() + .filter(|&sled_id| { + !sleds_added.contains(sled_id) + && !sleds_removed.contains(sled_id) + }) + .map(|s| *s) + .collect(); + + // Sleds are modified if any zones or disks on those sleds are anything + // other than unchanged. + let mut sleds_modified = sleds_unchanged_or_modified.clone(); + sleds_modified.retain(|sled_id| { + physical_disks.added.contains_key(sled_id) + || physical_disks.removed.contains_key(sled_id) + || zones.added.contains_key(sled_id) + || zones.removed.contains_key(sled_id) + || zones.modified.contains_key(sled_id) + || zones.errors.contains_key(sled_id) + }); + + // The rest of the sleds must be unchanged. + let unchanged_sleds: BTreeSet<_> = sleds_unchanged_or_modified + .difference(&sleds_modified) + .map(|sled_id| *sled_id) + .collect(); + + BlueprintDiff { + before_meta, + after_meta, + zones, + physical_disks, + sleds_added, + sleds_removed, + sleds_unchanged: unchanged_sleds, + sleds_modified, + } + } + + /// Return a struct that can be used to display the diff. + pub fn display(&self) -> BlueprintDiffDisplay<'_> { + BlueprintDiffDisplay::new(self) + } +} + +/// Wrapper to allow a [`BlueprintDiff`] to be displayed. +/// +/// Returned by [`BlueprintDiff::display()`]. +#[derive(Clone, Debug)] +#[must_use = "this struct does nothing unless displayed"] +pub struct BlueprintDiffDisplay<'diff> { + pub diff: &'diff BlueprintDiff, + // TODO: add colorization with a stylesheet +} + +impl<'diff> BlueprintDiffDisplay<'diff> { + #[inline] + fn new(diff: &'diff BlueprintDiff) -> Self { + Self { diff } + } + + pub fn make_metadata_diff_table(&self) -> KvListWithHeading { + let diff = self.diff; + let mut kv = vec![]; + match &diff.before_meta { + DiffBeforeMetadata::Collection { .. } => { + // Collections don't have DNS versions, so this is new. + kv.push(KvPair::new( + BpDiffState::Added, + INTERNAL_DNS_VERSION, + linear_table_modified( + &NOT_PRESENT_IN_COLLECTION_PARENS, + &diff.after_meta.internal_dns_version, + ), + )); + kv.push(KvPair::new( + BpDiffState::Added, + EXTERNAL_DNS_VERSION, + linear_table_modified( + &NOT_PRESENT_IN_COLLECTION_PARENS, + &diff.after_meta.external_dns_version, + ), + )); + } + DiffBeforeMetadata::Blueprint(before) => { + if before.internal_dns_version + != diff.after_meta.internal_dns_version + { + kv.push(KvPair::new( + BpDiffState::Modified, + INTERNAL_DNS_VERSION, + linear_table_modified( + &before.internal_dns_version, + &diff.after_meta.internal_dns_version, + ), + )); + } else { + kv.push(KvPair::new( + BpDiffState::Unchanged, + INTERNAL_DNS_VERSION, + linear_table_unchanged(&before.internal_dns_version), + )); + }; + + if before.external_dns_version + != diff.after_meta.external_dns_version + { + kv.push(KvPair::new( + BpDiffState::Modified, + EXTERNAL_DNS_VERSION, + linear_table_modified( + &before.external_dns_version, + &diff.after_meta.external_dns_version, + ), + )); + } else { + kv.push(KvPair::new( + BpDiffState::Unchanged, + EXTERNAL_DNS_VERSION, + linear_table_unchanged(&before.external_dns_version), + )); + }; + } + } + + KvListWithHeading::new(METADATA_HEADING, kv) + } + + /// Write out physical disk and zone tables for a given `sled_id` + fn write_tables( + &self, + f: &mut fmt::Formatter<'_>, + sled_id: &SledUuid, + ) -> fmt::Result { + // Write the physical disks table if it exists + if let Some(table) = + self.diff.physical_disks.to_bp_sled_subtable(sled_id) + { + writeln!(f, "{table}\n")?; + } + + // Write the zones table if it exists + if let Some(table) = self.diff.zones.to_bp_sled_subtable(sled_id) { + writeln!(f, "{table}\n")?; + } + + Ok(()) + } +} + +impl<'diff> fmt::Display for BlueprintDiffDisplay<'diff> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let diff = self.diff; + + // Print things differently based on whether the diff is between a + // collection and a blueprint, or a blueprint and a blueprint. + match &diff.before_meta { + DiffBeforeMetadata::Collection { id } => { + writeln!( + f, + "from: collection {}\n\ + to: blueprint {}", + id, diff.after_meta.id, + )?; + } + DiffBeforeMetadata::Blueprint(before) => { + writeln!( + f, + "from: blueprint {}\n\ + to: blueprint {}\n", + before.id, diff.after_meta.id + )?; + } + } + + // Write out sled information + // + // The order is: + // + // 1. Unchanged + // 2. Removed + // 3. Modified + // 4. Added + // 5. Errors + // + // The idea behind the order is to (a) group all changes together + // and (b) put changes towards the bottom, so people have to scroll + // back less. + // + // We put errors at the bottom to ensure they are seen immediately. + + // Write out tables for unchanged sleds + if !diff.sleds_unchanged.is_empty() { + writeln!(f, " UNCHANGED SLEDS:\n")?; + for sled_id in &diff.sleds_unchanged { + writeln!(f, " sled {sled_id}:\n")?; + self.write_tables(f, sled_id)?; + } + } + + // Write out tables for removed sleds + if !diff.sleds_removed.is_empty() { + writeln!(f, " REMOVED SLEDS:\n")?; + for sled_id in &diff.sleds_removed { + writeln!(f, " sled {sled_id}:\n")?; + self.write_tables(f, sled_id)?; + } + } + + // Write out tables for modified sleds + if !diff.sleds_modified.is_empty() { + writeln!(f, " MODIFIED SLEDS:\n")?; + for sled_id in &diff.sleds_modified { + writeln!(f, " sled {sled_id}:\n")?; + self.write_tables(f, sled_id)?; + } + } + + // Write out tables for added sleds + if !diff.sleds_added.is_empty() { + writeln!(f, " ADDED SLEDS:\n")?; + for sled_id in &diff.sleds_added { + writeln!(f, " sled {sled_id}:\n")?; + self.write_tables(f, sled_id)?; + } + } + + // Write out zone errors. + if !diff.zones.errors.is_empty() { + writeln!(f, "ERRORS:")?; + for (sled_id, errors) in &diff.zones.errors { + writeln!(f, "\n sled {sled_id}\n")?; + writeln!( + f, + " zone diff errors: before gen {}, after gen {}\n", + errors.generation_before, errors.generation_after + )?; + + for err in &errors.errors { + writeln!(f, " zone id: {}", err.zone_before.id())?; + writeln!(f, " reason: {}", err.reason)?; + } + } + } + + // Write out metadata diff table + writeln!(f, "{}", self.make_metadata_diff_table())?; + + Ok(()) + } +} diff --git a/nexus/types/src/deployment/blueprint_display.rs b/nexus/types/src/deployment/blueprint_display.rs new file mode 100644 index 0000000000..d5dc5e3074 --- /dev/null +++ b/nexus/types/src/deployment/blueprint_display.rs @@ -0,0 +1,331 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Types helpful for rendering [`Blueprints`]. + +use omicron_common::api::external::Generation; +use std::fmt; + +pub mod constants { + pub(super) const ADDED_PREFIX: char = '+'; + pub(super) const REMOVED_PREFIX: char = '-'; + pub(super) const MODIFIED_PREFIX: char = '*'; + pub(super) const UNCHANGED_PREFIX: char = ' '; + + pub const ARROW: &str = "->"; + pub const METADATA_HEADING: &str = "METADATA"; + pub const CREATED_BY: &str = "created by"; + pub const CREATED_AT: &str = "created at"; + pub const INTERNAL_DNS_VERSION: &str = "internal DNS version"; + pub const EXTERNAL_DNS_VERSION: &str = "external DNS version"; + pub const COMMENT: &str = "comment"; + + pub const UNCHANGED_PARENS: &str = "(unchanged)"; + pub const NONE_PARENS: &str = "(none)"; + pub const NOT_PRESENT_IN_COLLECTION_PARENS: &str = + "(not present in collection)"; +} +use constants::*; + +/// The state of a sled or resource (e.g. zone or physical disk) in this +/// blueprint, with regards to the parent blueprint +#[derive(Debug, Clone, Copy)] +pub enum BpDiffState { + Unchanged, + Removed, + Modified, + Added, +} + +impl BpDiffState { + pub fn prefix(&self) -> char { + match self { + BpDiffState::Unchanged => UNCHANGED_PREFIX, + BpDiffState::Removed => REMOVED_PREFIX, + BpDiffState::Modified => MODIFIED_PREFIX, + BpDiffState::Added => ADDED_PREFIX, + } + } +} + +impl fmt::Display for BpDiffState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + BpDiffState::Unchanged => "UNCHANGED", + BpDiffState::Removed => "REMOVED", + BpDiffState::Modified => "MODIFIED", + BpDiffState::Added => "ADDED", + }; + write!(f, "{s}") + } +} + +/// A wrapper aound generation numbers for blueprints or blueprint diffs +#[derive(Debug, Clone, Copy)] +pub enum BpGeneration { + // A value in a single blueprint + Value(Generation), + + // A diff between two blueprints + Diff { before: Option, after: Option }, +} + +impl fmt::Display for BpGeneration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BpGeneration::Value(generation) => { + write!(f, "at generation {generation}") + } + BpGeneration::Diff { before: None, after: Some(after) } => { + write!(f, "at generation {after}") + } + BpGeneration::Diff { before: Some(before), after: None } => { + write!(f, "from generation {before}") + } + BpGeneration::Diff { before: Some(before), after: Some(after) } => { + if before == after { + write!(f, "at generation {after}") + } else { + write!(f, "generation {before} -> {after}") + } + } + BpGeneration::Diff { before: None, after: None } => { + write!(f, "Error: unknown generation") + } + } + } +} + +/// A row in a [`BpSledSubtable`] +pub struct BpSledSubtableRow { + state: BpDiffState, + columns: Vec, +} + +impl BpSledSubtableRow { + pub fn new(state: BpDiffState, columns: Vec) -> Self { + BpSledSubtableRow { state, columns } + } +} + +/// Metadata about all instances of specific type of [`BpSledSubtable`], +/// such as omicron zones or physical disks. +pub trait BpSledSubtableSchema { + fn table_name(&self) -> &'static str; + fn column_names(&self) -> &'static [&'static str]; +} + +// Provide data specific to an instance of a [`BpSledSubtable`] +pub trait BpSledSubtableData { + fn bp_generation(&self) -> BpGeneration; + fn rows( + &self, + state: BpDiffState, + ) -> impl Iterator; +} + +/// A table specific to a sled resource, such as a zone or disk. +/// `BpSledSubtable`s are always nested under [`BpSledTable`]s. +pub struct BpSledSubtable { + table_name: &'static str, + column_names: &'static [&'static str], + generation: BpGeneration, + rows: Vec, +} + +impl BpSledSubtable { + pub fn new( + schema: impl BpSledSubtableSchema, + generation: BpGeneration, + rows: Vec, + ) -> BpSledSubtable { + BpSledSubtable { + table_name: schema.table_name(), + column_names: schema.column_names(), + generation, + rows, + } + } + + /// Compute the max column widths based on the contents of `column_names` + // and `rows`. + fn column_widths(&self) -> Vec { + let mut widths: Vec = + self.column_names.iter().map(|s| s.len()).collect(); + + for row in &self.rows { + assert_eq!(row.columns.len(), widths.len()); + for (i, s) in row.columns.iter().enumerate() { + widths[i] = usize::max(s.len(), widths[i]); + } + } + + widths + } +} + +const SUBTABLE_INDENT: usize = 4; +const COLUMN_GAP: usize = 3; + +impl fmt::Display for BpSledSubtable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let widths = self.column_widths(); + let mut total_width = + widths.iter().fold(0, |acc, i| acc + i + COLUMN_GAP); + total_width -= COLUMN_GAP; + + // Write the name of the subtable + writeln!( + f, + "{: &'static str { + "physical disks" + } + + fn column_names(&self) -> &'static [&'static str] { + &["vendor", "model", "serial"] + } +} + +/// The [`BpSledSubtable`] schema for omicron zones +pub struct BpOmicronZonesSubtableSchema {} +impl BpSledSubtableSchema for BpOmicronZonesSubtableSchema { + fn table_name(&self) -> &'static str { + "omicron zones" + } + fn column_names(&self) -> &'static [&'static str] { + &["zone type", "zone id", "disposition", "underlay IP"] + } +} + +// An entry in a [`KvListWithHeading`] +#[derive(Debug)] +pub struct KvPair { + state: BpDiffState, + key: String, + val: String, +} + +impl KvPair { + pub fn new_unchanged, S2: Into>( + key: S1, + val: S2, + ) -> KvPair { + KvPair { + state: BpDiffState::Unchanged, + key: key.into(), + val: val.into(), + } + } + + pub fn new, S2: Into>( + state: BpDiffState, + key: S1, + val: S2, + ) -> KvPair { + KvPair { state, key: key.into(), val: val.into() } + } +} + +// A top-to-bottom list of KV pairs with a heading +#[derive(Debug)] +pub struct KvListWithHeading { + heading: &'static str, + kv: Vec, +} + +impl KvListWithHeading { + pub fn new_unchanged, S2: Into>( + heading: &'static str, + kv: Vec<(S1, S2)>, + ) -> KvListWithHeading { + let kv = + kv.into_iter().map(|(k, v)| KvPair::new_unchanged(k, v)).collect(); + KvListWithHeading { heading, kv } + } + + pub fn new(heading: &'static str, kv: Vec) -> KvListWithHeading { + KvListWithHeading { heading, kv } + } + + /// Compute the max width of the keys for alignment purposes + fn max_key_width(&self) -> usize { + self.kv.iter().fold(0, |acc, kv| usize::max(acc, kv.key.len())) + } +} + +impl fmt::Display for KvListWithHeading { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Write the heading + writeln!(f, " {}:", self.heading)?; + + // Write the rows + let key_width = self.max_key_width() + 1; + for kv in &self.kv { + let prefix = kv.state.prefix(); + writeln!( + f, + "{prefix: String { + format!("{before} {ARROW} {after}") +} + +pub fn linear_table_unchanged(value: &dyn fmt::Display) -> String { + format!("{value} {UNCHANGED_PARENS}") +} diff --git a/nexus/types/src/lib.rs b/nexus/types/src/lib.rs index b6286c3f64..494573e834 100644 --- a/nexus/types/src/lib.rs +++ b/nexus/types/src/lib.rs @@ -34,4 +34,3 @@ pub mod external_api; pub mod identity; pub mod internal_api; pub mod inventory; -mod sectioned_table; diff --git a/nexus/types/src/sectioned_table.rs b/nexus/types/src/sectioned_table.rs deleted file mode 100644 index addb4c876e..0000000000 --- a/nexus/types/src/sectioned_table.rs +++ /dev/null @@ -1,357 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Support for tables with builtin sections. -//! -//! This could live in its own crate (within omicron, or even on crates.io), -//! but is here for now. - -use std::collections::HashSet; -use std::iter; - -use tabled::builder::Builder; -use tabled::grid::config::Border; -use tabled::settings::object::Columns; -use tabled::settings::object::Object; -use tabled::settings::object::Rows; -use tabled::settings::span::ColumnSpan; -use tabled::settings::Modify; -use tabled::settings::Padding; -use tabled::settings::Style; -use tabled::Table; - -/// A sectioned table. -/// -/// A sectioned table allows sections and subsections to be defined, with each -/// section having a title and a list of rows in that section. The section -/// headers and other rows can break standard table conventions. -/// -/// There are two kinds of special rows: -/// -/// 1. Headings: rows that span all columns. -/// 2. Spanned rows: also rows that span all columns, but not as headings. -/// -/// This builder does not currently automatically indent sections or records -- -/// that can be done in the future, though it has to be done with some care. -#[derive(Debug)] -pub(crate) struct StBuilder { - builder: Builder, - // Rows that are marked off with ---- on both sides. - header_rows: Vec, - // Heading rows that span all columns. - headings: Vec<(HeadingSpacing, usize)>, - // Other rows that span all columns. - spanned_rows: Vec, -} - -impl StBuilder { - pub(crate) fn new() -> Self { - let builder = Builder::new(); - - Self { - builder, - header_rows: Vec::new(), - headings: Vec::new(), - spanned_rows: Vec::new(), - } - } - - /// Adds a header row to the table. - /// - /// This row contains column titles, along with *two* initial columns of - /// padding. The border will extend to the first column but not the second - /// one. - pub(crate) fn push_header_row(&mut self, row: Vec) { - self.header_rows.push(self.builder.count_records()); - self.push_record(row); - } - - /// Adds a record to the table. - pub(crate) fn push_record(&mut self, row: Vec) { - self.builder.push_record(row); - } - - /// Makes a new section of the table. - /// - /// This section will not be added to the table unless at least one row is - /// added to it, either directly or via nested sections. - pub(crate) fn make_section( - &mut self, - spacing: SectionSpacing, - heading: String, - cb: impl FnOnce(&mut StSectionBuilder), - ) { - let mut section = StSectionBuilder::from_builder( - self, - spacing.resolve(self.headings.is_empty()), - heading, - ); - cb(&mut section); - section.finish_with_root(self); - } - - /// Does the final build to produce a [`Table`]. - pub(crate) fn build(mut self) -> Table { - // Insert a column between 0 and 1 to enable header borders to be - // properly aligned with the rest of the text. - self.builder.insert_column( - 1, - iter::repeat("").take(self.builder.count_records()), - ); - - let mut table = self.builder.build(); - table - .with(Style::blank()) - .with( - // Columns 0 and 1 (indent/gutter) should not have any border - // and padding. - Modify::new(Columns::new(0..=1)) - .with(Border::empty()) - .with(Padding::zero()), - ) - .with( - Modify::new(Columns::single(2)) - // Column 2 (first column of actual data) should not have - // left padding. - .with(Padding::new(0, 1, 0, 0)), - ) - .with( - Modify::new(Columns::last()) - // Rightmost column should have no border and padding. - .with(Border::empty()) - .with(Padding::zero()), - ); - apply_normal_row_settings( - &mut table, - self.header_rows - .iter() - .copied() - .chain(self.headings.iter().map(|(_, i)| *i)) - .chain(self.spanned_rows.iter().copied()) - .collect(), - ); - apply_header_row_settings(&mut table, &self.header_rows); - apply_heading_settings(&mut table, &self.headings); - apply_spanned_row_settings(&mut table, &self.spanned_rows); - - table - } -} - -/// A part of a sectioned table. -/// -/// Created by [`StBuilder::make_section`] or -/// [`StNestedBuilder::make_subsection`]. -#[derive(Debug)] -pub(crate) struct StSectionBuilder { - start_index: usize, - spacing: HeadingSpacing, - heading: String, - rows: Vec>, - // Indexes for special rows, stored as absolute indexes wrt the overall - // zone table (i.e. start_index + 1 + index in rows). - nested_headings: Vec<(HeadingSpacing, usize)>, - spanned_rows: Vec, -} - -impl StSectionBuilder { - fn from_builder( - builder: &StBuilder, - spacing: HeadingSpacing, - heading: String, - ) -> Self { - let start_index = builder.builder.count_records(); - Self { - start_index, - spacing, - heading, - rows: Vec::new(), - nested_headings: Vec::new(), - spanned_rows: Vec::new(), - } - } - - pub(crate) fn is_empty(&self) -> bool { - self.rows.is_empty() - } - - pub(crate) fn push_record(&mut self, row: Vec) { - self.rows.push(row); - } - - pub(crate) fn push_spanned_row(&mut self, row: String) { - self.spanned_rows.push(self.next_row()); - self.rows.push(vec![row]); - } - - pub(crate) fn push_nested_heading( - &mut self, - spacing: SectionSpacing, - heading: String, - ) { - self.nested_headings.push(( - spacing.resolve(self.nested_headings.is_empty()), - self.next_row(), - )); - self.rows.push(vec![heading]); - } - - /// Makes a new subsection of this section. - /// - /// This subsection will not be added to the table unless at least one row - /// is added to it, either directly or via nested sections. - pub(crate) fn make_subsection( - &mut self, - spacing: SectionSpacing, - heading: String, - cb: impl FnOnce(&mut Self), - ) { - let mut subsection = Self { - start_index: self.next_row(), - spacing: spacing.resolve(self.nested_headings.is_empty()), - heading, - rows: Vec::new(), - nested_headings: Vec::new(), - spanned_rows: Vec::new(), - }; - cb(&mut subsection); - subsection.finish_with_parent(self); - } - - fn next_row(&self) -> usize { - // +1 to account for the heading row. - self.start_index + 1 + self.rows.len() - } - - fn finish_with_root(self, root: &mut StBuilder) { - if !self.rows.is_empty() { - // Push all the indexes. - root.headings.push((self.spacing, self.start_index)); - root.headings.extend(self.nested_headings); - root.spanned_rows.extend(self.spanned_rows); - - // Push all the rows. - root.push_record(vec![self.heading]); - for row in self.rows { - root.push_record(row); - } - } - } - - fn finish_with_parent(self, parent: &mut StSectionBuilder) { - if !self.rows.is_empty() { - // Push all the indexes. - parent.nested_headings.push((self.spacing, self.start_index)); - parent.nested_headings.extend(self.nested_headings); - parent.spanned_rows.extend(self.spanned_rows); - - // Push all the rows. - parent.rows.push(vec![self.heading]); - parent.rows.extend(self.rows); - } - } -} - -/// Spacing for sections. -#[derive(Copy, Clone, Debug)] -pub(crate) enum SectionSpacing { - /// Always add a line of spacing above the section heading. - /// - /// There will always be one row of padding above the heading. - Always, - - /// Only add a line of spacing if this isn't the first heading in the - /// series. - IfNotFirst, - - /// Do not add a line of spacing above the heading. - Never, -} - -impl SectionSpacing { - fn resolve(self, is_empty: bool) -> HeadingSpacing { - match (self, is_empty) { - (SectionSpacing::Always, _) => HeadingSpacing::Yes, - (SectionSpacing::IfNotFirst, true) => HeadingSpacing::No, - (SectionSpacing::IfNotFirst, false) => HeadingSpacing::Yes, - (SectionSpacing::Never, _) => HeadingSpacing::No, - } - } -} - -/// Spacing for headings -- a resolved form of [`SectionSpacing`]. -#[derive(Copy, Clone, Debug)] -enum HeadingSpacing { - /// Add a line of padding above the heading. - Yes, - - /// Do not add a line of padding above the heading. - No, -} - -fn apply_normal_row_settings(table: &mut Table, special_rows: HashSet) { - for row in 0..table.count_rows() { - if special_rows.contains(&row) { - continue; - } - - table.with( - Modify::new((row, 0)) - // Adjust the first column to span 2 (the extra indent). - .with(ColumnSpan::new(2)), - ); - } -} - -fn apply_header_row_settings(table: &mut Table, header_rows: &[usize]) { - for &hr in header_rows { - table.with( - Modify::new(Rows::single(hr).intersect(Columns::new(1..))) - // Column 1 onwards (everything after the initial indent) have - // borders. - .with(Border::new( - // top/bottom - Some('-'), - Some('-'), - // no left/right - None, - None, - // corners - Some('-'), - Some('-'), - Some('-'), - Some('-'), - )), - ); - } -} - -fn apply_heading_settings( - table: &mut Table, - headings: &[(HeadingSpacing, usize)], -) { - for &(kind, h) in headings { - let padding = match kind { - HeadingSpacing::Yes => Padding::new(0, 0, 1, 0), - HeadingSpacing::No => Padding::new(0, 0, 0, 0), - }; - - table.with( - Modify::new((h, 0)) - // Adjust each heading row to span the whole row. - .with(ColumnSpan::max()) - .with(padding), - ); - } -} - -fn apply_spanned_row_settings(table: &mut Table, spanned_rows: &[usize]) { - for &sr in spanned_rows { - table.with( - Modify::new((sr, 0)) - // Adjust each spanned row to span the whole row. - .with(ColumnSpan::max()), - ); - } -} diff --git a/oximeter/collector/tests/output/self-stat-schema.json b/oximeter/collector/tests/output/self-stat-schema.json index 1b18362a26..4a56a81c86 100644 --- a/oximeter/collector/tests/output/self-stat-schema.json +++ b/oximeter/collector/tests/output/self-stat-schema.json @@ -39,7 +39,7 @@ } ], "datum_type": "cumulative_u64", - "created": "2024-05-03T22:37:51.326086935Z" + "created": "2024-05-11T09:41:23.361298682Z" }, "oximeter_collector:failed_collections": { "timeseries_name": "oximeter_collector:failed_collections", @@ -86,6 +86,6 @@ } ], "datum_type": "cumulative_u64", - "created": "2024-05-03T22:37:51.327389025Z" + "created": "2024-05-11T09:41:23.361907436Z" } -} \ No newline at end of file +} diff --git a/sled-storage/src/disk.rs b/sled-storage/src/disk.rs index c9e848559e..608d3678da 100644 --- a/sled-storage/src/disk.rs +++ b/sled-storage/src/disk.rs @@ -25,7 +25,16 @@ use crate::config::MountConfig; use crate::dataset; #[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, + Clone, + Debug, + Deserialize, + Serialize, + JsonSchema, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, )] pub struct OmicronPhysicalDiskConfig { pub identity: DiskIdentity,