Skip to content

Commit 78021f0

Browse files
authored
[nexus-mgs-updates] Add support for host phase 1 updates (#8737)
1 parent c097d37 commit 78021f0

File tree

24 files changed

+2830
-76
lines changed

24 files changed

+2830
-76
lines changed

Cargo.lock

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

clients/gateway-client/src/lib.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ impl PartialOrd for crate::types::SpIdentifier {
103103

104104
#[derive(Debug, thiserror::Error)]
105105
pub enum HostPhase1HashError {
106-
#[error("timed out waiting for hash calculation")]
107-
Timeout,
106+
#[error("timed out after {0:?} waiting for hash calculation")]
107+
Timeout(Duration),
108108
#[error("hash calculation failed (phase1 written while hashing?)")]
109109
ContentsModifiedWhileHashing,
110110
#[error("failed to send request to {kind}")]
@@ -126,7 +126,8 @@ impl Client {
126126
/// handful of seconds on real hardware.
127127
pub async fn host_phase_1_flash_hash_calculate_with_timeout(
128128
&self,
129-
sp: types::SpIdentifier,
129+
sp_type: types::SpType,
130+
sp_slot: u16,
130131
phase1_slot: u16,
131132
timeout: Duration,
132133
) -> Result<[u8; 32], HostPhase1HashError> {
@@ -159,8 +160,8 @@ impl Client {
159160

160161
let need_to_start_hashing = match self
161162
.sp_component_hash_firmware_get(
162-
sp.type_,
163-
sp.slot,
163+
sp_type,
164+
sp_slot,
164165
PHASE1_FLASH,
165166
phase1_slot,
166167
)
@@ -185,8 +186,8 @@ impl Client {
185186
// catch a `HashInProgress` error here and return an HTTP success.
186187
// We'll return any other error.
187188
self.sp_component_hash_firmware_start(
188-
sp.type_,
189-
sp.slot,
189+
sp_type,
190+
sp_slot,
190191
PHASE1_FLASH,
191192
phase1_slot,
192193
)
@@ -201,12 +202,12 @@ impl Client {
201202
loop {
202203
tokio::time::sleep(SLEEP_BETWEEN_POLLS).await;
203204
if start.elapsed() > timeout {
204-
return Err(HostPhase1HashError::Timeout);
205+
return Err(HostPhase1HashError::Timeout(timeout));
205206
}
206207
match self
207208
.sp_component_hash_firmware_get(
208-
sp.type_,
209-
sp.slot,
209+
sp_type,
210+
sp_slot,
210211
PHASE1_FLASH,
211212
phase1_slot,
212213
)

common/src/disk.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ impl DiskManagementError {
619619
Deserialize,
620620
Serialize,
621621
JsonSchema,
622+
Diffable,
622623
strum::EnumIter,
623624
)]
624625
pub enum M2Slot {
@@ -663,6 +664,21 @@ impl fmt::Display for M2Slot {
663664
}
664665
}
665666

667+
impl FromStr for M2Slot {
668+
type Err = String;
669+
670+
fn from_str(s: &str) -> Result<Self, Self::Err> {
671+
match s {
672+
"a" | "A" => Ok(Self::A),
673+
"b" | "B" => Ok(Self::B),
674+
_ => Err(format!(
675+
"unrecognized value {s} for M2 slot. \
676+
Must be one of `a`, `A`, `b`, or `B`",
677+
)),
678+
}
679+
}
680+
}
681+
666682
impl TryFrom<i64> for M2Slot {
667683
type Error = anyhow::Error;
668684

dev-tools/ls-apis/tests/api_dependencies.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Repo Depot API (client: repo-depot-client)
9191
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
9292

9393
Sled Agent (client: sled-agent-client)
94-
consumed by: omicron-nexus (omicron/nexus) via 7 paths
94+
consumed by: omicron-nexus (omicron/nexus) via 8 paths
9595

9696
Wicketd (client: wicketd-client)
9797

dev-tools/reconfigurator-sp-updater/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal-dns-resolver.workspace = true
2020
internal-dns-types.workspace = true
2121
nexus-mgs-updates.workspace = true
2222
nexus-types.workspace = true
23+
omicron-common.workspace = true
2324
omicron-repl-utils.workspace = true
2425
oxide-tokio-rt.workspace = true
2526
qorb.workspace = true

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ use nexus_types::deployment::ExpectedActiveRotSlot;
2121
use nexus_types::deployment::ExpectedVersion;
2222
use nexus_types::deployment::PendingMgsUpdate;
2323
use nexus_types::deployment::PendingMgsUpdateDetails;
24+
use nexus_types::deployment::PendingMgsUpdateHostPhase1Details;
2425
use nexus_types::deployment::PendingMgsUpdates;
2526
use nexus_types::internal_api::views::MgsUpdateDriverStatus;
2627
use nexus_types::inventory::BaseboardId;
28+
use omicron_common::disk::M2Slot;
2729
use omicron_repl_utils::run_repl_on_stdin;
2830
use qorb::resolver::Resolver;
2931
use qorb::resolvers::fixed::FixedResolver;
3032
use slog::{info, o, warn};
3133
use std::collections::BTreeMap;
3234
use std::net::SocketAddr;
35+
use std::net::SocketAddrV6;
3336
use std::sync::Arc;
3437
use std::time::Duration;
3538
use swrite::SWrite;
@@ -278,6 +281,9 @@ struct TopLevelArgs {
278281
command: Commands,
279282
}
280283

284+
// Clippy wants us to box `SetArgs`, but that makes matches slightly more
285+
// awkward. This is just a dev/test tool; big variants are fine.
286+
#[allow(clippy::large_enum_variant)]
281287
#[derive(Debug, Subcommand)]
282288
enum Commands {
283289
/// Show configured updates
@@ -357,6 +363,26 @@ fn cmd_config(
357363
expected_stage0_next_version,
358364
);
359365
}
366+
PendingMgsUpdateDetails::HostPhase1(
367+
PendingMgsUpdateHostPhase1Details {
368+
expected_active_phase_1_slot,
369+
expected_boot_disk,
370+
expected_active_phase_1_hash,
371+
expected_active_phase_2_hash,
372+
expected_inactive_phase_1_hash,
373+
expected_inactive_phase_2_hash,
374+
sled_agent_address,
375+
},
376+
) => {
377+
swriteln!(s," preconditions: expected active phase 1 slot {expected_active_phase_1_slot:?}
378+
expected boot disk {expected_boot_disk:?}
379+
expected active phase 1 artifact {expected_active_phase_1_hash}
380+
expected active phase 2 artifact {expected_active_phase_2_hash}
381+
expected inactive phase 1 artifact {expected_inactive_phase_1_hash}
382+
expected inactive phase 2 artifact {expected_inactive_phase_2_hash}
383+
sled_agent_address {sled_agent_address}",
384+
);
385+
}
360386
}
361387

362388
swriteln!(s);
@@ -430,6 +456,22 @@ enum Component {
430456
#[arg(long, short = 'i')]
431457
expected_stage0_next_version: ExpectedVersion,
432458
},
459+
HostPhase1 {
460+
#[arg(long)]
461+
expected_active_phase_1_slot: M2Slot,
462+
#[arg(long)]
463+
expected_boot_disk: M2Slot,
464+
#[arg(long)]
465+
expected_slot_a_phase_1_hash: ArtifactHash,
466+
#[arg(long)]
467+
expected_slot_a_phase_2_hash: ArtifactHash,
468+
#[arg(long)]
469+
expected_slot_b_phase_1_hash: ArtifactHash,
470+
#[arg(long)]
471+
expected_slot_b_phase_2_hash: ArtifactHash,
472+
#[arg(long)]
473+
sled_agent_address: SocketAddrV6,
474+
},
433475
}
434476

435477
fn cmd_set(
@@ -497,6 +539,52 @@ fn cmd_set(
497539
expected_stage0_version,
498540
expected_stage0_next_version,
499541
},
542+
Component::HostPhase1 {
543+
expected_active_phase_1_slot,
544+
expected_boot_disk,
545+
expected_slot_a_phase_1_hash,
546+
expected_slot_a_phase_2_hash,
547+
expected_slot_b_phase_1_hash,
548+
expected_slot_b_phase_2_hash,
549+
sled_agent_address,
550+
} => {
551+
let (
552+
expected_active_phase_1_hash,
553+
expected_inactive_phase_1_hash,
554+
) = match expected_active_phase_1_slot {
555+
M2Slot::A => (
556+
expected_slot_a_phase_1_hash,
557+
expected_slot_b_phase_1_hash,
558+
),
559+
M2Slot::B => (
560+
expected_slot_b_phase_1_hash,
561+
expected_slot_a_phase_1_hash,
562+
),
563+
};
564+
let (
565+
expected_active_phase_2_hash,
566+
expected_inactive_phase_2_hash,
567+
) = match expected_active_phase_1_slot {
568+
M2Slot::A => (
569+
expected_slot_a_phase_2_hash,
570+
expected_slot_b_phase_2_hash,
571+
),
572+
M2Slot::B => (
573+
expected_slot_b_phase_2_hash,
574+
expected_slot_a_phase_2_hash,
575+
),
576+
};
577+
let details = PendingMgsUpdateHostPhase1Details {
578+
expected_active_phase_1_slot,
579+
expected_boot_disk,
580+
expected_active_phase_1_hash,
581+
expected_active_phase_2_hash,
582+
expected_inactive_phase_1_hash,
583+
expected_inactive_phase_2_hash,
584+
sled_agent_address,
585+
};
586+
PendingMgsUpdateDetails::HostPhase1(details)
587+
}
500588
},
501589
artifact_hash: args.artifact_hash,
502590
artifact_version: ArtifactVersion::new(args.version)

gateway-test-utils/src/setup.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,30 @@ pub async fn test_setup(
9898
.await
9999
}
100100

101+
pub async fn test_setup_metrics_disabled(
102+
test_name: &str,
103+
sp_port: SpPort,
104+
) -> GatewayTestContext {
105+
let (mut server_config, sp_sim_config) = load_test_config();
106+
match server_config.metrics.as_mut() {
107+
Some(cfg) => {
108+
cfg.disabled = true;
109+
}
110+
None => {
111+
server_config.metrics =
112+
Some(MetricsConfig { disabled: true, ..Default::default() });
113+
}
114+
}
115+
test_setup_with_config(
116+
test_name,
117+
sp_port,
118+
server_config,
119+
&sp_sim_config,
120+
None,
121+
)
122+
.await
123+
}
124+
101125
fn expected_location(
102126
config: &omicron_gateway::Config,
103127
sp_port: SpPort,

nexus/db-queries/src/db/datastore/deployment.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,6 +2302,8 @@ async fn insert_pending_mgs_update(
23022302
_expected_stage0_next_version,
23032303
) = update_dsl::bp_pending_mgs_update_rot_bootloader::all_columns();
23042304
}
2305+
// TODO implement
2306+
PendingMgsUpdateDetails::HostPhase1(_) => (),
23052307
}
23062308
Ok(())
23072309
}

nexus/inventory/src/collector.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ impl<'a> Collector<'a> {
261261

262262
let result = client
263263
.host_phase_1_flash_hash_calculate_with_timeout(
264-
sp,
264+
sp.type_,
265+
sp.slot,
265266
phase1_slot,
266267
PHASE1_HASH_TIMEOUT,
267268
)

nexus/mgs-updates/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ iddqd.workspace = true
1616
id-map.workspace = true
1717
internal-dns-resolver.workspace = true
1818
internal-dns-types.workspace = true
19+
nexus-sled-agent-shared.workspace = true
1920
nexus-types.workspace = true
2021
omicron-common.workspace = true
2122
omicron-uuid-kinds.workspace = true
2223
qorb.workspace = true
2324
repo-depot-client.workspace = true
2425
reqwest.workspace = true
2526
sha2.workspace = true
27+
sled-agent-client.workspace = true
2628
slog.workspace = true
2729
slog-error-chain.workspace = true
2830
thiserror.workspace = true
@@ -37,11 +39,16 @@ omicron-workspace-hack.workspace = true
3739
[dev-dependencies]
3840
anyhow.workspace = true
3941
assert_matches.workspace = true
42+
camino.workspace = true
4043
dropshot.workspace = true
4144
gateway-messages.workspace = true
4245
gateway-test-utils.workspace = true
46+
http.workspace = true
4347
hubtools.workspace = true
4448
omicron-test-utils.workspace = true
4549
rand.workspace = true
4650
repo-depot-api.workspace = true
51+
sled-agent-api.workspace = true
52+
sled-agent-types.workspace = true
53+
sled-diagnostics.workspace = true
4754
sp-sim.workspace = true

0 commit comments

Comments
 (0)