Skip to content

allow operators to override the sled-add prechecks - #11638

Merged
davepacheco merged 5 commits into
dap/add-sled-check-2from
dap/add-sled-check-3
Nov 3, 2026
Merged

davepacheco merged 5 commits into
dap/add-sled-check-2from
dap/add-sled-check-3

Conversation

@davepacheco

Copy link
Copy Markdown
Collaborator

Depends on #11637 and I intend to land these together, but I separated them out for review.

This PR makes the checks added in 11637 overridable. The goal here is safe-by-default, overridable by someone who knows what they're doing, and a naive operator shouldn't be able to override it without realizing it's risky.

Add a new version of the Nexus external API, SLED_ADD_PRECHECK_OVERRIDES,
in which the request to add sleds to a rack accepts `override_prechecks`.
It has two booleans, both false by default:

* `allow_without_settled_target_release` skips the checks that the system
  has settled on its target release (no update in progress, no mupdate
  waiting to be cleared, and updates not suspended).
* `allow_unverified_sled_software` skips the checks that the new sleds'
  SP, RoT, RoT bootloader, and host phase 1 images match the target
  release.

Overridden checks still run when possible.  Any problem they find is
logged as a warning instead of being returned.  A board that is not a
sled is always refused.

The older `sled_add` endpoint was removed from the API before this
version, so it always runs every check.

@ahl ahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great. thanks for doing this.

@@ -26949,6 +26949,18 @@
"RackMembershipAddSledsRequest": {
"type": "object",
"properties": {
"override_prechecks": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "prechecks" rather than, say, "checks"?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either is fine with me. "precheck" is slightly more evocative to me of something we're going to run before we do anything, and if they fail, nothing will have happened. "checks" could be something else (e.g., a check that the system is healthy after? I don't know). I really don't care though.

Comment on lines +28493 to +28494
"allow_without_settled_target_release": {
"description": "Add the requested sleds even if the system has not settled on its current target release\n\nThis allows sleds to be added when the system is in the middle of a software update or if it has recently been MUPdated and that MUPdate hasn't been resolved. This is not generally supported because in order for adding a sled to work, the versions of software on that sled must match the rest of the control plane, but that's not well-defined in these two cases\n\nThis override is provided for working around bugs and only in emergency situations. It should not generally be used.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in our docs, a single space after a period is much more prevalent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

]
},
"SledAddPrecheckOverrides": {
"description": "Instructs the system to override safety checks when adding this sled",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"description": "Instructs the system to override safety checks when adding this sled",
"description": "Instructs the system to override safety checks when adding a sled",

?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@@ -26949,6 +26949,18 @@
"RackMembershipAddSledsRequest": {
"type": "object",
"properties": {
"override_prechecks": {
"description": "Optional overrides for various safety checks\n\nOverriding checks is dangerous and should only be done at the direction of Oxide support.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything worth adding with regard to the specific danger e.g. what the consequences of misuse might be?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to do this with the specific checks in this struct, but honestly, it's too complicated and inside-baseball to give a good explanation for most customers. These overrides are here in case we run into a situation where it's really important to add a sled and either (1) the logic for the checks is implemented wrong, or (2) we (i.e., engineering or maybe support) have determined it to be okay. I tried to convey that in the docs by saying with:

    /// This override is provided for working around bugs and only in emergency
    /// situations. It should not generally be used.

It's worth being explicit with ourselves about it though. I'll start with the second one. Concretely, if the sled software is mismatched, the most likely failure mode (other than it just working because some of it happens to be close enough) is that the process of adding it just stops without explanation. The request might succeed, but the async process of adding it fails and there's no way for you to see it. However, you could reasonably use allow_unverified_sled_software: true if you knew that the release on the sled really was matched (e.g., if one was 23.1 and the other was 23.2 and you knew there were no meaningful changes to the SP or sled agent API in those releases). Again, I still wouldn't advise it unless we needed to.

The other check, allow_without_settled_target_release, is trickier. There are a few ways this check can fail:

  • We don't support adding a sled if you've never set the target release or if there's an unresolved MUPdate because in both cases we don't know what version of software should be on the sled. But if you're overriding that as above, you don't care about this, either.
  • We don't support adding a sled in the middle of an update because in that case, the correct version of software on it depends on where we are in the update. If it's running the old SP+host OS, but we've already started updating other stuff, that's not valid. In practice, it might be okay as long as Nexus hasn't been updated yet, but it's possible that it has been updated and then might not be able to talk to this host OS. In that case, again, the update will get stuck and it won't be evident why. But again: it's possible for engineering, PA, or a support person to know that these aren't risks and proceed anyway.

Comment thread nexus/src/app/rack.rs Outdated
self.sled_check_versions(
opctx,
[&baseboard_id],
&SledAddPrecheckOverrides::default(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I picked this up from you a long time ago - should we use a more descriptive name than default here? SledAddPrecheckOverrides::none() or SledAddPrecheckOverrides::no_overrides() or something like that?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holy smokes: @davepacheco using "default"!?!?!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wasn't super excited about this. I can get behind default when it's super clear what the semantics are (like maybe for i32), or when the default can't reasonably be computed by the caller (like the default path to store user configuration, which varies from system to system), or when it's very unusual to need to change it (like in this case, hopefully). I like the idea of not impl'ing Default on the type and using something like no_overrides(), but it seemed wrong at the API level to make people write out that they're not overriding anything. The natural way to let them leave that out is to impl Default on this type. But I could as well use the serde annotation to get the default from a helper, though. I don't have a strong feeling either way here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went down this path, but I think using #[serde(default = ...)] instead of #[serde(default)] might have the side effect that users of generated clients have to provide these fields instead of getting a default? That seems meaningfully worse to me. What do you think @jgallagher @ahl?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That... seems terrible and not at all what I'd expect. Is that because the default only applies to deserialization and doesn't make it to the OpenAPI spec?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't poke too hard at it, to be honest. 7142d62 is the commit in which I undid this. In the "before", it was using #[serde(default = ...)]. Nothing about the default value for this top-level field makes it into the OpenAPI document. That's also true in the "after" version with #[serde(default)], but the difference is that the individual boolean fields themselves have default values and those do make it into the OpenAPI document. Since these are plain objects, I imagine a client can infer that they can construct the whole struct from the defaults, but I'm not sure if that happens in practice or if that's technically implied by the spec.

@davepacheco

davepacheco commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator Author

Manual testing notes

Summary

This should be adequately tested by the tests here, but given the importance, I wanted to test on a racklette, too. I deployed commit a166dd1 to the berlin racklette. (This isn't quite the tip of this branch, but it's functionally close enough.)

  • done: expunge sled 17
  • done: add sled 17 with no overrides: works because no target release was ever set. Reconfigurator goes through a few blueprints deploying internal DNS and Crucible zones.
  • done: expunge sled 17 again
  • done: do MUPdate recovery with the wrong TUF repo. (This sets the target release, passing the first gate, but should still fail due to the presence of sleds with unknown versions.)
  • done: attempt add sled 17: fails (as expected) because of presence of MUPdate.
  • done: attempt add sled 17 with overriding version check fails (as expected) because of MUPdate
  • done: attempt add sled 17 with overriding state check fails (as expected) because of version mismatch
  • NOTE: attempt add sled 17 with both overrides: the "add" did work and it set up the new sled but it did NOT put internal_dns or Crucible on it. I think that's because Reconfigurator won't do anything involving versions (like adding new zones) in this mode and that this is expected.
  • done: do mupdate recovery with the correct TUF repo: immediately deploys internal DNS and then Crucible to sled 17 (as expected)
  • done: expunge sled 17
  • done: attempt add sled 17 (should work now without overrides, since this is a normal state with the target release set)
  • done: expunge sled 17 again
  • done: use faux-mgs to change SP version to an older one
  • done: attempt add sled 17 fails (as expected) because of version mismatch
  • done: attempt add sled 17 with state override fails (as expected) because of version mismatch
  • done: attempt add sled 17 with version override only works (as expected)

Proves:

  • can add sled without overrides in these cases:
    • normal operation with matching software
    • legacy (initial setup) case
  • checks kick in when:
    • target release is set and there's a MUPdate that's not resolved
    • target release is set and there's a software mismatch
  • the overrides work and override the right thing

I did not check the case of being in the middle of an update.

Testing details

For each of the expungements, I did this, based on the support procedure:

  • Remove from trust quorum: omdb --destructive nexus trust-quorum remove-sled --sled-id SLED_ID c5e750a8-e260-44d5-99c1-a380d1194cc7
  • Wait for this to show Committed: omdb nexus trust-quorum get-config c5e750a8-e260-44d5-99c1-a380d1194cc7 latest | head -n 15
  • Clean-slate it with:
svcadm disable -s sled-agent
zoneadm list -npc | cut -d: -f2 | grep -v oxz_switch | xargs -i zoneadm -z {} halt
rm -rf /pool/int/*/config/*  /pool/int/*/cluster/* /pool/int/*/update/* /pool/int/*/factory
zpool import -a
zpool list -Hpo name | grep oxp | xargs -l zpool destroy -f
  • pilot sp cycle 17
  • omdb --destructive nexus sleds expunge SLED_ID

I had to blow through this warning each time:

WARNING: sled af2c58b7-e6d3-4805-8969-7715fd108a8b is PRESENT in the most recent inventory collection (spotted at 2026-11-03 00:29:12.430433 UTC). It is dangerous to expunge a sled that is still running. Are you sure you want to proceed anyway?

but I determined that should be fine and was hard to avoid. It's fine because this warning is trying to ensure that the components on the sled aren't running any more and I've determined that by virtue of rebooting it. (The first time I also confirmed that sled agent had been unable to adopt disks and start any zones.) It's hard to avoid because inventory will keep seeing the SP since it's not going away in any of this. This warning was written prior to support for kicking the sled out of TQ and I think it could use some editing. I'll file an issue for that.

Now, for the rest of the test details.

Details

Initial state:

$ oxide system update status
{
  "components_by_release_version": {
    "unknown": 29,
    "install dataset": 60
  },
  "contact_support": false,
  "suspended": true,
  "target_release": null,
  "time_last_step_planned": "2026-11-02T23:11:54.370891Z"
}
[berlin]root@oxz_switch1:~# omdb reconfigurator history
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:104::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:104::4]:32221,[fdcc:7057:faf9:103::3]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT
    1 2026-11-02T23:11:39.511Z 02ce1072-3ebd-42e8-9210-0380550f2c46  enabled: initial blueprint from rack setup
    2 2026-11-02T23:11:42.518Z 4fe7b962-9efe-4f8a-918d-9abfbe689e6b  enabled: updated target release minimum generation from 1 to 2
    3 2026-11-02T23:11:52.138Z 493f3def-94ba-48ce-87c0-f875ad1ef80a  enabled:
    4 2026-11-02T23:11:52.783Z e3006eda-5393-4e4f-a50a-e70a761c200a  enabled:
    5 2026-11-02T23:11:54.370Z 25bd0884-4eb2-467b-9964-0d8cb0d0c876  enabled:
[berlin]root@oxz_switch1:~# omdb db sleds
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:104::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:104::4]:32221,[fdcc:7057:faf9:103::3]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
note: listing all commissioned sleds (use -F to filter, e.g. -F in-service)
 SERIAL       IP                             ROLE      POLICY      STATE   BP AVAIL   ID
 BRM44220007  [fdcc:7057:faf9:103::1]:12345  -         in service  active  available  6c966dde-e798-442e-a02b-34d6c38a9b19
 BRM42220082  [fdcc:7057:faf9:101::1]:12345  scrimlet  in service  active  available  9ccbe757-c2e6-404e-8e9e-5ee4f53595b1
 271FVPY0     [fdcc:7057:faf9:104::1]:12345  -         in service  active  available  af2c58b7-e6d3-4805-8969-7715fd108a8b
 BRM42220011  [fdcc:7057:faf9:102::1]:12345  scrimlet  in service  active  available  ec5ece3c-b8b3-4cb5-aade-1d5731306a0d
[berlin]root@oxz_switch1:~# pilot host ls
CUBBY IP                        SERIAL      IMAGE
14    fe80::aa40:25ff:fe04:219  BRM42220011 ci a166dd1/d05caa3 2026-10-29 23:15
15    fe80::aa40:25ff:fe04:49a  BRM44220007 ci a166dd1/d05caa3 2026-10-29 23:15
16    fe80::aa40:25ff:fe04:198  BRM42220082 ci a166dd1/d05caa3 2026-10-29 23:15
17    fe80::aa40:25ff:fe04:28df 271FVPY0    ci a166dd1/d05caa3 2026-10-29 23:15

To find the rack id:

[berlin]root@oxz_switch1:~# omdb -w db sql
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
#
# Welcome to the CockroachDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: CockroachDB OSS v22.1.22-68-ge9150bd549 (x86_64-pc-solaris2.11, built 2026/10/23 21:06:44, go1.26.2) (same version as client)
# Cluster ID: 3508bb89-59ca-4388-b0e5-0ab18eb3effd
#
# Enter \? for a brief introduction.
#
root@[fdcc:7057:faf9:101::3]:32221/omicron> select rack_id from sled;
                rack_id
----------------------------------------
  c5e750a8-e260-44d5-99c1-a380d1194cc7
  c5e750a8-e260-44d5-99c1-a380d1194cc7
  c5e750a8-e260-44d5-99c1-a380d1194cc7
  c5e750a8-e260-44d5-99c1-a380d1194cc7
(4 rows)


Time: 3ms total (execution 3ms / network 1ms)

root@[fdcc:7057:faf9:101::3]:32221/omicron> ^D

I'll walk through the first expungement for completeness:

omdb --destructive nexus trust-quorum remove-sled --sled-id af2c58b7-e6d3-4805-8969-7715fd108a8b c5e750a8-e260-44d5-99c1-a380d1194cc7
[berlin]root@oxz_switch1:~# omdb nexus trust-quorum get-config c5e750a8-e260-44d5-99c1-a380d1194cc7 latest
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
TrustQuorumConfig {
    rack_id: c5e750a8-e260-44d5-99c1-a380d1194cc7 (rack),
    epoch: Epoch(
        2,
    ),
    last_committed_epoch: Some(
        Epoch(
            1,
        ),
    ),
    state: Preparing,
    threshold: Threshold(
        2,
    ),
    commit_crash_tolerance: 0,
    coordinator: BaseboardId {
        part_number: "913-0000019",
        serial_number: "BRM42220082",
    },
    encrypted_rack_secrets: None,
    members: {
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM42220011",
        }: TrustQuorumMemberData {
            state: Unacked,
            share_digest: None,
            time_prepared: None,
            time_committed: None,
        },
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM42220082",
        }: TrustQuorumMemberData {
            state: Unacked,
            share_digest: None,
            time_prepared: None,
            time_committed: None,
        },
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM44220007",
        }: TrustQuorumMemberData {
            state: Unacked,
            share_digest: None,
            time_prepared: None,
            time_committed: None,
        },
    },
    time_created: 2026-11-03T00:13:46.017632Z,
    time_committing: None,
    time_committed: None,
    time_aborted: None,
    abort_reason: None,
}

This eventually got to:

[berlin]root@oxz_switch1:~# omdb nexus trust-quorum get-config c5e750a8-e260-44d5-99c1-a380d1194cc7 latest
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
TrustQuorumConfig {
    rack_id: c5e750a8-e260-44d5-99c1-a380d1194cc7 (rack),
    epoch: Epoch(
        2,
    ),
    last_committed_epoch: Some(
        Epoch(
            1,
        ),
    ),
    state: Committed,
    threshold: Threshold(
        2,
    ),
    commit_crash_tolerance: 0,
    coordinator: BaseboardId {
        part_number: "913-0000019",
        serial_number: "BRM42220082",
    },
    encrypted_rack_secrets: Some(
        EncryptedRackSecrets {
            salt: Salt(
...
            ),
            data: [
...
            ],
        },
    ),
    members: {
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM42220011",
        }: TrustQuorumMemberData {
            state: Committed,
            share_digest: Some(
                sha3 digest: 1cdf33bca9df21931f9b8f0c851754dfb4863b46aa09848ccbcb5897f53bd3,
            ),
            time_prepared: Some(
                2026-11-03T00:14:32.779638Z,
            ),
            time_committed: Some(
                2026-11-03T00:15:32.726880Z,
            ),
        },
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM42220082",
        }: TrustQuorumMemberData {
            state: Committed,
            share_digest: Some(
                sha3 digest: 4261d2d159e5d4b7e960fae9c2745f4391b4e97c4f55b9589a92a31691be5f8,
            ),
            time_prepared: Some(
                2026-11-03T00:14:32.784959Z,
            ),
            time_committed: Some(
                2026-11-03T00:15:32.726880Z,
            ),
        },
        BaseboardId {
            part_number: "913-0000019",
            serial_number: "BRM44220007",
        }: TrustQuorumMemberData {
            state: Committed,
            share_digest: Some(
                sha3 digest: b2abc482e87f65e7b743d5c9e6aa69ddd09a15fecd8d94d8831325ba95cc723,
            ),
            time_prepared: Some(
                2026-11-03T00:14:32.788622Z,
            ),
            time_committed: Some(
                2026-11-03T00:15:32.726880Z,
            ),
        },
    },
    time_created: 2026-11-03T00:13:46.017632Z,
    time_committing: Some(
        2026-11-03T00:14:32.795602Z,
    ),
    time_committed: Some(
        2026-11-03T00:15:32.789636Z,
    ),
    time_aborted: None,
    abort_reason: None,
}

After rebooting the sled I looked at sled agent and found:

00:00:55.478Z WARN SledAgent (trust-quorum): Received Expunged message for newer epoch. Recording expungement in persistent state.
    baseboard_id = 913-0000019:271FVPY0
    epoch = 1
    file = trust-quorum/protocol/src/node.rs:530
    from = 913-0000019:BRM44220007
    platform_id = BaseboardId { part_number: "913-0000019", serial_number: "271FVPY0" }
    received_epoch = 2
00:00:55.479Z WARN SledAgent (trust-quorum): expunged
    baseboard_id = 913-0000019:271FVPY0
    file = trust-quorum/protocol/src/node.rs:413
    from = 913-0000019:BRM42220082
    platform_id = BaseboardId { part_number: "913-0000019", serial_number: "271FVPY0" }
00:00:55.480Z WARN SledAgent (trust-quorum): expunged
    baseboard_id = 913-0000019:271FVPY0
    file = trust-quorum/protocol/src/node.rs:413
    from = 913-0000019:BRM42220011
    platform_id = BaseboardId { part_number: "913-0000019", serial_number: "271FVPY0" }
...
00:02:55.478Z WARN SledAgent (ConfigReconcilerTask): Disk adoption failed
    disk_identity = DiskIdentity { vendor: "1b96", model: "WUS4C6432DSP3X3", serial: "A079DDE7" }
    error = Failed to adopt disk containing zpool 2e529682-09b8-43b5-8325-be44b096016c: KeyManager error: Failed to retrieve secret: Timeout retrieving secret: Timeout retrieving secret
    file = sled-agent/config-reconciler/src/reconciler_task/external_disks.rs:620

which is good, I think.

Finally:

[berlin]root@oxz_switch1:~# omdb --destructive nexus sleds expunge af2c58b7-e6d3-4805-8969-7715fd108a8b
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:104::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:104::4]:32221,[fdcc:7057:faf9:103::3]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
WARNING: sled af2c58b7-e6d3-4805-8969-7715fd108a8b is PRESENT in the most recent inventory collection (spotted at 2026-11-03 00:29:12.430433 UTC). It is dangerous to expunge a sled that is still running. Are you sure you want to proceed anyway?
y/N〉y
WARNING: Are you sure that you have removed this sled from the latest trust quorum configuration for rack c5e750a8-e260-44d5-99c1-a380d1194cc7?. Please double check with: `omdb nexus trust-quorum get-config <RACK_ID> latest`

WARNING: This operation will PERMANENTLY and IRRECOVABLY mark sled af2c58b7-e6d3-4805-8969-7715fd108a8b (271FVPY0) expunged. To proceed, type the sled's serial number.
sled serial number〉271FVPY0
expunged sled af2c58b7-e6d3-4805-8969-7715fd108a8b (previous policy: InService(Provisionable))

Reconfigurator moved a bunch of stuff to different sleds:

[berlin]root@oxz_switch1:~# omdb reconfigurator history
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT
    1 2026-11-02T23:11:39.511Z 02ce1072-3ebd-42e8-9210-0380550f2c46  enabled: initial blueprint from rack setup
    2 2026-11-02T23:11:42.518Z 4fe7b962-9efe-4f8a-918d-9abfbe689e6b  enabled: updated target release minimum generation from 1 to 2
    3 2026-11-02T23:11:52.138Z 493f3def-94ba-48ce-87c0-f875ad1ef80a  enabled:
    4 2026-11-02T23:11:52.783Z e3006eda-5393-4e4f-a50a-e70a761c200a  enabled:
    5 2026-11-02T23:11:54.370Z 25bd0884-4eb2-467b-9964-0d8cb0d0c876  enabled:
    6 2026-11-03T00:31:15.828Z 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879  enabled: sled af2c58b7-e6d3-4805-8969-7715fd108a8b expunged (expunged 9 disks, 62 datasets, 14 zones)

I waited for execution of that to complete:

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_executor
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_executor"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 85, triggered by a periodic timer firing
    started at 2026-11-03T00:33:41.871Z (46s ago) and ran for 1215ms
    target blueprint: 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879
    execution:        enabled
    status:           completed (16 steps)
    error:            (none)

and it's now gone:

[berlin]root@oxz_switch1:~# omdb db sleds
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
note: listing all commissioned sleds (use -F to filter, e.g. -F in-service)
 SERIAL       IP                             ROLE      POLICY      STATE   BP AVAIL   ID                                   
 BRM44220007  [fdcc:7057:faf9:103::1]:12345  -         in service  active  available  6c966dde-e798-442e-a02b-34d6c38a9b19 
 BRM42220082  [fdcc:7057:faf9:101::1]:12345  scrimlet  in service  active  available  9ccbe757-c2e6-404e-8e9e-5ee4f53595b1 
 BRM42220011  [fdcc:7057:faf9:102::1]:12345  scrimlet  in service  active  available  ec5ece3c-b8b3-4cb5-aade-1d5731306a0d 

or, rather, decommissioned:

[berlin]root@oxz_switch1:~# omdb db sleds -F all
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
 SERIAL       IP                             ROLE      POLICY      STATE           BP AVAIL        ID
 BRM44220007  [fdcc:7057:faf9:103::1]:12345  -         in service  active          available       6c966dde-e798-442e-a02b-34d6c38a9b19
 BRM42220082  [fdcc:7057:faf9:101::1]:12345  scrimlet  in service  active          available       9ccbe757-c2e6-404e-8e9e-5ee4f53595b1
 271FVPY0     [fdcc:7057:faf9:104::1]:12345  -         expunged    decommissioned  decommissioned  af2c58b7-e6d3-4805-8969-7715fd108a8b
 BRM42220011  [fdcc:7057:faf9:102::1]:12345  scrimlet  in service  active          available       ec5ece3c-b8b3-4cb5-aade-1d5731306a0d

Now, let's add it back using the documented procedure:

dap@ivanova ~ $ oxide system hardware sled list-uninitialized
[
  {
    "baseboard": {
      "part": "913-0000019",
      "revision": 6,
      "serial": "271FVPY0"
    },
    "cubby": 17,
    "rack_id": "c5e750a8-e260-44d5-99c1-a380d1194cc7"
  }
]
$ oxide system hardware sled list-uninitialized  | jq '{sled_ids: [.[] | {part_number: .baseboard.part, serial_number: .baseboard.serial}]}'  > new-sleds.json
dap@ivanova berlin-testing $ jq < new-sleds.json 
{
  "sled_ids": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    }
  ]
}
dap@ivanova berlin-testing $ oxide system hardware rack membership add --rack-id c5e750a8-e260-44d5-99c1-a380d1194cc7 --json-body new-sleds.json 
{
  "members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "rack_id": "c5e750a8-e260-44d5-99c1-a380d1194cc7",
  "state": "in_progress",
  "time_created": "2026-11-03T00:39:20.755195Z",
  "unacknowledged_members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "version": 3
}

That (and each successful "add" after this) created three new blueprints: one adds the sled with little information, one adds the internal DNS zone, and the last one adds the Crucible zones:

[berlin]root@oxz_switch1:~# omdb reconfigurator history --limit 4 --diff
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT
... (earlier history omitted)
    6 2026-11-03T00:31:15.828Z 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879  enabled: sled af2c58b7-e6d3-4805-8969-7715fd108a8b expunged (expunged 9 disks, 62 datasets, 14 zones)
    7 2026-11-03T00:39:55.055Z 62e29c88-600c-4683-9b87-ea8dbf5c7fe1  enabled:
from: blueprint 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879
to:   blueprint 62e29c88-600c-4683-9b87-ea8dbf5c7fe1

 ADDED SLEDS:

  sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c (active, config generation 1):
+   update disposition:   available (generation 1)

    measurements:
    ---------------------------
    hash      version
    ---------------------------
+   unknown   (unknown version)


    host phase 2 contents:
    ------------------------
    slot   boot image source
    ------------------------
+   A      current contents
+   B      current contents


 COCKROACHDB SETTINGS:
    state fingerprint:::::::::::::::::   d4d87aa2ad877a4cc2fddd0573952362739110de (unchanged)
    cluster.preserve_downgrade_option:   "22.1" (unchanged)

 METADATA:
*   internal DNS version:::   1 -> 2
    external DNS version:::   2 (unchanged)
    target release min gen:   2 (unchanged)
    nexus gen::::::::::::::   1 (unchanged)

 OXIMETER SETTINGS:
    generation:   1 (unchanged)
    read from::   SingleNode (unchanged)


    8 2026-11-03T00:39:55.559Z 7418e05a-7b11-4a44-8576-6b00cfe33f0c  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added 9 disks, updated 0, removed 0 disks, sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: internal_ntp
from: blueprint 62e29c88-600c-4683-9b87-ea8dbf5c7fe1
to:   blueprint 7418e05a-7b11-4a44-8576-6b00cfe33f0c

 MODIFIED SLEDS:

  sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c (active, config generation 1 -> 2):
    update disposition:   available (generation 1) (unchanged)

    measurements:
    ---------------------------
    hash      version
    ---------------------------
    unknown   (unknown version)


    host phase 2 contents:
    ------------------------
    slot   boot image source
    ------------------------
    A      current contents
    B      current contents


    physical disks:
    -------------------------------------------------
    vendor   model             serial     disposition
    -------------------------------------------------
+   1b96     WUS4C6432DSP3X3   A079DDCC   in service
+   1b96     WUS4C6432DSP3X3   A079DDE7   in service
+   1b96     WUS4C6432DSP3X3   A079DE55   in service
+   1b96     WUS4C6432DSP3X3   A079DF02   in service
+   1b96     WUS4C6432DSP3X3   A079DF08   in service
+   1b96     WUS4C6432DSP3X3   A079E3AD   in service
+   1b96     WUS4C6432DSP3X3   A079E3D2   in service
+   1b96     WUS4C6432DSP3X3   A079E567   in service
+   1b96     WUS4C6432DSP3X3   A079E665   in service


    datasets:
    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    dataset name                                                                                       dataset id                             disposition   quota     reservation   compression
    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/zone                                                a42772ea-6e42-4a04-a469-21c4025ffdbf   in service    none      none          off
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/zone                                                c0fe31ba-21d5-4ace-b212-ba4c74aaf500   in service    none      none          off
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/zone                                                152f5c53-bc7e-4114-86c7-4e1ca4ecaa62   in service    none      none          off
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/zone                                                aa492e67-c09c-441b-9db5-846d25781b48   in service    none      none          off
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/zone                                                dece9fa1-d137-43bb-8900-2f35dd888261   in service    none      none          off
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/zone                                                18abca26-fca7-4fc3-b86e-fbe07875a66b   in service    none      none          off
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/zone                                                e8c321b3-9d1f-446b-8e58-871b1497fe0d   in service    none      none          off
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/zone                                                f3214a51-6c29-48d6-a6b4-64094958b903   in service    none      none          off
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/zone                                                612cd5e2-3714-436a-8ad1-e62288f20f0b   in service    none      none          off
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/zone/oxz_ntp_5c6aa401-d8e0-4473-82bd-855ff4a503f7   4fda3bcd-ee81-4f3e-80a3-19f38a750e67   in service    none      none          off
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/debug                                               5afe570f-d392-4da4-9962-9801c7cb8f40   in service    100 GiB   none          lz4
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/debug                                               8085fda2-8736-4d72-9974-d129c7dc30b9   in service    100 GiB   none          lz4
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/debug                                               c85d1740-3d73-4da5-8e62-46435438ca23   in service    100 GiB   none          lz4
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/debug                                               b84a22b4-ad3b-4404-ab75-3437a6ba36d4   in service    100 GiB   none          lz4
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/debug                                               fa75740d-c8f7-47a4-90e0-c984078fb582   in service    100 GiB   none          lz4
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/debug                                               e060b1f8-d047-42e3-89dd-644950ead998   in service    100 GiB   none          lz4
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/debug                                               bb3bd0d1-1766-4094-9ae8-83cfd2cdac09   in service    100 GiB   none          lz4
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/debug                                               a63463ab-18ad-496f-8622-2e9b4967029f   in service    100 GiB   none          lz4
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/debug                                               a3f85789-d38c-458c-9f89-6a063d7555f4   in service    100 GiB   none          lz4
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/local_storage                                       a49ee454-7ad4-48e5-a0bc-0394ee5813ba   in service    none      none          off
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/local_storage                                       8714652f-65ae-4988-845d-ecb1c2779835   in service    none      none          off
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/local_storage                                       80fdadc1-cff9-4e33-b258-576b45ad5a16   in service    none      none          off
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/local_storage                                       db7f352c-0c8a-4614-ae29-19a9c8a3b934   in service    none      none          off
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/local_storage                                       f09fb24b-66ec-4afc-9c81-8b943840e3d2   in service    none      none          off
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/local_storage                                       55d7f856-edad-4840-82b7-0bd5e69cdb39   in service    none      none          off
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/local_storage                                       bf13437a-e09e-4a6f-8a5b-27bbfdceaf8c   in service    none      none          off
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/local_storage                                       e20cd4ae-46f2-4e30-ad91-fb01e843fd11   in service    none      none          off
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/local_storage                                       27439443-a647-4b47-a465-22ff3185b1df   in service    none      none          off
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/local_storage_unencrypted                                 fa0003ea-5f12-4c53-bd49-ba0deea6a77e   in service    none      none          off
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/local_storage_unencrypted                                 d944f527-39e8-48d5-83f7-e253459b19a0   in service    none      none          off
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/local_storage_unencrypted                                 c027bdaa-489e-421c-88f1-63056fb0d803   in service    none      none          off
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/local_storage_unencrypted                                 de78745a-e81a-41e3-94fe-66becd86b71d   in service    none      none          off
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/local_storage_unencrypted                                 a4279385-5598-4fac-b300-50a1e111a9e4   in service    none      none          off
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/local_storage_unencrypted                                 489b7c45-db90-4c52-a830-a604d3a759ae   in service    none      none          off
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/local_storage_unencrypted                                 c8cbe038-8461-44c4-be34-70bab64056ca   in service    none      none          off
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/local_storage_unencrypted                                 81ca6854-d53a-4d64-a834-aa75824efbc6   in service    none      none          off
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/local_storage_unencrypted                                 84abddde-3320-44eb-8579-07b228a615c7   in service    none      none          off


    omicron zones:
    -----------------------------------------------------------------------------------------------------------
    zone type      zone id                                image source      disposition   underlay IP
    -----------------------------------------------------------------------------------------------------------
+   internal_ntp   5c6aa401-d8e0-4473-82bd-855ff4a503f7   install dataset   in service    fdcc:7057:faf9:121::3


 COCKROACHDB SETTINGS:
    state fingerprint:::::::::::::::::   d4d87aa2ad877a4cc2fddd0573952362739110de (unchanged)
    cluster.preserve_downgrade_option:   "22.1" (unchanged)

 METADATA:
    internal DNS version:::   2 (unchanged)
    external DNS version:::   2 (unchanged)
    target release min gen:   2 (unchanged)
    nexus gen::::::::::::::   1 (unchanged)

 OXIMETER SETTINGS:
    generation:   1 (unchanged)
    read from::   SingleNode (unchanged)


    9 2026-11-03T00:40:21.163Z c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: crucible
from: blueprint 7418e05a-7b11-4a44-8576-6b00cfe33f0c
to:   blueprint c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b

 MODIFIED SLEDS:

  sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c (active, config generation 2 -> 3):
    update disposition:   available (generation 1) (unchanged)

    measurements:
    ---------------------------
    hash      version
    ---------------------------
    unknown   (unknown version)


    host phase 2 contents:
    ------------------------
    slot   boot image source
    ------------------------
    A      current contents
    B      current contents


    physical disks:
    -------------------------------------------------
    vendor   model             serial     disposition
    -------------------------------------------------
    1b96     WUS4C6432DSP3X3   A079DDCC   in service
    1b96     WUS4C6432DSP3X3   A079DDE7   in service
    1b96     WUS4C6432DSP3X3   A079DE55   in service
    1b96     WUS4C6432DSP3X3   A079DF02   in service
    1b96     WUS4C6432DSP3X3   A079DF08   in service
    1b96     WUS4C6432DSP3X3   A079E3AD   in service
    1b96     WUS4C6432DSP3X3   A079E3D2   in service
    1b96     WUS4C6432DSP3X3   A079E567   in service
    1b96     WUS4C6432DSP3X3   A079E665   in service


    datasets:
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    dataset name                                                                                            dataset id                             disposition   quota     reservation   compression
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/zone                                                     a42772ea-6e42-4a04-a469-21c4025ffdbf   in service    none      none          off
    oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/zone                                                     c0fe31ba-21d5-4ace-b212-ba4c74aaf500   in service    none      none          off
    oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/zone                                                     152f5c53-bc7e-4114-86c7-4e1ca4ecaa62   in service    none      none          off
    oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/zone                                                     aa492e67-c09c-441b-9db5-846d25781b48   in service    none      none          off
    oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/zone                                                     dece9fa1-d137-43bb-8900-2f35dd888261   in service    none      none          off
    oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/zone                                                     18abca26-fca7-4fc3-b86e-fbe07875a66b   in service    none      none          off
    oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/zone                                                     e8c321b3-9d1f-446b-8e58-871b1497fe0d   in service    none      none          off
    oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/zone                                                     f3214a51-6c29-48d6-a6b4-64094958b903   in service    none      none          off
    oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/zone                                                     612cd5e2-3714-436a-8ad1-e62288f20f0b   in service    none      none          off
    oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/zone/oxz_ntp_5c6aa401-d8e0-4473-82bd-855ff4a503f7        4fda3bcd-ee81-4f3e-80a3-19f38a750e67   in service    none      none          off
    oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/debug                                                    5afe570f-d392-4da4-9962-9801c7cb8f40   in service    100 GiB   none          lz4
    oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/debug                                                    8085fda2-8736-4d72-9974-d129c7dc30b9   in service    100 GiB   none          lz4
    oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/debug                                                    c85d1740-3d73-4da5-8e62-46435438ca23   in service    100 GiB   none          lz4
    oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/debug                                                    b84a22b4-ad3b-4404-ab75-3437a6ba36d4   in service    100 GiB   none          lz4
    oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/debug                                                    fa75740d-c8f7-47a4-90e0-c984078fb582   in service    100 GiB   none          lz4
    oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/debug                                                    e060b1f8-d047-42e3-89dd-644950ead998   in service    100 GiB   none          lz4
    oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/debug                                                    bb3bd0d1-1766-4094-9ae8-83cfd2cdac09   in service    100 GiB   none          lz4
    oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/debug                                                    a63463ab-18ad-496f-8622-2e9b4967029f   in service    100 GiB   none          lz4
    oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/debug                                                    a3f85789-d38c-458c-9f89-6a063d7555f4   in service    100 GiB   none          lz4
    oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/local_storage                                            a49ee454-7ad4-48e5-a0bc-0394ee5813ba   in service    none      none          off
    oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/local_storage                                            8714652f-65ae-4988-845d-ecb1c2779835   in service    none      none          off
    oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/local_storage                                            80fdadc1-cff9-4e33-b258-576b45ad5a16   in service    none      none          off
    oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/local_storage                                            db7f352c-0c8a-4614-ae29-19a9c8a3b934   in service    none      none          off
    oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/local_storage                                            f09fb24b-66ec-4afc-9c81-8b943840e3d2   in service    none      none          off
    oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/local_storage                                            55d7f856-edad-4840-82b7-0bd5e69cdb39   in service    none      none          off
    oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/local_storage                                            bf13437a-e09e-4a6f-8a5b-27bbfdceaf8c   in service    none      none          off
    oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/local_storage                                            e20cd4ae-46f2-4e30-ad91-fb01e843fd11   in service    none      none          off
    oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/local_storage                                            27439443-a647-4b47-a465-22ff3185b1df   in service    none      none          off
    oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/local_storage_unencrypted                                      fa0003ea-5f12-4c53-bd49-ba0deea6a77e   in service    none      none          off
    oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/local_storage_unencrypted                                      d944f527-39e8-48d5-83f7-e253459b19a0   in service    none      none          off
    oxp_282900b8-091c-4a02-980c-76533cd5e84a/local_storage_unencrypted                                      c027bdaa-489e-421c-88f1-63056fb0d803   in service    none      none          off
    oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/local_storage_unencrypted                                      de78745a-e81a-41e3-94fe-66becd86b71d   in service    none      none          off
    oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/local_storage_unencrypted                                      a4279385-5598-4fac-b300-50a1e111a9e4   in service    none      none          off
    oxp_ad2f5050-33e4-411e-bc56-f687df31013e/local_storage_unencrypted                                      489b7c45-db90-4c52-a830-a604d3a759ae   in service    none      none          off
    oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/local_storage_unencrypted                                      c8cbe038-8461-44c4-be34-70bab64056ca   in service    none      none          off
    oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/local_storage_unencrypted                                      81ca6854-d53a-4d64-a834-aa75824efbc6   in service    none      none          off
    oxp_dc183145-2bb0-4b07-9000-c5989493bd56/local_storage_unencrypted                                      84abddde-3320-44eb-8579-07b228a615c7   in service    none      none          off
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crucible                                                       7d092683-4cc2-4e44-b3a0-a8e867e5edc3   in service    none      none          off
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crucible                                                       9f5c2364-2b54-4151-a0c1-3aef055c866e   in service    none      none          off
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/crucible                                                       7e216004-23a1-4612-9764-8739a0e1d372   in service    none      none          off
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crucible                                                       710ae1d8-eb87-4499-b638-54fe8ed4ff94   in service    none      none          off
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crucible                                                       18868f78-e121-4fe5-bee9-3af2445715c0   in service    none      none          off
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crucible                                                       10d4d379-c3ae-40cf-8994-98eae409164f   in service    none      none          off
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crucible                                                       bacbd98c-3b89-45d7-9c76-17a6f513d44a   in service    none      none          off
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crucible                                                       cef93582-c573-4a11-981f-64b401cd5d64   in service    none      none          off
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crucible                                                       3dc68690-5f9b-4c15-a3bd-91bf61ee4f67   in service    none      none          off
+   oxp_d7e67da4-5e98-4c2c-8857-949d6572b3c3/crypt/zone/oxz_crucible_19269a25-de12-40a4-9589-6c3d10a6ad6b   fc86921d-f6f8-4e4f-851b-5eb7a30f355e   in service    none      none          off
+   oxp_282900b8-091c-4a02-980c-76533cd5e84a/crypt/zone/oxz_crucible_1aae3c6d-72b0-4e2f-9c27-ec9b7c6b8f2c   04036a53-9de1-4ef0-bfe1-cd8281b933ff   in service    none      none          off
+   oxp_6b561c72-3d96-4c43-b668-0e236a6e138b/crypt/zone/oxz_crucible_26ea95af-ac94-417b-bac7-164587139b2b   2e2850a6-156f-4fe3-8c8f-ae62c88d9875   in service    none      none          off
+   oxp_056b7a86-a128-4524-b2e7-4b60bf5c0ac9/crypt/zone/oxz_crucible_63ce5ca2-a8e3-4212-97a7-cd4991e3d6e8   bf494db7-91a5-4de6-8099-576707107b48   in service    none      none          off
+   oxp_53992e4c-9d51-4b4d-8fbd-e18e5f19296f/crypt/zone/oxz_crucible_83f2df86-8920-46ef-872a-b9f6074dd430   8ba725b0-6e2d-44ef-afaf-f59519046a5b   in service    none      none          off
+   oxp_1a64a5ae-00fa-4829-b9b7-8ac0f82d335b/crypt/zone/oxz_crucible_af6704f5-b468-4912-92c0-8e0c63e5dc9f   ca6476a0-b0b8-46cf-898e-9c55dff9c39e   in service    none      none          off
+   oxp_dc183145-2bb0-4b07-9000-c5989493bd56/crypt/zone/oxz_crucible_c489b3d3-0201-4a17-accb-86894f2a6124   f746561f-13d6-457a-adc5-04072c448abd   in service    none      none          off
+   oxp_ad2f5050-33e4-411e-bc56-f687df31013e/crypt/zone/oxz_crucible_d0d57b66-471f-4429-839b-4d09a789f1ba   b8226e87-325e-4a55-a446-d25671880733   in service    none      none          off
+   oxp_d4cc8ca4-85a6-42f6-96ba-537d3db971ec/crypt/zone/oxz_crucible_e774f29d-61de-4c19-8ba7-cb9ccf02a7e0   ee5477c5-9a11-4eda-8ae1-40b51d494d9c   in service    none      none          off


    omicron zones:
    -----------------------------------------------------------------------------------------------------------
    zone type      zone id                                image source      disposition   underlay IP
    -----------------------------------------------------------------------------------------------------------
    internal_ntp   5c6aa401-d8e0-4473-82bd-855ff4a503f7   install dataset   in service    fdcc:7057:faf9:121::3
+   crucible       19269a25-de12-40a4-9589-6c3d10a6ad6b   install dataset   in service    fdcc:7057:faf9:121::b
+   crucible       1aae3c6d-72b0-4e2f-9c27-ec9b7c6b8f2c   install dataset   in service    fdcc:7057:faf9:121::6
+   crucible       26ea95af-ac94-417b-bac7-164587139b2b   install dataset   in service    fdcc:7057:faf9:121::8
+   crucible       63ce5ca2-a8e3-4212-97a7-cd4991e3d6e8   install dataset   in service    fdcc:7057:faf9:121::4
+   crucible       83f2df86-8920-46ef-872a-b9f6074dd430   install dataset   in service    fdcc:7057:faf9:121::7
+   crucible       af6704f5-b468-4912-92c0-8e0c63e5dc9f   install dataset   in service    fdcc:7057:faf9:121::5
+   crucible       c489b3d3-0201-4a17-accb-86894f2a6124   install dataset   in service    fdcc:7057:faf9:121::c
+   crucible       d0d57b66-471f-4429-839b-4d09a789f1ba   install dataset   in service    fdcc:7057:faf9:121::9
+   crucible       e774f29d-61de-4c19-8ba7-cb9ccf02a7e0   install dataset   in service    fdcc:7057:faf9:121::a


 COCKROACHDB SETTINGS:
    state fingerprint:::::::::::::::::   d4d87aa2ad877a4cc2fddd0573952362739110de (unchanged)
    cluster.preserve_downgrade_option:   "22.1" (unchanged)

 METADATA:
*   internal DNS version:::   2 -> 3
    external DNS version:::   2 (unchanged)
    target release min gen:   2 (unchanged)
    nexus gen::::::::::::::   1 (unchanged)

 OXIMETER SETTINGS:
    generation:   1 (unchanged)
    read from::   SingleNode (unchanged)

Once again, I checked that the system was at rest by checking the executor had executed the blueprint with no errors and the planner isn't waiting for anything (at least, not for anything to execute):

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_executor
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_executor"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 95, triggered by a periodic timer firing
    started at 2026-11-03T00:41:41.890Z (2s ago) and ran for 1259ms
    target blueprint: c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b
    execution:        enabled
    status:           completed (16 steps)
    error:            (none)

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_planner
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_planner"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 303, triggered by a periodic timer firing
    started at 2026-11-03T00:41:33.499Z (16s ago) and ran for 148ms
    plan unchanged from parent c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b
    note: 9/5000 blueprints in database
planning report:
* zone adds and updates are blocked:
  - current target release generation (1) is lower than minimum required by blueprint (2)
  - sleds have deployment units with image sources not set to Artifact:
    - sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: 10 zones
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19: 18 zones
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: 16 zones
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: 16 zones

  - sleds have measurements with image sources not set to Artifact:
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19 set to install dataset
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1 set to install dataset
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d set to install dataset

* adding zones despite being blocked, because: target release generation is 1
* zone updates waiting on zone add blockers
* will ensure cockroachdb setting: "22.1"
Measurement updates:
Waiting on zone add/update blockers

No services in maintenance, and each sled has a bunch of zones:

[berlin]root@oxz_switch1:~# pilot host exec -c 'svcs -Zxv' 14-17
14  BRM42220011        ok: 
15  BRM44220007        ok: 
16  BRM42220082        ok: 
17  271FVPY0           ok: 
[berlin]root@oxz_switch1:~# pilot host exec -c 'zoneadm list' 14-17
14  BRM42220011        ok: global
oxz_switch
oxz_internal_dns_7d746646-eee8-43c6-8756-441ef846e471
oxz_ntp_63280113-c1c1-47a4-921c-1c7d4a451866
oxz_cockroachdb_5fdaf88a-63b4-49dc-a80c-030459ac804a
oxz_external_dns_e3db649e-4761-4ed3-b517-7a7608f7a007
oxz_crucible_pantry_dfc0c895-a84d-44d1-a3b5-3586fee7b3cd
oxz_crucible_6881aa3a-e23b-40be-8195-af269a74e492
oxz_crucible_843bd6e5-54df-4247-a455-8faf4e2bb589
oxz_crucible_966eba83-2804-4372-a72b-f8994783d8af
oxz_crucible_182d9aae-52fb-4f84-b2a5-2ef2693eca3d
oxz_crucible_bba563fa-c0fc-447e-8e49-a028ce97a49f
oxz_crucible_f69c7ad0-92c3-498d-a67c-4d001bc259d5
oxz_crucible_982aefe7-f2b6-411f-a5b1-43a54a2d2b7b
oxz_crucible_4244ba9c-0591-49fc-a39f-0b5cd0eb9eb6
oxz_crucible_bb29dfbd-3480-4325-bd2f-d4ddcf92f79d
oxz_nexus_b832d789-60dd-4527-bdb0-8b2f1aab7f43
oxz_cockroachdb_153b0a09-60ae-4562-8ac2-7a69e69d8bca
15  BRM44220007        ok: global
oxz_internal_dns_539b8983-88a2-4f84-9152-fee78c9c297f
oxz_ntp_b06a85a8-59fb-4602-b833-a1e93ae0442f
oxz_cockroachdb_e9d34925-f636-40c1-8c35-cae1ae76761f
oxz_oximeter_9241cae4-c07e-420e-b5a2-e0ed5dc84c6b
oxz_external_dns_b2e11991-ab17-4401-a5e5-0849dc7f20bf
oxz_crucible_pantry_79c02ca7-c08e-4518-9bdb-f5c9db3ab990
oxz_crucible_26816c5c-67a8-46ae-ba0e-17e9f3fe46b8
oxz_crucible_abdcb8e5-6d18-4418-affe-3d3079a4f26d
oxz_crucible_7009bc54-e2e2-444b-81b6-727e7caeb470
oxz_crucible_d125e340-0be2-48f9-a48b-daedcdbe0c2f
oxz_crucible_4469061e-d619-494a-a95a-d4d55ca14658
oxz_crucible_f7924800-7a3c-49ae-b913-c7e66aaa3159
oxz_crucible_817700b2-8755-4cfa-96ee-4c716f52dc28
oxz_crucible_ef7a3034-d6f3-45cd-b681-ec5c2983ee91
oxz_crucible_e496df4c-a1e2-472c-9314-e9d74363d6cb
oxz_crucible_2c95afef-2830-48bd-b49b-115428e9b89e
oxz_nexus_00adb948-783f-4a1b-9d74-321f1f198cdc
oxz_clickhouse_d6a22749-c7d7-49f2-b919-95fa74125ccb
16  BRM42220082        ok: global
oxz_switch
oxz_internal_dns_80358eee-6633-400a-9535-098adec2325e
oxz_ntp_4c0175cb-a0a9-4124-be4d-26ff2a54f59e
oxz_cockroachdb_2dbe0dd4-aa1a-4a27-b928-0c76ec168fe8
oxz_external_dns_f0943a4d-2b4e-4dd9-984f-22fb1cdfdae4
oxz_crucible_pantry_efb6b5b2-e6df-4dc4-8d71-561183d1b393
oxz_crucible_49b58166-ff08-4507-8c20-fa653124eeac
oxz_crucible_d6ca48da-51c3-4c82-8198-5966b735b646
oxz_crucible_44287193-4f76-4dfa-83e5-203cbfe6bb96
oxz_crucible_79c51af8-7757-4797-bd7c-30c4ec8dc053
oxz_crucible_4d077427-c6c5-45dd-8170-b785927c51f2
oxz_crucible_ca40fab3-f264-447d-9154-53dde7d14b02
oxz_crucible_77af1c64-6113-4a2b-80ee-57be489abb70
oxz_crucible_37851b26-fc30-46bb-b0a9-b2d5def5e27d
oxz_crucible_72f728b2-8076-49ed-9719-606f2d79116a
oxz_nexus_40ce022a-dfb0-4218-8a78-8d7aad87bdfc
oxz_cockroachdb_ee8fc216-c815-4aac-87b3-0a01b4bd6fba
17  271FVPY0           ok: global
oxz_ntp_5c6aa401-d8e0-4473-82bd-855ff4a503f7
oxz_crucible_af6704f5-b468-4912-92c0-8e0c63e5dc9f
oxz_crucible_d0d57b66-471f-4429-839b-4d09a789f1ba
oxz_crucible_63ce5ca2-a8e3-4212-97a7-cd4991e3d6e8
oxz_crucible_83f2df86-8920-46ef-872a-b9f6074dd430
oxz_crucible_e774f29d-61de-4c19-8ba7-cb9ccf02a7e0
oxz_crucible_19269a25-de12-40a4-9589-6c3d10a6ad6b
oxz_crucible_c489b3d3-0201-4a17-accb-86894f2a6124
oxz_crucible_1aae3c6d-72b0-4e2f-9c27-ec9b7c6b8f2c
oxz_crucible_26ea95af-ac94-417b-bac7-164587139b2b

Following my test plan above, I expunged the sled again. Then I did a MUPdate recovery with the wrong repo (which I'd previously uploaded):

dap@ivanova berlin-testing $ oxide system update recovery-finish --system-version 23.0.0-0.ci+gite1b07ab75f5

I was initially surprised that this made no blueprints. It did fill out the Update page in the console with a target release, etc. And it makes sense there'd be no blueprints. The blueprints we normally expect during MUPdate resolution are one that clears the MUPdate override (which was already done in this case a while ago, right after RSS) and one that resolves artifacts (which hasn't happened yet since it's the wrong repo).

The planning report looks very subtly different:

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_planner
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_planner"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 344, triggered by a dependent task completing
    started at 2026-11-03T00:53:02.697Z (4s ago) and ran for 534ms
    plan unchanged from parent 26afb2c3-f05d-43b9-9082-d8582283e342
    note: 10/5000 blueprints in database
planning report:
* zone adds waiting on blockers
* zone adds and updates are blocked:
  - sleds have deployment units with image sources not set to Artifact:
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19: 18 zones
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: 16 zones
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: 16 zones

  - sleds have measurements with image sources not set to Artifact:
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19 set to install dataset
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1 set to install dataset
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d set to install dataset

* zone updates waiting on zone add blockers
* waiting to update top-level nexus_generation: some non-Nexus zone are not yet updated
* will ensure cockroachdb setting: "22.1"
Measurement updates:
Waiting on zone add/update blockers

It's no longer complaining about the target release generation.

I then added the sled again:

dap@ivanova berlin-testing $ oxide system hardware rack membership add --rack-id c5e750a8-e260-44d5-99c1-a380d1194cc7 --json-body new-sleds.json
error
Error Response: status: 400 Bad Request; headers: {"content-type": "application/json", "x-request-id": "276a0a80-e674-4b84-adc8-ff39b3c3c068", "content-length": "250", "date": "Tue, 03 Nov 2026 00:54:21 GMT"}; value: Error { error_code: Some("InvalidRequest"), message: "sled cannot be added: a support-driven recovery (mupdate) has occurred on sled 6c966dde-e798-442e-a02b-34d6c38a9b19 and must be cleared first", request_id: "276a0a80-e674-4b84-adc8-ff39b3c3c068" }

Excellent -- this is what we should expect when the target release has been set but there's an uncompleted MUPdate recovery. I found at this point that I had to switch to oxide api in order to pass the overrides I wanted. (I had tried putting them into new-sleds.json directly, but that didn't work. The CLI seems to ignore fields here that it doesn't know about.) So let's try that again with oxide api. First, use --debug to get the request details:

dap@ivanova berlin-testing $ oxide --debug system hardware rack membership add --rack-id c5e750a8-e260-44d5-99c1-a380d1194cc7 --json-body new-sleds-override-both.json
{"timestamp":"2026-11-03T01:02:49.436120Z","level":"DEBUG","message":"request succeeded","url":"https://recovery.sys.berlin.eng.oxide.computer/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add","path":"/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add","operation_id":"rack_membership_add_sleds","remote_addr":"172.20.37.6:443","http.request.method":"POST","http.request.body":"{\"sled_ids\":[{\"part_number\":\"913-0000019\",\"serial_number\":\"271FVPY0\"}]}","http.response.content_length":250,"http.response.status_code":400,"start_time":"2026-11-03T01:02:48.961802Z","duration_ms":474,"oxide.request_id":"c9a5b352-d6e6-455b-8d2f-f67e698d5c65","target":"oxide"}
error
Error Response: status: 400 Bad Request; headers: {"content-type": "application/json", "x-request-id": "c9a5b352-d6e6-455b-8d2f-f67e698d5c65", "content-length": "250", "date": "Tue, 03 Nov 2026 01:02:49 GMT"}; value: Error { error_code: Some("InvalidRequest"), message: "sled cannot be added: a support-driven recovery (mupdate) has occurred on sled 6c966dde-e798-442e-a02b-34d6c38a9b19 and must be cleared first", request_id: "c9a5b352-d6e6-455b-8d2f-f67e698d5c65" }

and:

dap@ivanova berlin-testing $ cat | jq
{"timestamp":"2026-11-03T01:02:49.436120Z","level":"DEBUG","message":"request succeeded","url":"https://recovery.sys.berlin.eng.oxide.computer/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add","path":"/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add","operation_id":"rack_membership_add_sleds","remote_addr":"172.20.37.6:443","http.request.method":"POST","http.request.body":"{\"sled_ids\":[{\"part_number\":\"913-0000019\",\"serial_number\":\"271FVPY0\"}]}","http.response.content_length":250,"http.response.status_code":400,"start_time":"2026-11-03T01:02:48.961802Z","duration_ms":474,"oxide.request_id":"c9a5b352-d6e6-455b-8d2f-f67e698d5c65","target":"oxide"}
{
  "timestamp": "2026-11-03T01:02:49.436120Z",
  "level": "DEBUG",
  "message": "request succeeded",
  "url": "https://recovery.sys.berlin.eng.oxide.computer/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add",
  "path": "/v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add",
  "operation_id": "rack_membership_add_sleds",
  "remote_addr": "172.20.37.6:443",
  "http.request.method": "POST",
  "http.request.body": "{\"sled_ids\":[{\"part_number\":\"913-0000019\",\"serial_number\":\"271FVPY0\"}]}",
  "http.response.content_length": 250,
  "http.response.status_code": 400,
  "start_time": "2026-11-03T01:02:48.961802Z",
  "duration_ms": 474,
  "oxide.request_id": "c9a5b352-d6e6-455b-8d2f-f67e698d5c65",
  "target": "oxide"
}

I have created three files that differ from new-sleds.json solely in the overrides:

$ cat new-sleds-override-system.json 
{
  "override_prechecks": {
      "allow_unverified_sled_software": false,
      "allow_without_settled_target_release": true
  },
  "sled_ids": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    }
  ]
}
$ cat new-sleds-override-version.json 
{
  "override_prechecks": {
      "allow_unverified_sled_software": true,
      "allow_without_settled_target_release": false
  },
  "sled_ids": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    }
  ]
}
$ cat new-sleds-override-both.json 
{
  "override_prechecks": {
      "allow_unverified_sled_software": true,
      "allow_without_settled_target_release": true
  },
  "sled_ids": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    }
  ]
}

Now we can start using them with oxide api. First, the one with no overrides fails because of the pending MUPdate:

$ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds.json
error; status code: 400 Bad Request
{
  "error_code": "InvalidRequest",
  "message": "sled cannot be added: a support-driven recovery (mupdate) has occurred on sled 6c966dde-e798-442e-a02b-34d6c38a9b19 and must be cleared first",
  "request_id": "6674d23d-f828-4752-b1e1-7e52eddc7c4f"
}

Now, if we override the MUPdate check, we get a version mismatch:

$ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds-override-system.json
error; status code: 400 Bad Request
{
  "error_code": "InvalidRequest",
  "message": "sled cannot be added: the software on sled 913-0000019:271FVPY0 does not match target release 23.0.0-0.ci+gite1b07ab75f5: SP is running 1.84.0 (expected 1.79.0); Host OS is running fb02e4929c7484960cf9c95fd5b554beefe4469dffedc2dd218bc1ffa31d9174 (expected 0c7f9e658141f853ec1fe07717c356ce4f679f6ac0c992382e52774daec6392f)",
  "request_id": "6e509fe7-4b5d-4470-8402-51c488070ef3"
}
error

Here, it's comparing the version deployed to the one in the target release. Remember that the target release is wrong -- I deliberately used the wrong TUF repo for the first MUPdate recovery. So that message is right: the sled is running a different SP version than expected. If we override just that, we get the MUPdate error again:

$ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds-override-version.json
error; status code: 400 Bad Request
{
  "error_code": "InvalidRequest",
  "message": "sled cannot be added: a support-driven recovery (mupdate) has occurred on sled 6c966dde-e798-442e-a02b-34d6c38a9b19 and must be cleared first",
  "request_id": "a46ea7d0-960a-46e6-8feb-4c4ce04952ff"
}
error

But if we override both, we can add the sled:

$ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds-override-both.json
{
  "members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "rack_id": "c5e750a8-e260-44d5-99c1-a380d1194cc7",
  "state": "in_progress",
  "time_aborted": null,
  "time_committed": null,
  "time_created": "2026-11-03T01:10:34.898726Z",
  "unacknowledged_members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "version": 5
}

Great.

Now, I was surprised that in this case, Reconfigurator wrote one blueprint to add the sled, but didn't add any zones. I believe this is actually expected, though, because we don't proceed with "add" operations when there's been a MUPdate.

[berlin]root@oxz_switch1:~# omdb reconfigurator history
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT
    1 2026-11-02T23:11:39.511Z 02ce1072-3ebd-42e8-9210-0380550f2c46  enabled: initial blueprint from rack setup
    2 2026-11-02T23:11:42.518Z 4fe7b962-9efe-4f8a-918d-9abfbe689e6b  enabled: updated target release minimum generation from 1 to 2
    3 2026-11-02T23:11:52.138Z 493f3def-94ba-48ce-87c0-f875ad1ef80a  enabled:
    4 2026-11-02T23:11:52.783Z e3006eda-5393-4e4f-a50a-e70a761c200a  enabled:
    5 2026-11-02T23:11:54.370Z 25bd0884-4eb2-467b-9964-0d8cb0d0c876  enabled:
    6 2026-11-03T00:31:15.828Z 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879  enabled: sled af2c58b7-e6d3-4805-8969-7715fd108a8b expunged (expunged 9 disks, 62 datasets, 14 zones)
    7 2026-11-03T00:39:55.055Z 62e29c88-600c-4683-9b87-ea8dbf5c7fe1  enabled:
    8 2026-11-03T00:39:55.559Z 7418e05a-7b11-4a44-8576-6b00cfe33f0c  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added 9 disks, updated 0, removed 0 disks, sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: internal_ntp
    9 2026-11-03T00:40:21.163Z c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: crucible
   10 2026-11-03T00:47:50.289Z 26afb2c3-f05d-43b9-9082-d8582283e342  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c expunged (expunged 9 disks, 55 datasets, 10 zones)
   11 2026-11-03T01:11:45.394Z 2c878d49-3d68-4db7-8cec-6f331f6b77eb  enabled:
[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_executor
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_executor"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 128, triggered by a periodic timer firing
    started at 2026-11-03T01:12:41.965Z (55s ago) and ran for 1471ms
    target blueprint: 2c878d49-3d68-4db7-8cec-6f331f6b77eb
    execution:        enabled
    status:           completed (16 steps)
    error:            (none)

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_planner
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_planner"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 411, triggered by a periodic timer firing
    started at 2026-11-03T01:13:33.575Z (6s ago) and ran for 243ms
    plan unchanged from parent 2c878d49-3d68-4db7-8cec-6f331f6b77eb
    note: 11/5000 blueprints in database
planning report:
* zone adds waiting on blockers
* zone adds and updates are blocked:
  - sleds have deployment units with image sources not set to Artifact:
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19: 18 zones
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: 16 zones
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: 16 zones

  - sleds have measurements with image sources not set to Artifact:
    - sled 6c966dde-e798-442e-a02b-34d6c38a9b19 set to install dataset
    - sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1 set to install dataset
    - sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d set to install dataset

* zone updates waiting on zone add blockers
* waiting to update top-level nexus_generation: some non-Nexus zone are not yet updated
* will ensure cockroachdb setting: "22.1"
Measurement updates:
Waiting on zone add/update blockers

If that explanation is right, then doing MUPdate recovery with the right TUF repo should immediately cause it to go deploy things. Sure enough:

$ oxide system update recovery-finish --system-version 24.0.0-0.ci+gita166dd1e7e9
[berlin]root@oxz_switch1:~# omdb reconfigurator history
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT
    1 2026-11-02T23:11:39.511Z 02ce1072-3ebd-42e8-9210-0380550f2c46  enabled: initial blueprint from rack setup
    2 2026-11-02T23:11:42.518Z 4fe7b962-9efe-4f8a-918d-9abfbe689e6b  enabled: updated target release minimum generation from 1 to 2
    3 2026-11-02T23:11:52.138Z 493f3def-94ba-48ce-87c0-f875ad1ef80a  enabled:
    4 2026-11-02T23:11:52.783Z e3006eda-5393-4e4f-a50a-e70a761c200a  enabled:
    5 2026-11-02T23:11:54.370Z 25bd0884-4eb2-467b-9964-0d8cb0d0c876  enabled:
    6 2026-11-03T00:31:15.828Z 28feeeeb-0b36-4b95-bbd6-0ff08c7c9879  enabled: sled af2c58b7-e6d3-4805-8969-7715fd108a8b expunged (expunged 9 disks, 62 datasets, 14 zones)
    7 2026-11-03T00:39:55.055Z 62e29c88-600c-4683-9b87-ea8dbf5c7fe1  enabled:
    8 2026-11-03T00:39:55.559Z 7418e05a-7b11-4a44-8576-6b00cfe33f0c  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added 9 disks, updated 0, removed 0 disks, sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: internal_ntp
    9 2026-11-03T00:40:21.163Z c1d6cd35-f7ea-4b31-80ad-be6ef1020f9b  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c: added zone: crucible
   10 2026-11-03T00:47:50.289Z 26afb2c3-f05d-43b9-9082-d8582283e342  enabled: sled 1d54d23c-2133-4050-ab14-fad8a2fcfd2c expunged (expunged 9 disks, 55 datasets, 10 zones)
   11 2026-11-03T01:11:45.394Z 2c878d49-3d68-4db7-8cec-6f331f6b77eb  enabled:
   12 2026-11-03T01:14:53.791Z 69561526-8f79-40e1-9321-5de79a04c15d  enabled: sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5: noop updated host phase 2 to Artifact: both slot A and slot B, sled 6c966dde-e798-442e-a02b-34d6c38a9b19: performed all noop measurements updates, sled 6c966dde-e798-442e-a02b-34d6c38a9b19: performed 18 noop zone image source updates, sled 6c966dde-e798-442e-a02b-34d6c38a9b19: noop updated host phase 2 to Artifact: both slot A and slot B, sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: performed all noop measurements updates, sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: performed 16 noop zone image source updates, sled 9ccbe757-c2e6-404e-8e9e-5ee4f53595b1: noop updated host phase 2 to Artifact: both slot A and slot B, sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: performed all noop measurements updates, sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: performed 16 noop zone image source updates, sled ec5ece3c-b8b3-4cb5-aade-1d5731306a0d: noop updated host phase 2 to Artifact: both slot A and slot B, sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5: added 9 disks, updated 0, removed 0 disks, sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5: added zone: internal_ntp
   13 2026-11-03T01:15:08.063Z c18ab07f-70ec-4f04-a035-d10ff73b486a  enabled: sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5: added zone: crucible

and we're at rest, with a much cleaner planning report, since we're now at a normal situation with Reconfigurator in charge:

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_planner
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_planner"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 428, triggered by a dependent task completing
    started at 2026-11-03T01:17:02.751Z (8s ago) and ran for 366ms
    plan unchanged from parent c18ab07f-70ec-4f04-a035-d10ff73b486a
    note: 13/5000 blueprints in database
planning report:
* will ensure cockroachdb setting: "22.1"

I expunged the sled again:

[berlin]root@oxz_switch1:~# omdb reconfigurator history --limit 2
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT                           
... (earlier history omitted)
   13 2026-11-03T01:15:08.063Z c18ab07f-70ec-4f04-a035-d10ff73b486a  enabled: sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5: added zone: crucible
   14 2026-11-03T01:20:55.493Z 1d110cc5-7a31-4a5e-b6a7-55df79a42b49  enabled: sled 05d3df4e-66e1-4c24-8618-f4467bad4ed5 expunged (expunged 9 disks, 55 datasets, 10 zones)

Re-adding in this state should work. Again, this is a normal state, similar to being between self-service upgrades. And the sled has the right software on it. No overrides should be required:

dap@ivanova berlin-testing $ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds.json
{
  "members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "rack_id": "c5e750a8-e260-44d5-99c1-a380d1194cc7",
  "state": "in_progress",
  "time_aborted": null,
  "time_committed": null,
  "time_created": "2026-11-03T01:23:21.844760Z",
  "unacknowledged_members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "version": 7
}

This worked. It took a little longer than expected to move through the blueprints, but this was because TUF artifact replication was bogged down trying to talk to now-expunged sleds. This cleared up as expected.

I expunged the sled again.

[berlin]root@oxz_switch1:~# omdb reconfigurator history --limit 3
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
VERSN TIME                     BLUEPRINT                           
... (earlier history omitted)
   16 2026-11-03T01:23:58.183Z b0600fe8-ff85-4bf6-a9b2-9e925cda4796  enabled: sled a8b2f303-8588-471c-916b-16a5a5306d81: added 9 disks, updated 0, removed 0 disks, sled a8b2f303-8588-471c-916b-16a5a5306d81: added zone: internal_ntp
   17 2026-11-03T01:27:55.136Z 6f1dfce5-2975-4740-873a-9f38658a04fc  enabled: sled a8b2f303-8588-471c-916b-16a5a5306d81: noop updated host phase 2 to Artifact: both slot A and slot B, sled a8b2f303-8588-471c-916b-16a5a5306d81: added zone: crucible
   18 2026-11-03T01:32:13.119Z a8d3d0c2-5a86-45f7-8cd1-3316837c5a89  enabled: sled a8b2f303-8588-471c-916b-16a5a5306d81 expunged (expunged 9 disks, 55 datasets, 10 zones)

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_executor
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:103::13]:12232
task: "blueprint_executor"
  configured period: every 1m
  currently executing: iter 73, triggered by a periodic timer firing
    started at 2026-11-03T01:32:51.087Z, running for 636ms
  last completed activation: iter 72, triggered by a dependent task completing
    started at 2026-11-03T01:32:50.058Z (1s ago) and ran for 1028ms
    target blueprint: a8d3d0c2-5a86-45f7-8cd1-3316837c5a89 
    execution:        enabled                              
    status:           completed (16 steps)                 
    error:            (none)                               

[berlin]root@oxz_switch1:~# omdb nexus background-tasks show blueprint_planner
note: Nexus URL not specified.  Will pick one from DNS.
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using Nexus URL http://[fdcc:7057:faf9:101::5]:12232
task: "blueprint_planner"
  configured period: every 1m
  currently executing: no
  last completed activation: iter 486, triggered by a periodic timer firing
    started at 2026-11-03T01:32:33.620Z (25s ago) and ran for 399ms
    plan unchanged from parent a8d3d0c2-5a86-45f7-8cd1-3316837c5a89
    note: 18/5000 blueprints in database
planning report:
* will ensure cockroachdb setting: "22.1"

At this point, the last test is to verify that we get the expected error if we put a different SP image on there. (I tried a different RoT image at first, but we haven't changed the RoT image in months and I didn't have a different one handy.)

[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 state
Nov 03 03:08:51.851 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 03:08:52.096 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 03:08:52.103 INFO V2(SpStateV2 { hubris_archive_id: [154, 198, 136, 176, 128, 132, 93, 83], serial_number: [50, 55, 49, 70, 86, 80, 89, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], model: [57, 49, 51, 45, 48, 48, 48, 48, 48, 49, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], revision: 6, base_mac_address: [168, 64, 37, 4, 40, 199], power_state: A0, rot: Ok(RotStateV2 { active: A, persistent_boot_preference: A, pending_persistent_boot_preference: None, transient_boot_preference: None, slot_a_sha3_256_digest: Some([201, 38, 171, 16, 31, 97, 178, 116, 67, 33, 108, 95, 6, 139, 242, 20, 52, 241, 22, 7, 167, 195, 189, 182, 90, 154, 240, 250, 149, 188, 201, 23]), slot_b_sha3_256_digest: Some([35, 249, 132, 191, 52, 221, 60, 152, 20, 13, 211, 244, 154, 27, 29, 233, 214, 32, 162, 181, 45, 115, 96, 90, 13, 185, 233, 48, 206, 179, 50, 176]) }) }), component: faux-mgs
hubris archive: 9ac688b080845d53
serial number: 271FVPY0
model: 913-0000019
revision: 6
base MAC address: a8:40:25:04:28:c7
power state: A0
rot: Ok(RotStateV2 {
active: A,
persistent_boot_preference: A,
pending_persistent_boot_preference: None,
transient_boot_preference: None,
slot_a_sha3_256_digest: Some("c926ab101f61b27443216c5f068bf21434f11607a7c3bdb65a9af0fa95bcc917"),
slot_b_sha3_256_digest: Some("23f984bf34dd3c98140dd3f49a1b1de9d620a2b52d73605a0db9e930ceb332b0"),
}
[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 read-component-caboose --component sp --slot 0 BORD
Nov 03 04:01:50.136 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 04:01:50.137 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
gimlet-c
[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 read-component-caboose --component sp --slot 0 VERS
Nov 03 04:01:51.635 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 04:01:51.648 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
1.84.0
[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 read-component-caboose --component sp --slot 0 NAME
Nov 03 04:01:56.462 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 04:01:56.479 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
gimlet-c
[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 update sp 0 68738d84c30667e2822f822a00e7d3c7ac34aa15e2f0e0a8030bc0d36ff5925e.gimlet_sp-gimlet-c-1.79.0.tar.gz
Nov 03 04:04:01.603 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 04:04:01.605 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:01.608 INFO generated update ID, id: b25a2f90-a0b2-4f8a-b31f-a57891708db7, component: faux-mgs
Nov 03 04:04:01.675 INFO starting SP update, sp_image_size: 728320, aux_flash_size: 0, aux_flash_chck: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], id: b25a2f90-a0b2-4f8a-b31f-a57891708db7, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:08.162 INFO update preparation complete, update_id: b25a2f90-a0b2-4f8a-b31f-a57891708db7, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:09.501 INFO update in progress, total_size: 728320, bytes_received: 0, component: faux-mgs
Nov 03 04:04:10.557 INFO update in progress, total_size: 728320, bytes_received: 60636, component: faux-mgs
Nov 03 04:04:11.580 INFO update in progress, total_size: 728320, bytes_received: 273840, component: faux-mgs
Nov 03 04:04:12.585 INFO update in progress, total_size: 728320, bytes_received: 609294, component: faux-mgs
Nov 03 04:04:13.271 INFO update complete, id: b25a2f90-a0b2-4f8a-b31f-a57891708db7, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
update complete
[berlin]root@oxz_switch1:~# faux-mgs --interface gimlet17 reset-component sp
Nov 03 04:04:27.166 INFO creating SP handle on interface gimlet17, component: faux-mgs
Nov 03 04:04:27.200 INFO initial discovery complete, addr: [fe80::aa40:25ff:fe04:28cf%53]:11111, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:27.261 INFO SP is prepared to reset component sp, component: faux-mgs
Nov 03 04:04:27.341 INFO using watchdog during reset, watchdog_timeout_ms: 90000, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:29.343 INFO disabling watchdog, interface: gimlet17, socket: control-plane-agent, component: faux-mgs
Nov 03 04:04:29.346 INFO SP reset component sp completereset complete
, component: faux-mgs

Sled 17 reset. It didn't take long for inventory to reflect it:

[berlin]root@oxz_switch1:~# omdb db inventory collections show latest sp 271FVPY0
note: database URL not specified.  Will search DNS.
note: (override with --db-url or OMDB_DB_URL)
note: using DNS from system config (typically /etc/resolv.conf)
note: (if this is not right, use --dns-server to specify an alternate DNS server)
note: using database URL postgresql://root@[fdcc:7057:faf9:102::11]:32221,[fdcc:7057:faf9:101::3]:32221,[fdcc:7057:faf9:102::3]:32221,[fdcc:7057:faf9:103::3]:32221,[fdcc:7057:faf9:101::11]:32221/omicron?sslmode=disable
note: database schema version matches expected (311.0.0)
collection: c25c0755-195f-484d-85de-36feeb603133
collector:  00adb948-783f-4a1b-9d74-321f1f198cdc (likely a Nexus instance)
started:    2026-11-03T04:05:55.629Z
done:       2026-11-03T04:06:21.557Z
errors:     0

Sled 271FVPY0
    part number: 913-0000019
    power:    A0
    revision: 6
    rack ID:  c5e750a8-e260-44d5-99c1-a380d1194cc7
    MGS slot: Sled 17
    found at: 2026-11-03T04:06:02.727Z from http://[fdcc:7057:faf9:101::2]:12225
    host phase 1 active slot: A
    host phase 1 hashes:
        SLOT HASH
        A    fb02e4929c7484960cf9c95fd5b554beefe4469dffedc2dd218bc1ffa31d9174
        B    fb02e4929c7484960cf9c95fd5b554beefe4469dffedc2dd218bc1ffa31d9174
    cabooses:
        SLOT       BOARD       NAME        VERSION GIT_COMMIT                               SIGN
        SpSlot0    gimlet-c    gimlet-c    1.79.0  33aea6d10bc94dc2d928f3f6b8c193c2181d144c n/a
        SpSlot1    gimlet-c    gimlet-c    1.84.0  ca30150c27d2d414347f5dd6fe46cd4838c0e163 n/a
        RotSlotA   oxide-rot-1 oxide-rot-1 1.0.38  e40997968406278927d41c03d24b4ae6472c375a 11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf
        RotSlotB   oxide-rot-1 oxide-rot-1 1.0.38  e40997968406278927d41c03d24b4ae6472c375a 11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf
        Stage0     oxide-rot-1 oxide-rot-1 1.4.1   bdf56dd950b934360df596ed5b2d8b8813c92168 11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf
        Stage0Next oxide-rot-1 oxide-rot-1 1.4.1   bdf56dd950b934360df596ed5b2d8b8813c92168 11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf
    RoT pages:
        SLOT         DATA_BASE64
        Cmpa         oAAAiAAAAAAAAAAAAAAAAP8CAP3/AgD9...
        CfpaActive   AAAAABUAAAAAAAAAAAAAAAAAAAAAAAAA...
        CfpaInactive AAAAABUAAAAAAAAAAAAAAAAAAAAAAAAA...
        CfpaScratch  AAAAABUAAAAAAAAAAAAAAAAAAAAAAAAA...
    RoT: active slot: slot A
    RoT: persistent boot preference: slot A
    RoT: pending persistent boot preference: -
    RoT: transient boot preference: -
    RoT: slot A SHA3-256: c926ab101f61b27443216c5f068bf21434f11607a7c3bdb65a9af0fa95bcc917
    RoT: slot B SHA3-256: 23f984bf34dd3c98140dd3f49a1b1de9d620a2b52d73605a0db9e930ceb332b0

Now, let's try adding it. With no overrides, we get the expected version mismatch error:

dap@ivanova berlin-testing $ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds.json 
error; status code: 400 Bad Request
{
  "error_code": "InvalidRequest",
  "message": "sled cannot be added: the software on sled 913-0000019:271FVPY0 does not match target release 24.0.0-0.ci+gita166dd1e7e9: SP is running 1.79.0 (expected 1.84.0)",
  "request_id": "db2aaebd-8e40-480c-ae8d-4db7d2ab9f70"
}
error

With an override of the system state check, we get the same:

dap@ivanova berlin-testing $ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds-override-system.json
error; status code: 400 Bad Request
{
  "error_code": "InvalidRequest",
  "message": "sled cannot be added: the software on sled 913-0000019:271FVPY0 does not match target release 24.0.0-0.ci+gita166dd1e7e9: SP is running 1.79.0 (expected 1.84.0)",
  "request_id": "506121ad-afb0-4d3b-95a3-219e64a43be8"
}
error

but if we override just the version, it works:

dap@ivanova berlin-testing $ oxide api /v1/system/hardware/racks/c5e750a8-e260-44d5-99c1-a380d1194cc7/membership/add -X POST --input new-sleds-override-version.json 
{
  "members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "rack_id": "c5e750a8-e260-44d5-99c1-a380d1194cc7",
  "state": "in_progress",
  "time_aborted": null,
  "time_committed": null,
  "time_created": "2026-11-03T04:10:12.834362Z",
  "unacknowledged_members": [
    {
      "part_number": "913-0000019",
      "serial_number": "271FVPY0"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220011"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM42220082"
    },
    {
      "part_number": "913-0000019",
      "serial_number": "BRM44220007"
    }
  ],
  "version": 11
}

and Reconfigurator went ahead and updated the SP and deployed the usual stuff:

   22 2026-11-03T03:06:16.480Z 9257ea12-064f-4da5-8034-9979f5cac1b7  enabled: sled f9627989-91f1-4f64-99fa-d0b55ea81f44 expunged (expunged 9 disks, 55 datasets, 10 zones)
   23 2026-11-03T04:10:45.549Z 9bb2bacc-e89b-4ebc-811a-29d3a8657c1b  enabled: 
   24 2026-11-03T04:10:54.474Z 8c27bda2-7a5e-4f75-a7d6-86e5424473d6  enabled: sled 1c5aff87-2a5f-4de5-9b45-d45b9d0c3d41: added 9 disks, updated 0, removed 0 disks, sled 1c5aff87-2a5f-4de5-9b45-d45b9d0c3d41: added zone: internal_ntp
   25 2026-11-03T04:14:04.126Z 340a8757-4943-4216-8007-5d3a6c5893b5  enabled: update Sled 17 (271FVPY0) SP to 1.84.0, sled 1c5aff87-2a5f-4de5-9b45-d45b9d0c3d41: noop updated host phase 2 to Artifact: both slot A and slot B, sled 1c5aff87-2a5f-4de5-9b45-d45b9d0c3d41: added zone: crucible
   26 2026-11-03T04:17:14.990Z 77f68360-052f-464c-b48e-4616e266a130  enabled: 
   27 2026-11-03T04:19:03.030Z 8c626615-a35b-4d4b-a60f-2a47261746f2  enabled: updating Crucible zone 534fcace-95b5-488a-b52b-13b9bd4b9d55 in-place
   28 2026-11-03T04:19:18.126Z 925ee6be-5851-463d-89fb-4d11a501e1a9  enabled: updating Crucible zone 7a39788d-7441-4dd4-8570-3239b0491949 in-place
   29 2026-11-03T04:19:33.375Z 60f5e50d-bc62-4b09-9de5-3584f39e7e2d  enabled: updating Crucible zone 8cb8e80c-9aae-48fc-9a63-cce99f262edf in-place
   30 2026-11-03T04:19:48.441Z cf5008e9-f2aa-4bd8-9682-3f3df284987a  enabled: updating Crucible zone 962b48bf-e4e5-49c3-9f75-3090f102b83c in-place
   31 2026-11-03T04:20:00.267Z a07b6da9-9632-425b-aeb4-496b8007b68c  enabled: updating Crucible zone a282e6fd-3a2d-402a-9473-a1db096a1a53 in-place
   32 2026-11-03T04:20:11.899Z 325c94aa-7864-4ff2-ad59-832a77beb9f9  enabled: updating Crucible zone bcf63fe7-0019-4fc8-b449-ae04000660cb in-place
   33 2026-11-03T04:20:30.226Z d4b1d027-56c3-4353-852e-3018fbef166e  enabled: updating Crucible zone dbd5c253-e265-4a65-8d10-96771207d558 in-place
   34 2026-11-03T04:20:44.178Z 461e7f9f-fbbf-443a-a87c-7558d7d8c382  enabled: updating Crucible zone dfbd4d50-e2b3-4c57-9cad-6154eb98c110 in-place
   35 2026-11-03T04:20:57.886Z f0f0afb0-5d0c-4302-83c1-f731f01cab52  enabled: updating Crucible zone ff014930-baa9-4e1c-8e58-716509b25a29 in-place

This covers the whole test plan.

I'm a little surprised that it updated Crucible zones at the end.

@davepacheco
davepacheco added this pull request to stack #11669 November 3, 2026 04:55
@davepacheco
davepacheco merged commit 40b22e5 into main Nov 3, 2026
19 checks passed
@davepacheco
davepacheco deleted the dap/add-sled-check-3 branch November 3, 2026 04:59
davepacheco added a commit that referenced this pull request Nov 3, 2026
<del>Depends on #11636.</del> This PR adds checks to the "add sled" API
that verify the RFD 747 conditions for self-service sled add: that
there's no self-service update in progress and that the sled's software
is running the right versions (as best we can tell, which ought to be
good enough in practice). This version of the PR removes the old,
non-TQ-aware `sled_add` path that existed for compatibility with older
clients. That was removed in R19 but was still being served for older
clients. We should get rid of this altogether, but that's a lot more
work. (See #11641.)

I've got a follow-up in #11638 that makes these new checks overridable.
This is important because this is the _only_ way to add sleds today and
I think support should be able to say "I know this version is compatible
enough" or something like that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants