Skip to content

Commit 0be1731

Browse files
authored
[mgs] PendingMgsUpdate type to have a variant for the RoT (#8054)
Implements the first step of updating the PendingMgsUpdate type to have a variant for the RoT. This lays out the foundation for the work in #7989
1 parent 32c64f2 commit 0be1731

File tree

10 files changed

+271
-2
lines changed

10 files changed

+271
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dev-tools/reconfigurator-sp-updater/src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,28 @@ fn cmd_config(
322322
expected_active_version, expected_inactive_version,
323323
)?;
324324
}
325+
PendingMgsUpdateDetails::Rot {
326+
expected_slot_a_version,
327+
expected_slot_b_version,
328+
expected_active_slot,
329+
expected_persistent_boot_preference,
330+
expected_pending_persistent_boot_preference,
331+
expected_transient_boot_preference,
332+
} => {
333+
writeln!(
334+
&mut s,
335+
" preconditions: expected_slot_a_version {:?}
336+
expected_slot_b_version {:?}
337+
expected active_slot {:?}
338+
expected persistent_boot_preference {:?}
339+
expected pending_persistent_boot_preference {:?}
340+
expected transient_boot_preference {:?}",
341+
expected_slot_a_version, expected_slot_b_version,
342+
expected_active_slot, expected_persistent_boot_preference,
343+
expected_pending_persistent_boot_preference,
344+
expected_transient_boot_preference,
345+
)?;
346+
}
325347
}
326348

327349
writeln!(&mut s)?;

gateway-types/src/rot.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ pub enum RotSlot {
180180
B,
181181
}
182182

183+
impl RotSlot {
184+
pub fn to_u16(&self) -> u16 {
185+
match self {
186+
RotSlot::A => 0,
187+
RotSlot::B => 1,
188+
}
189+
}
190+
191+
pub fn toggled(&self) -> Self {
192+
match self {
193+
RotSlot::A => RotSlot::B,
194+
RotSlot::B => RotSlot::A,
195+
}
196+
}
197+
}
198+
183199
impl From<gateway_messages::RotSlotId> for RotSlot {
184200
fn from(slot: gateway_messages::RotSlotId) -> Self {
185201
match slot {

nexus/mgs-updates/src/driver.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
//! Drive one or more in-progress MGS-managed updates
66
77
use crate::ArtifactCache;
8+
use crate::SpComponentUpdateHelper;
89
use crate::driver_update::ApplyUpdateError;
910
use crate::driver_update::PROGRESS_TIMEOUT;
1011
use crate::driver_update::SpComponentUpdate;
1112
use crate::driver_update::apply_update;
13+
use crate::rot_updater::ReconfiguratorRotUpdater;
1214
use crate::sp_updater::ReconfiguratorSpUpdater;
1315
use futures::FutureExt;
1416
use futures::future::BoxFuture;
@@ -302,13 +304,24 @@ impl MgsUpdateDriver {
302304
));
303305
info!(&log, "begin update attempt for baseboard");
304306

305-
let (sp_update, updater) = match &request.details {
307+
let (sp_update, updater): (
308+
_,
309+
Box<dyn SpComponentUpdateHelper + Send + Sync>,
310+
) = match &request.details {
306311
nexus_types::deployment::PendingMgsUpdateDetails::Sp { .. } => {
307312
let sp_update =
308313
SpComponentUpdate::from_request(&log, &request, update_id);
309314

310315
(sp_update, Box::new(ReconfiguratorSpUpdater {}))
311316
}
317+
nexus_types::deployment::PendingMgsUpdateDetails::Rot {
318+
..
319+
} => {
320+
let sp_update =
321+
SpComponentUpdate::from_request(&log, &request, update_id);
322+
323+
(sp_update, Box::new(ReconfiguratorRotUpdater {}))
324+
}
312325
};
313326

314327
let baseboard_id = baseboard_id.clone();

nexus/mgs-updates/src/driver_update.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ impl SpComponentUpdate {
8282
firmware_slot: 0,
8383
update_id,
8484
},
85+
PendingMgsUpdateDetails::Rot { expected_active_slot, .. } => {
86+
SpComponentUpdate {
87+
log: log.clone(),
88+
component: SpComponent::ROT,
89+
target_sp_type: request.sp_type,
90+
target_sp_slot: request.slot_id,
91+
// Like the SP, we request an update to the inactive slot
92+
firmware_slot: expected_active_slot.toggled().to_u16(),
93+
update_id,
94+
}
95+
}
8596
}
8697
}
8798
}

nexus/mgs-updates/src/rot_updater.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ use super::SpComponentUpdateError;
99
use super::UpdateProgress;
1010
use super::common_sp_update::SpComponentUpdater;
1111
use super::common_sp_update::deliver_update;
12+
use crate::SpComponentUpdateHelper;
13+
use crate::common_sp_update::PrecheckError;
14+
use crate::common_sp_update::PrecheckStatus;
15+
use futures::future::BoxFuture;
1216
use gateway_client::SpComponent;
1317
use gateway_client::types::RotSlot;
1418
use gateway_client::types::SpComponentFirmwareSlot;
1519
use gateway_client::types::SpType;
20+
use nexus_types::deployment::PendingMgsUpdate;
1621
use slog::Logger;
1722
use slog::info;
1823
use tokio::sync::watch;
@@ -192,3 +197,29 @@ impl SpComponentUpdater for RotUpdater {
192197
&self.log
193198
}
194199
}
200+
201+
pub struct ReconfiguratorRotUpdater;
202+
impl SpComponentUpdateHelper for ReconfiguratorRotUpdater {
203+
/// Checks if the component is already updated or ready for update
204+
fn precheck<'a>(
205+
&'a self,
206+
_log: &'a slog::Logger,
207+
_mgs_clients: &'a mut MgsClients,
208+
_update: &'a PendingMgsUpdate,
209+
) -> BoxFuture<'a, Result<PrecheckStatus, PrecheckError>> {
210+
// TODO-K: fill in the precheck
211+
todo!()
212+
}
213+
214+
/// Attempts once to perform any post-update actions (e.g., reset the
215+
/// device)
216+
fn post_update<'a>(
217+
&'a self,
218+
_log: &'a slog::Logger,
219+
_mgs_clients: &'a mut MgsClients,
220+
_update: &'a PendingMgsUpdate,
221+
) -> BoxFuture<'a, Result<(), GatewayClientError>> {
222+
// TODO-K: fill in the post_update
223+
todo!()
224+
}
225+
}

nexus/mgs-updates/src/sp_updater.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,12 @@ impl SpComponentUpdateHelper for ReconfiguratorSpUpdater {
213213
let PendingMgsUpdateDetails::Sp {
214214
expected_active_version,
215215
expected_inactive_version,
216-
} = &update.details;
216+
} = &update.details
217+
else {
218+
unreachable!(
219+
"pending MGS update details will always be for the SP"
220+
);
221+
};
217222
if caboose.version != expected_active_version.to_string() {
218223
return Err(PrecheckError::WrongActiveVersion {
219224
expected: expected_active_version.clone(),

nexus/types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ url = { workspace = true, features = ["serde"] }
4949

5050
api_identity.workspace = true
5151
gateway-client.workspace = true
52+
gateway-types.workspace = true
5253
internal-dns-types.workspace = true
5354
nexus-sled-agent-shared.workspace = true
5455
omicron-common.workspace = true

nexus/types/src/deployment.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub use blueprint_diff::BlueprintDiffSummary;
6868
use blueprint_display::BpPendingMgsUpdates;
6969
pub use clickhouse::ClickhouseClusterConfig;
7070
use gateway_client::types::SpType;
71+
use gateway_types::rot::RotSlot;
7172
pub use network_resources::AddNetworkResourceError;
7273
pub use network_resources::OmicronZoneExternalFloatingAddr;
7374
pub use network_resources::OmicronZoneExternalFloatingIp;
@@ -1245,6 +1246,36 @@ pub enum PendingMgsUpdateDetails {
12451246
/// expected contents of the inactive slot
12461247
expected_inactive_version: ExpectedVersion,
12471248
},
1249+
/// the RoT is being updated
1250+
Rot {
1251+
// implicit: component = ROT
1252+
// implicit: firmware slot id will be the inactive slot
1253+
/// expected contents of "A" slot
1254+
expected_slot_a_version: ExpectedVersion,
1255+
/// expected contents of "B" slot
1256+
expected_slot_b_version: ExpectedVersion,
1257+
/// the slot of the currently running image
1258+
expected_active_slot: RotSlot,
1259+
// under normal operation, this should always match the active slot.
1260+
// if this field changed without the active slot changing, that might
1261+
// reflect a bad update.
1262+
//
1263+
/// the persistent boot preference written into the current authoritative
1264+
/// CFPA page (ping or pong)
1265+
expected_persistent_boot_preference: RotSlot,
1266+
// if this value changed, but not any of this other information, that could
1267+
// reflect an attempt to switch to the other slot.
1268+
//
1269+
/// the persistent boot preference written into the CFPA scratch page that
1270+
/// will become the persistent boot preference in the authoritative CFPA
1271+
/// page upon reboot, unless CFPA update of the authoritative page fails
1272+
/// for some reason.
1273+
expected_pending_persistent_boot_preference: Option<RotSlot>,
1274+
// this field is not in use yet.
1275+
//
1276+
/// override persistent preference selection for a single boot
1277+
expected_transient_boot_preference: Option<RotSlot>,
1278+
},
12481279
}
12491280

12501281
impl slog::KV for PendingMgsUpdateDetails {
@@ -1268,6 +1299,43 @@ impl slog::KV for PendingMgsUpdateDetails {
12681299
&format!("{:?}", expected_inactive_version),
12691300
)
12701301
}
1302+
PendingMgsUpdateDetails::Rot {
1303+
expected_slot_a_version,
1304+
expected_slot_b_version,
1305+
expected_active_slot,
1306+
expected_persistent_boot_preference,
1307+
expected_pending_persistent_boot_preference,
1308+
expected_transient_boot_preference,
1309+
} => {
1310+
serializer.emit_str(Key::from("component"), "rot")?;
1311+
serializer.emit_str(
1312+
Key::from("expected_slot_a_version"),
1313+
&format!("{:?}", expected_slot_a_version),
1314+
)?;
1315+
serializer.emit_str(
1316+
Key::from("expected_slot_b_version"),
1317+
&format!("{:?}", expected_slot_b_version),
1318+
)?;
1319+
serializer.emit_str(
1320+
Key::from("expected_active_slot"),
1321+
&format!("{:?}", expected_active_slot),
1322+
)?;
1323+
serializer.emit_str(
1324+
Key::from("expected_persistent_boot_preference"),
1325+
&format!("{:?}", expected_persistent_boot_preference),
1326+
)?;
1327+
serializer.emit_str(
1328+
Key::from("expected_pending_persistent_boot_preference"),
1329+
&format!(
1330+
"{:?}",
1331+
expected_pending_persistent_boot_preference
1332+
),
1333+
)?;
1334+
serializer.emit_str(
1335+
Key::from("expected_transient_boot_preference"),
1336+
&format!("{:?}", expected_transient_boot_preference),
1337+
)
1338+
}
12711339
}
12721340
}
12731341
}

openapi/nexus-internal.json

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5377,6 +5377,75 @@
53775377
"expected_active_version",
53785378
"expected_inactive_version"
53795379
]
5380+
},
5381+
{
5382+
"description": "the RoT is being updated",
5383+
"type": "object",
5384+
"properties": {
5385+
"component": {
5386+
"type": "string",
5387+
"enum": [
5388+
"rot"
5389+
]
5390+
},
5391+
"expected_active_slot": {
5392+
"description": "the slot of the currently running image",
5393+
"allOf": [
5394+
{
5395+
"$ref": "#/components/schemas/RotSlot"
5396+
}
5397+
]
5398+
},
5399+
"expected_pending_persistent_boot_preference": {
5400+
"nullable": true,
5401+
"description": "the persistent boot preference written into the CFPA scratch page that will become the persistent boot preference in the authoritative CFPA page upon reboot, unless CFPA update of the authoritative page fails for some reason.",
5402+
"allOf": [
5403+
{
5404+
"$ref": "#/components/schemas/RotSlot"
5405+
}
5406+
]
5407+
},
5408+
"expected_persistent_boot_preference": {
5409+
"description": "the persistent boot preference written into the current authoritative CFPA page (ping or pong)",
5410+
"allOf": [
5411+
{
5412+
"$ref": "#/components/schemas/RotSlot"
5413+
}
5414+
]
5415+
},
5416+
"expected_slot_a_version": {
5417+
"description": "expected contents of \"A\" slot",
5418+
"allOf": [
5419+
{
5420+
"$ref": "#/components/schemas/ExpectedVersion"
5421+
}
5422+
]
5423+
},
5424+
"expected_slot_b_version": {
5425+
"description": "expected contents of \"B\" slot",
5426+
"allOf": [
5427+
{
5428+
"$ref": "#/components/schemas/ExpectedVersion"
5429+
}
5430+
]
5431+
},
5432+
"expected_transient_boot_preference": {
5433+
"nullable": true,
5434+
"description": "override persistent preference selection for a single boot",
5435+
"allOf": [
5436+
{
5437+
"$ref": "#/components/schemas/RotSlot"
5438+
}
5439+
]
5440+
}
5441+
},
5442+
"required": [
5443+
"component",
5444+
"expected_active_slot",
5445+
"expected_persistent_boot_preference",
5446+
"expected_slot_a_version",
5447+
"expected_slot_b_version"
5448+
]
53805449
}
53815450
]
53825451
},
@@ -6013,6 +6082,38 @@
60136082
"time"
60146083
]
60156084
},
6085+
"RotSlot": {
6086+
"oneOf": [
6087+
{
6088+
"type": "object",
6089+
"properties": {
6090+
"slot": {
6091+
"type": "string",
6092+
"enum": [
6093+
"a"
6094+
]
6095+
}
6096+
},
6097+
"required": [
6098+
"slot"
6099+
]
6100+
},
6101+
{
6102+
"type": "object",
6103+
"properties": {
6104+
"slot": {
6105+
"type": "string",
6106+
"enum": [
6107+
"b"
6108+
]
6109+
}
6110+
},
6111+
"required": [
6112+
"slot"
6113+
]
6114+
}
6115+
]
6116+
},
60166117
"RouteConfig": {
60176118
"type": "object",
60186119
"properties": {

0 commit comments

Comments
 (0)