From b192d6af189b5b6d61c4b3b8e1fb957166ee796d Mon Sep 17 00:00:00 2001 From: Sergio Visinoni Date: Sat, 9 May 2026 10:10:46 +0000 Subject: [PATCH 1/3] test: add failing test to reproduce the issue We want blueprint.creator to store the Nexus UUID, but currently it stores a hardcoded string "blueprint_creator". This test ensures that the desired behaviour is in place. --- nexus/src/app/background/tasks/blueprint_planner.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nexus/src/app/background/tasks/blueprint_planner.rs b/nexus/src/app/background/tasks/blueprint_planner.rs index f9ecba7ab52..63c17e38e03 100644 --- a/nexus/src/app/background/tasks/blueprint_planner.rs +++ b/nexus/src/app/background/tasks/blueprint_planner.rs @@ -552,6 +552,13 @@ mod test { blueprint.diff_since_blueprint(&initial_blueprint).has_changes() ); + // Verify the creator is the Nexus UUID, not the hardcoded string. + let nexus_id = nexus.id.to_string(); + assert_eq!( + blueprint.creator, nexus_id, + "blueprint creator should be the Nexus UUID, not a hardcoded string" + ); + // Planning again should not change the plan, because nothing has changed. let status = serde_json::from_value::( planner.activate(&opctx).await, From 87dcbcdefa310bf9e3f29719d21d90d7bcbd4b1b Mon Sep 17 00:00:00 2001 From: Sergio Visinoni Date: Sat, 9 May 2026 10:14:54 +0000 Subject: [PATCH 2/3] fix: store Neux UUID in BlueprintPlanner This commit ensures that the Nexus UUID is stored in BlueprintPlanner, as reported in #10396. * Add a creator element to the BlueprintPlanner structure * Store the UUID string for the Nexus creator * Update tests to ensure they either use the available nexus_id or a mock one when relevant --- nexus/src/app/background/init.rs | 1 + nexus/src/app/background/tasks/blueprint_planner.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nexus/src/app/background/init.rs b/nexus/src/app/background/init.rs index bdf9ee01638..edd4d0fbaf9 100644 --- a/nexus/src/app/background/init.rs +++ b/nexus/src/app/background/init.rs @@ -585,6 +585,7 @@ impl BackgroundTasksInitializer { reconfigurator_config_watcher.clone(), inventory_load_watcher.clone(), rx_blueprint.clone(), + nexus_id, ); let rx_planner = blueprint_planner.watcher(); driver.register(TaskDefinition { diff --git a/nexus/src/app/background/tasks/blueprint_planner.rs b/nexus/src/app/background/tasks/blueprint_planner.rs index 63c17e38e03..7be3d1a65f5 100644 --- a/nexus/src/app/background/tasks/blueprint_planner.rs +++ b/nexus/src/app/background/tasks/blueprint_planner.rs @@ -25,6 +25,7 @@ use omicron_common::api::external::Error; use omicron_common::api::external::LookupType; use omicron_uuid_kinds::BlueprintUuid; use omicron_uuid_kinds::GenericUuid as _; +use omicron_uuid_kinds::OmicronZoneUuid; use serde_json::json; use slog_error_chain::InlineErrorChain; use std::sync::Arc; @@ -62,6 +63,7 @@ pub struct BlueprintPlanner { rx_blueprint: Receiver>, tx_planned: Sender>, blueprint_limit: u64, + creator: String, } /// The default number of blueprints, beyond which the auto-planner will stop @@ -86,6 +88,7 @@ impl BlueprintPlanner { rx_config: Receiver, rx_inventory: Receiver>>, rx_blueprint: Receiver>, + nexus_id: OmicronZoneUuid, ) -> Self { let (tx_planned, _) = watch::channel(None); Self { @@ -95,6 +98,7 @@ impl BlueprintPlanner { rx_blueprint, tx_planned, blueprint_limit: DEFAULT_BLUEPRINT_LIMIT, + creator: nexus_id.to_string(), } } @@ -205,7 +209,7 @@ impl BlueprintPlanner { let planner = Planner::new_based_on( opctx.log.clone(), &input, - "blueprint_planner", + &self.creator, &collection, PlannerRng::from_entropy(), ) @@ -517,6 +521,7 @@ mod test { rx_config_loader, rx_inventory, rx_loader.clone(), + nexus.id, ); // On activation, the planner should run successfully and generate @@ -694,11 +699,13 @@ mod test { let (_tx_inventory, rx_inventory) = watch::channel(None); let (_tx_blueprint, rx_blueprint) = watch::channel(None); + let test_nexus_id = OmicronZoneUuid::new_v4(); let mut planner = BlueprintPlanner::new( datastore.clone(), rx_config_loader, rx_inventory, rx_blueprint, + test_nexus_id, ); // This limit matches the loop above. From c4b26142f4b39c370639abe8a63f9db0de460c9b Mon Sep 17 00:00:00 2001 From: Sergio Visinoni Date: Mon, 11 May 2026 15:54:38 +0000 Subject: [PATCH 3/3] Prepend the UUID with the nexus label This will make it clear that the creator of this blueprint was a nexus instance --- nexus/src/app/background/tasks/blueprint_planner.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nexus/src/app/background/tasks/blueprint_planner.rs b/nexus/src/app/background/tasks/blueprint_planner.rs index 7be3d1a65f5..4c0eead2323 100644 --- a/nexus/src/app/background/tasks/blueprint_planner.rs +++ b/nexus/src/app/background/tasks/blueprint_planner.rs @@ -98,7 +98,7 @@ impl BlueprintPlanner { rx_blueprint, tx_planned, blueprint_limit: DEFAULT_BLUEPRINT_LIMIT, - creator: nexus_id.to_string(), + creator: format!("nexus {}", nexus_id), } } @@ -558,10 +558,10 @@ mod test { ); // Verify the creator is the Nexus UUID, not the hardcoded string. - let nexus_id = nexus.id.to_string(); + let expected_creator = format!("nexus {}", nexus.id); assert_eq!( - blueprint.creator, nexus_id, - "blueprint creator should be the Nexus UUID, not a hardcoded string" + blueprint.creator, expected_creator, + "blueprint creator should be the 'nexus UUID', not a hardcoded string" ); // Planning again should not change the plan, because nothing has changed.