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,