Skip to content

Commit da66012

Browse files
authoredFeb 6, 2025··
[reconfigurator] Add target blueprint to reconfigurator state (#7485)
This PR adds the target blueprint as an optional field in `UnstableReconfiguratorState`. I don't love that it's optional, because on any real deployed system, there is guaranteed to be a target blueprint. However, `UnstableReconfiguratorState` is also used by `reconfigurator-cli`, and it has various ways for a system state in memory to have no target blueprint. E.g., it has a test where it starts from an empty state, adds a sled, then writes out an `UnstableReconfiguratorState` that contains that sled but no blueprints at all (and therefore no target blueprint). I'm not sure how much fidelity reconfigurator-cli's simulated system should be required to follow, but it seemed like a nontrivial lift to require it to always have a blueprint, hence making the field optional. Maybe it could use a different serialization struct, but that seems not ideal in other ways. Very open to ideas! Builds on #7484. PR 2 of 3 moving toward adding `omdb db reconfigurator archive`.
1 parent efe2328 commit da66012

File tree

7 files changed

+68
-19
lines changed

7 files changed

+68
-19
lines changed
 

‎dev-tools/reconfigurator-cli/src/main.rs

+26-10
Original file line numberDiff line numberDiff line change
@@ -730,25 +730,41 @@ fn cmd_blueprint_list(
730730
#[derive(Tabled)]
731731
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
732732
struct BlueprintRow {
733+
#[tabled(rename = "T")]
734+
is_target: &'static str,
735+
#[tabled(rename = "ENA")]
736+
enabled: &'static str,
733737
id: BlueprintUuid,
734738
parent: Cow<'static, str>,
735739
time_created: String,
736740
}
737741

738742
let state = sim.current_state();
739743

744+
let target_blueprint = state.system().target_blueprint();
740745
let mut rows = state.system().all_blueprints().collect::<Vec<_>>();
741746
rows.sort_unstable_by_key(|blueprint| blueprint.time_created);
742-
let rows = rows.into_iter().map(|blueprint| BlueprintRow {
743-
id: blueprint.id,
744-
parent: blueprint
745-
.parent_blueprint_id
746-
.map(|s| Cow::Owned(s.to_string()))
747-
.unwrap_or(Cow::Borrowed("<none>")),
748-
time_created: humantime::format_rfc3339_millis(
749-
blueprint.time_created.into(),
750-
)
751-
.to_string(),
747+
let rows = rows.into_iter().map(|blueprint| {
748+
let (is_target, enabled) = match target_blueprint {
749+
Some(t) if t.target_id == blueprint.id => {
750+
let enabled = if t.enabled { "yes" } else { "no" };
751+
("*", enabled)
752+
}
753+
_ => ("", ""),
754+
};
755+
BlueprintRow {
756+
is_target,
757+
enabled,
758+
id: blueprint.id,
759+
parent: blueprint
760+
.parent_blueprint_id
761+
.map(|s| Cow::Owned(s.to_string()))
762+
.unwrap_or(Cow::Borrowed("<none>")),
763+
time_created: humantime::format_rfc3339_millis(
764+
blueprint.time_created.into(),
765+
)
766+
.to_string(),
767+
}
752768
});
753769
let table = tabled::Table::new(rows)
754770
.with(tabled::settings::Style::empty())

‎dev-tools/reconfigurator-cli/tests/output/cmd-example-stdout

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ ID NERRORS TIME_DONE
3030
9e187896-7809-46d0-9210-d75be1b3c4d4 0 <REDACTED_TIMESTAMP>
3131

3232
> blueprint-list
33-
ID PARENT TIME_CREATED
34-
02697f74-b14a-4418-90f0-c28b2a3a6aa9 <none> <REDACTED_TIMESTAMP>
35-
ade5749d-bdf3-4fab-a8ae-00bea01b3a5a 02697f74-b14a-4418-90f0-c28b2a3a6aa9 <REDACTED_TIMESTAMP>
33+
T ENA ID PARENT TIME_CREATED
34+
02697f74-b14a-4418-90f0-c28b2a3a6aa9 <none> <REDACTED_TIMESTAMP>
35+
* yes ade5749d-bdf3-4fab-a8ae-00bea01b3a5a 02697f74-b14a-4418-90f0-c28b2a3a6aa9 <REDACTED_TIMESTAMP>
3636

3737
>
3838

@@ -225,9 +225,9 @@ ID NERRORS TIME_DONE
225225
9e187896-7809-46d0-9210-d75be1b3c4d4 0 <REDACTED_TIMESTAMP>
226226

227227
> blueprint-list
228-
ID PARENT TIME_CREATED
229-
02697f74-b14a-4418-90f0-c28b2a3a6aa9 <none> <REDACTED_TIMESTAMP>
230-
ade5749d-bdf3-4fab-a8ae-00bea01b3a5a 02697f74-b14a-4418-90f0-c28b2a3a6aa9 <REDACTED_TIMESTAMP>
228+
T ENA ID PARENT TIME_CREATED
229+
02697f74-b14a-4418-90f0-c28b2a3a6aa9 <none> <REDACTED_TIMESTAMP>
230+
* yes ade5749d-bdf3-4fab-a8ae-00bea01b3a5a 02697f74-b14a-4418-90f0-c28b2a3a6aa9 <REDACTED_TIMESTAMP>
231231

232232
>
233233

‎dev-tools/reconfigurator-cli/tests/output/cmd-stdout

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ID NZPOOLS SUBNET
66
ID NERRORS TIME_DONE
77

88
> blueprint-list
9-
ID PARENT TIME_CREATED
9+
T ENA ID PARENT TIME_CREATED
1010

1111
>
1212

‎nexus/reconfigurator/preparation/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ pub async fn reconfigurator_state_load(
318318
.collect::<Vec<Collection>>()
319319
.await;
320320

321+
let target_blueprint = datastore
322+
.blueprint_target_get_current(opctx)
323+
.await
324+
.context("failed to read current target blueprint")?;
325+
321326
let mut blueprint_ids = Vec::new();
322327
let mut paginator = Paginator::new(SQL_BATCH_SIZE);
323328
while let Some(p) = paginator.next() {
@@ -403,6 +408,7 @@ pub async fn reconfigurator_state_load(
403408
Ok(UnstableReconfiguratorState {
404409
planning_input,
405410
collections,
411+
target_blueprint: Some(target_blueprint),
406412
blueprints,
407413
internal_dns,
408414
external_dns,

‎nexus/reconfigurator/simulation/src/state.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl SimState {
136136
Ok(UnstableReconfiguratorState {
137137
planning_input,
138138
collections: self.system.all_collections().cloned().collect(),
139+
target_blueprint: self.system().target_blueprint(),
139140
blueprints: self.system.all_blueprints().cloned().collect(),
140141
internal_dns: self
141142
.system

‎nexus/reconfigurator/simulation/src/system.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use nexus_reconfigurator_planning::{
1313
system::{SledHwInventory, SystemDescription},
1414
};
1515
use nexus_types::{
16-
deployment::{Blueprint, SledFilter, UnstableReconfiguratorState},
16+
deployment::{
17+
Blueprint, BlueprintTarget, SledFilter, UnstableReconfiguratorState,
18+
},
1719
internal_api::params::{DnsConfigParams, DnsConfigZone},
1820
inventory::Collection,
1921
};
@@ -77,6 +79,9 @@ pub struct SimSystem {
7779
/// Stored with `Arc` to allow cheap cloning.
7880
blueprints: IndexMap<BlueprintUuid, Arc<Blueprint>>,
7981

82+
/// Current target blueprint.
83+
target_blueprint: Option<BlueprintTarget>,
84+
8085
/// Internal DNS configurations.
8186
///
8287
/// Stored with `Arc` to allow cheap cloning.
@@ -94,6 +99,7 @@ impl SimSystem {
9499
description: SystemDescription::new(),
95100
collections: IndexMap::new(),
96101
blueprints: IndexMap::new(),
102+
target_blueprint: None,
97103
internal_dns: BTreeMap::new(),
98104
external_dns: BTreeMap::new(),
99105
}
@@ -103,6 +109,7 @@ impl SimSystem {
103109
!self.description.has_sleds()
104110
&& self.collections.is_empty()
105111
&& self.blueprints.is_empty()
112+
&& self.target_blueprint.is_none()
106113
&& self.internal_dns.is_empty()
107114
&& self.external_dns.is_empty()
108115
}
@@ -138,6 +145,10 @@ impl SimSystem {
138145
}
139146
}
140147

148+
pub fn target_blueprint(&self) -> Option<BlueprintTarget> {
149+
self.target_blueprint
150+
}
151+
141152
pub fn all_blueprints(&self) -> impl ExactSizeIterator<Item = &Blueprint> {
142153
self.blueprints.values().map(|b| &**b)
143154
}
@@ -529,6 +540,11 @@ impl SimSystemBuilderInner {
529540
example.initial_blueprint.id,
530541
Arc::new(example.initial_blueprint),
531542
);
543+
self.system.target_blueprint = Some(BlueprintTarget {
544+
target_id: blueprint.id,
545+
enabled: true,
546+
time_made_target: blueprint.time_created,
547+
});
532548
self.system.blueprints.insert(blueprint.id, Arc::new(blueprint));
533549
}
534550

@@ -622,6 +638,8 @@ impl SimSystemBuilderInner {
622638
}
623639
}
624640

641+
self.system.target_blueprint = state.target_blueprint;
642+
625643
for blueprint in state.blueprints {
626644
let blueprint_id = blueprint.id;
627645
match self.add_blueprint_inner(Arc::new(blueprint)) {

‎nexus/types/src/deployment.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,9 @@ pub struct BlueprintMetadata {
11031103
}
11041104

11051105
/// Describes what blueprint, if any, the system is currently working toward
1106-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
1106+
#[derive(
1107+
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema,
1108+
)]
11071109
pub struct BlueprintTarget {
11081110
/// id of the blueprint that the system is trying to make real
11091111
pub target_id: BlueprintUuid,
@@ -1157,6 +1159,12 @@ impl From<&crate::inventory::Dataset> for CollectionDatasetIdentifier {
11571159
pub struct UnstableReconfiguratorState {
11581160
pub planning_input: PlanningInput,
11591161
pub collections: Vec<Collection>,
1162+
// When collected from a deployed system, `target_blueprint` will always be
1163+
// `Some(_)`. `UnstableReconfiguratorState` is also used by
1164+
// `reconfigurator-cli`, which allows construction of states that do not
1165+
// represent a fully-deployed system (and maybe no blueprints at all, hence
1166+
// no target blueprint).
1167+
pub target_blueprint: Option<BlueprintTarget>,
11601168
pub blueprints: Vec<Blueprint>,
11611169
pub internal_dns: BTreeMap<Generation, DnsConfigParams>,
11621170
pub external_dns: BTreeMap<Generation, DnsConfigParams>,

0 commit comments

Comments
 (0)
Please sign in to comment.