Skip to content

Commit 67985f3

Browse files
authored
[6/n] [sled-agent] add remove_mupdate_override to OmicronSledConfig and friends (#8097)
This adds: * data types * db storage * blueprint display * blueprint diff display * generation bump if there's a change to this field * reconfigurator-cli tests for display and generation bump * reconfigurator-cli support code including a couple of debug commands In subsequent commits we'll make decisions based on this.
1 parent 0b633fb commit 67985f3

File tree

35 files changed

+935
-30
lines changed

35 files changed

+935
-30
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dev-tools/reconfigurator-cli/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ humantime.workspace = true
2121
indent_write.workspace = true
2222
internal-dns-types.workspace = true
2323
itertools.workspace = true
24+
newtype-uuid.workspace = true
2425
nexus-inventory.workspace = true
2526
nexus-reconfigurator-blippy.workspace = true
2627
nexus-reconfigurator-planning.workspace = true

dev-tools/reconfigurator-cli/src/lib.rs

+195-3
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ use omicron_common::api::external::Name;
4242
use omicron_common::policy::NEXUS_REDUNDANCY;
4343
use omicron_repl_utils::run_repl_from_file;
4444
use omicron_repl_utils::run_repl_on_stdin;
45-
use omicron_uuid_kinds::BlueprintUuid;
4645
use omicron_uuid_kinds::CollectionUuid;
4746
use omicron_uuid_kinds::GenericUuid;
4847
use omicron_uuid_kinds::OmicronZoneUuid;
4948
use omicron_uuid_kinds::ReconfiguratorSimUuid;
5049
use omicron_uuid_kinds::SledUuid;
5150
use omicron_uuid_kinds::VnicUuid;
51+
use omicron_uuid_kinds::{BlueprintUuid, MupdateOverrideUuid};
5252
use std::borrow::Cow;
5353
use std::collections::BTreeMap;
54-
use std::fmt::Write;
54+
use std::fmt::{self, Write};
5555
use std::io::IsTerminal;
5656
use std::str::FromStr;
5757
use swrite::{SWrite, swriteln};
@@ -204,7 +204,9 @@ fn process_command(
204204
let cmd_result = match command {
205205
Commands::SledList => cmd_sled_list(sim),
206206
Commands::SledAdd(args) => cmd_sled_add(sim, args),
207+
Commands::SledRemove(args) => cmd_sled_remove(sim, args),
207208
Commands::SledShow(args) => cmd_sled_show(sim, args),
209+
Commands::SledSetPolicy(args) => cmd_sled_set_policy(sim, args),
208210
Commands::SiloList => cmd_silo_list(sim),
209211
Commands::SiloAdd(args) => cmd_silo_add(sim, args),
210212
Commands::SiloRemove(args) => cmd_silo_remove(sim, args),
@@ -252,8 +254,12 @@ enum Commands {
252254
SledList,
253255
/// add a new sled
254256
SledAdd(SledAddArgs),
257+
/// remove a sled from the system
258+
SledRemove(SledRemoveArgs),
255259
/// show details about one sled
256260
SledShow(SledArgs),
261+
/// set a sled's policy
262+
SledSetPolicy(SledSetPolicyArgs),
257263

258264
/// list silos
259265
SiloList,
@@ -308,6 +314,10 @@ enum Commands {
308314
struct SledAddArgs {
309315
/// id of the new sled
310316
sled_id: Option<SledUuid>,
317+
318+
/// number of disks or pools
319+
#[clap(short = 'd', long, visible_alias = "npools", default_value_t = SledBuilder::DEFAULT_NPOOLS)]
320+
ndisks: u8,
311321
}
312322

313323
#[derive(Debug, Args)]
@@ -320,6 +330,53 @@ struct SledArgs {
320330
filter: SledFilter,
321331
}
322332

333+
#[derive(Debug, Args)]
334+
struct SledSetPolicyArgs {
335+
/// id of the sled
336+
sled_id: SledUuid,
337+
338+
/// The policy to set for the sled
339+
#[clap(value_enum)]
340+
policy: SledPolicyOpt,
341+
}
342+
343+
#[derive(Clone, Copy, Debug, ValueEnum)]
344+
enum SledPolicyOpt {
345+
InService,
346+
NonProvisionable,
347+
Expunged,
348+
}
349+
350+
impl fmt::Display for SledPolicyOpt {
351+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352+
match self {
353+
SledPolicyOpt::InService => write!(f, "in-service"),
354+
SledPolicyOpt::NonProvisionable => write!(f, "non-provisionable"),
355+
SledPolicyOpt::Expunged => write!(f, "expunged"),
356+
}
357+
}
358+
}
359+
360+
impl From<SledPolicyOpt> for SledPolicy {
361+
fn from(value: SledPolicyOpt) -> Self {
362+
match value {
363+
SledPolicyOpt::InService => SledPolicy::InService {
364+
provision_policy: SledProvisionPolicy::Provisionable,
365+
},
366+
SledPolicyOpt::NonProvisionable => SledPolicy::InService {
367+
provision_policy: SledProvisionPolicy::NonProvisionable,
368+
},
369+
SledPolicyOpt::Expunged => SledPolicy::Expunged,
370+
}
371+
}
372+
}
373+
374+
#[derive(Debug, Args)]
375+
struct SledRemoveArgs {
376+
/// id of the sled
377+
sled_id: SledUuid,
378+
}
379+
323380
#[derive(Debug, Args)]
324381
struct SiloAddRemoveArgs {
325382
/// name of the silo
@@ -374,6 +431,14 @@ enum BlueprintEditCommands {
374431
#[command(subcommand)]
375432
image_source: ImageSourceArgs,
376433
},
434+
/// set the remove_mupdate_override field for a sled
435+
SetRemoveMupdateOverride {
436+
/// sled to set the field on
437+
sled_id: SledUuid,
438+
439+
/// the UUID to set the field to, or "unset"
440+
value: MupdateOverrideUuidOpt,
441+
},
377442
/// expunge a zone
378443
ExpungeZone { zone_id: OmicronZoneUuid },
379444
/// configure an SP update
@@ -393,6 +458,34 @@ enum BlueprintEditCommands {
393458
/// baseboard serial number whose update to delete
394459
serial: String,
395460
},
461+
/// debug commands that bypass normal checks
462+
///
463+
/// These commands mutate the blueprint directly, bypassing higher-level
464+
/// planner checks. They're meant for getting into weird states to test
465+
/// against.
466+
Debug {
467+
#[command(subcommand)]
468+
command: BlueprintEditDebugCommands,
469+
},
470+
}
471+
472+
#[derive(Debug, Subcommand)]
473+
enum BlueprintEditDebugCommands {
474+
/// remove a sled from the blueprint
475+
///
476+
/// This bypasses expungement and decommissioning checks, and simply drops
477+
/// the sled from the blueprint.
478+
RemoveSled {
479+
/// the sled to remove
480+
sled: SledUuid,
481+
},
482+
483+
/// Bump a sled's generation number, even if nothing else about the sled has
484+
/// changed.
485+
ForceSledGenerationBump {
486+
/// the sled to bump the sled-agent generation number of
487+
sled: SledUuid,
488+
},
396489
}
397490

398491
#[derive(Clone, Debug)]
@@ -430,6 +523,42 @@ impl From<BlueprintIdOpt> for BlueprintId {
430523
}
431524
}
432525

526+
/// Clap field for an optional mupdate override UUID.
527+
///
528+
/// This structure is similar to `Option`, but is specified separately to:
529+
///
530+
/// 1. Disable clap's magic around `Option`.
531+
/// 2. Provide a custom parser.
532+
///
533+
/// There are other ways to do both 1 and 2 (e.g. specify the type as
534+
/// `std::option::Option`), but when combined they're uglier than this.
535+
#[derive(Clone, Copy, Debug)]
536+
enum MupdateOverrideUuidOpt {
537+
Unset,
538+
Set(MupdateOverrideUuid),
539+
}
540+
541+
impl FromStr for MupdateOverrideUuidOpt {
542+
type Err = newtype_uuid::ParseError;
543+
544+
fn from_str(s: &str) -> Result<Self, Self::Err> {
545+
if s == "unset" || s == "none" {
546+
Ok(MupdateOverrideUuidOpt::Unset)
547+
} else {
548+
Ok(MupdateOverrideUuidOpt::Set(s.parse::<MupdateOverrideUuid>()?))
549+
}
550+
}
551+
}
552+
553+
impl From<MupdateOverrideUuidOpt> for Option<MupdateOverrideUuid> {
554+
fn from(value: MupdateOverrideUuidOpt) -> Self {
555+
match value {
556+
MupdateOverrideUuidOpt::Unset => None,
557+
MupdateOverrideUuidOpt::Set(uuid) => Some(uuid),
558+
}
559+
}
560+
}
561+
433562
#[derive(Clone, Debug, Subcommand)]
434563
enum SpUpdateComponent {
435564
/// update the SP itself
@@ -673,7 +802,7 @@ fn cmd_sled_add(
673802
) -> anyhow::Result<Option<String>> {
674803
let mut state = sim.current_state().to_mut();
675804
let sled_id = add.sled_id.unwrap_or_else(|| state.rng_mut().next_sled_id());
676-
let new_sled = SledBuilder::new().id(sled_id);
805+
let new_sled = SledBuilder::new().id(sled_id).npools(add.ndisks);
677806
state.system_mut().description_mut().sled(new_sled)?;
678807
sim.commit_and_bump(
679808
format!("reconfigurator-cli sled-add: {sled_id}"),
@@ -683,6 +812,24 @@ fn cmd_sled_add(
683812
Ok(Some(format!("added sled {}", sled_id)))
684813
}
685814

815+
fn cmd_sled_remove(
816+
sim: &mut ReconfiguratorSim,
817+
args: SledRemoveArgs,
818+
) -> anyhow::Result<Option<String>> {
819+
let mut state = sim.current_state().to_mut();
820+
let sled_id = args.sled_id;
821+
state
822+
.system_mut()
823+
.description_mut()
824+
.sled_remove(sled_id)
825+
.context("failed to remove sled")?;
826+
sim.commit_and_bump(
827+
format!("reconfigurator-cli sled-remove: {sled_id}"),
828+
state,
829+
);
830+
Ok(Some(format!("removed sled {} from system", sled_id)))
831+
}
832+
686833
fn cmd_sled_show(
687834
sim: &mut ReconfiguratorSim,
688835
args: SledArgs,
@@ -708,6 +855,25 @@ fn cmd_sled_show(
708855
Ok(Some(s))
709856
}
710857

858+
fn cmd_sled_set_policy(
859+
sim: &mut ReconfiguratorSim,
860+
args: SledSetPolicyArgs,
861+
) -> anyhow::Result<Option<String>> {
862+
let mut state = sim.current_state().to_mut();
863+
state
864+
.system_mut()
865+
.description_mut()
866+
.sled_set_policy(args.sled_id, args.policy.into())?;
867+
sim.commit_and_bump(
868+
format!(
869+
"reconfigurator-cli sled-set-policy: {} to {}",
870+
args.sled_id, args.policy,
871+
),
872+
state,
873+
);
874+
Ok(Some(format!("set sled {} policy to {}", args.sled_id, args.policy)))
875+
}
876+
711877
fn cmd_inventory_list(
712878
sim: &mut ReconfiguratorSim,
713879
) -> anyhow::Result<Option<String>> {
@@ -925,6 +1091,19 @@ fn cmd_blueprint_edit(
9251091
.context("failed to add CockroachDB zone")?;
9261092
format!("added CockroachDB zone to sled {}", sled_id)
9271093
}
1094+
BlueprintEditCommands::SetRemoveMupdateOverride { sled_id, value } => {
1095+
builder
1096+
.sled_set_remove_mupdate_override(sled_id, value.into())
1097+
.context("failed to set remove_mupdate_override")?;
1098+
match value {
1099+
MupdateOverrideUuidOpt::Unset => {
1100+
"unset remove_mupdate_override".to_owned()
1101+
}
1102+
MupdateOverrideUuidOpt::Set(uuid) => {
1103+
format!("set remove_mupdate_override to {uuid}")
1104+
}
1105+
}
1106+
}
9281107
BlueprintEditCommands::SetZoneImage { zone_id, image_source } => {
9291108
let sled_id = sled_with_zone(&builder, &zone_id)?;
9301109
let source = BlueprintZoneImageSource::from(image_source);
@@ -998,6 +1177,19 @@ fn cmd_blueprint_edit(
9981177
builder.pending_mgs_update_delete(baseboard_id);
9991178
format!("deleted configured update for serial {serial}")
10001179
}
1180+
BlueprintEditCommands::Debug {
1181+
command: BlueprintEditDebugCommands::RemoveSled { sled },
1182+
} => {
1183+
builder.debug_sled_remove(sled)?;
1184+
format!("debug: removed sled {sled} from blueprint")
1185+
}
1186+
BlueprintEditCommands::Debug {
1187+
command:
1188+
BlueprintEditDebugCommands::ForceSledGenerationBump { sled },
1189+
} => {
1190+
builder.debug_sled_force_generation_bump(sled)?;
1191+
format!("debug: forced sled {sled} generation bump")
1192+
}
10011193
};
10021194

10031195
let mut new_blueprint = builder.build();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Load example system with 7 sleds:
2+
#
3+
# sled 0: unset -> unset (unchanged)
4+
# sled 1: unset -> set
5+
# sled 2: set -> unset
6+
# sled 3: set -> set (unchanged)
7+
# sled 4: set -> set (changed)
8+
# sled 5: set -> set (unchanged) but change something else
9+
# sled 6: set -> sled removed
10+
#
11+
# We'll also add another sled below (new_sled_id) with
12+
# remove_mupdate_override set.
13+
#
14+
# We don't need any zones for this test, so disable that to keep the
15+
# outputs minimal.
16+
17+
load-example --nsleds 7 --ndisks-per-sled 0 --no-zones
18+
19+
# Set the field on sleds 2-6 (0-indexed).
20+
blueprint-edit latest set-remove-mupdate-override 9a867dc9-d505-427f-9eff-cdb1d4d9bd73 00000000-0000-0000-0000-000000000000
21+
blueprint-edit latest set-remove-mupdate-override aff6c093-197d-42c5-ad80-9f10ba051a34 00000000-0000-0000-0000-000000000000
22+
blueprint-edit latest set-remove-mupdate-override b82ede02-399c-48c6-a1de-411df4fa49a7 00000000-0000-0000-0000-000000000000
23+
blueprint-edit latest set-remove-mupdate-override d81c6a84-79b8-4958-ae41-ea46c9b19763 00000000-0000-0000-0000-000000000000
24+
blueprint-edit latest set-remove-mupdate-override e96e226f-4ed9-4c01-91b9-69a9cd076c9e 00000000-0000-0000-0000-000000000000
25+
26+
blueprint-show latest
27+
28+
# Now make another blueprint, starting by adding a new sled and removing sled 6.
29+
sled-add --ndisks 0
30+
blueprint-edit latest debug remove-sled e96e226f-4ed9-4c01-91b9-69a9cd076c9e
31+
sled-remove e96e226f-4ed9-4c01-91b9-69a9cd076c9e
32+
inventory-generate
33+
34+
# Edit sleds 1, 2, 4, 5, and the new one.
35+
blueprint-edit latest set-remove-mupdate-override 98e6b7c2-2efa-41ca-b20a-0a4d61102fe6 ffffffff-ffff-ffff-ffff-ffffffffffff
36+
blueprint-edit latest set-remove-mupdate-override 9a867dc9-d505-427f-9eff-cdb1d4d9bd73 unset
37+
blueprint-edit latest set-remove-mupdate-override b82ede02-399c-48c6-a1de-411df4fa49a7 ffffffff-ffff-ffff-ffff-ffffffffffff
38+
blueprint-edit latest debug force-sled-generation-bump d81c6a84-79b8-4958-ae41-ea46c9b19763
39+
blueprint-edit latest set-remove-mupdate-override 00320471-945d-413c-85e7-03e091a70b3c ffffffff-ffff-ffff-ffff-ffffffffffff
40+
41+
blueprint-diff 7f976e0d-d2a5-4eeb-9e82-c82bc2824aba latest

dev-tools/reconfigurator-cli/tests/output/cmds-set-mgs-updates-stdout

+9-7
Original file line numberDiff line numberDiff line change
@@ -2745,13 +2745,15 @@ edit contents of a blueprint directly
27452745
Usage: blueprint-edit [OPTIONS] <BLUEPRINT_ID> <COMMAND>
27462746

27472747
Commands:
2748-
add-nexus add a Nexus instance to a particular sled
2749-
add-cockroach add a CockroachDB instance to a particular sled
2750-
set-zone-image set the image source for a zone
2751-
expunge-zone expunge a zone
2752-
set-sp-update configure an SP update
2753-
delete-sp-update delete a configured SP update
2754-
help Print this message or the help of the given subcommand(s)
2748+
add-nexus add a Nexus instance to a particular sled
2749+
add-cockroach add a CockroachDB instance to a particular sled
2750+
set-zone-image set the image source for a zone
2751+
set-remove-mupdate-override set the remove_mupdate_override field for a sled
2752+
expunge-zone expunge a zone
2753+
set-sp-update configure an SP update
2754+
delete-sp-update delete a configured SP update
2755+
debug debug commands that bypass normal checks
2756+
help Print this message or the help of the given subcommand(s)
27552757

27562758
Arguments:
27572759
<BLUEPRINT_ID> id of the blueprint to edit, "latest", or "target"

dev-tools/reconfigurator-cli/tests/output/cmds-set-remove-mupdate-override-stderr

Whitespace-only changes.

0 commit comments

Comments
 (0)