Skip to content

Commit 034044c

Browse files
authored
[28/n] [blippy/sled-agent] add remove_mupdate_override checks (#8561)
Add remove_mupdate_override invariants to blippy and Sled Agent. Blippy doesn't check for `PendingMgsUpdates` yet -- that requires additional state (the map from sled ID to baseboard ID) that I don't want to spend effort passing in just yet.
1 parent c28e6e4 commit 034044c

File tree

5 files changed

+261
-14
lines changed

5 files changed

+261
-14
lines changed

Cargo.lock

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

nexus/reconfigurator/blippy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ nexus-sled-agent-shared.workspace = true
1111
nexus-types.workspace = true
1212
omicron-common.workspace = true
1313
omicron-uuid-kinds.workspace = true
14-
1514
omicron-workspace-hack.workspace = true
15+
tufaceous-artifact.workspace = true
1616

1717
[dev-dependencies]
1818
nexus-reconfigurator-planning.workspace = true

nexus/reconfigurator/blippy/src/blippy.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ use core::fmt;
99
use nexus_types::deployment::Blueprint;
1010
use nexus_types::deployment::BlueprintDatasetConfig;
1111
use nexus_types::deployment::BlueprintZoneConfig;
12+
use nexus_types::deployment::BlueprintZoneImageVersion;
1213
use nexus_types::inventory::ZpoolName;
1314
use omicron_common::address::DnsSubnet;
1415
use omicron_common::address::Ipv6Subnet;
1516
use omicron_common::address::SLED_PREFIX;
1617
use omicron_common::api::external::MacAddr;
1718
use omicron_common::disk::DatasetKind;
19+
use omicron_uuid_kinds::MupdateOverrideUuid;
1820
use omicron_uuid_kinds::SledUuid;
1921
use omicron_uuid_kinds::ZpoolUuid;
2022
use std::collections::BTreeSet;
2123
use std::net::IpAddr;
2224
use std::net::SocketAddrV6;
25+
use tufaceous_artifact::ArtifactHash;
2326

2427
#[derive(Debug, Clone, PartialEq, Eq)]
2528
pub struct Note {
@@ -177,6 +180,12 @@ pub enum SledKind {
177180
dataset: BlueprintDatasetConfig,
178181
address: SocketAddrV6,
179182
},
183+
MupdateOverrideWithArtifactZone {
184+
mupdate_override_id: MupdateOverrideUuid,
185+
zone: BlueprintZoneConfig,
186+
version: BlueprintZoneImageVersion,
187+
hash: ArtifactHash,
188+
},
180189
}
181190

182191
impl fmt::Display for SledKind {
@@ -372,6 +381,20 @@ impl fmt::Display for SledKind {
372381
dataset.kind, dataset.id, address,
373382
)
374383
}
384+
SledKind::MupdateOverrideWithArtifactZone {
385+
mupdate_override_id,
386+
zone,
387+
version,
388+
hash,
389+
} => {
390+
write!(
391+
f,
392+
"sled has remove_mupdate_override set ({mupdate_override_id}), \
393+
but zone {} image source is set to Artifact (version {version}, \
394+
hash {hash})",
395+
zone.id,
396+
)
397+
}
375398
}
376399
}
377400
}

nexus/reconfigurator/blippy/src/checks.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ use nexus_types::deployment::BlueprintPhysicalDiskDisposition;
1212
use nexus_types::deployment::BlueprintSledConfig;
1313
use nexus_types::deployment::BlueprintZoneConfig;
1414
use nexus_types::deployment::BlueprintZoneDisposition;
15+
use nexus_types::deployment::BlueprintZoneImageSource;
1516
use nexus_types::deployment::BlueprintZoneType;
1617
use nexus_types::deployment::OmicronZoneExternalIp;
18+
use nexus_types::deployment::SledFilter;
1719
use nexus_types::deployment::blueprint_zone_type;
1820
use omicron_common::address::DnsSubnet;
1921
use omicron_common::address::Ipv6Subnet;
@@ -31,6 +33,7 @@ pub(crate) fn perform_all_blueprint_only_checks(blippy: &mut Blippy<'_>) {
3133
check_external_networking(blippy);
3234
check_dataset_zpool_uniqueness(blippy);
3335
check_datasets(blippy);
36+
check_mupdate_override(blippy);
3437
}
3538

3639
fn check_underlay_ips(blippy: &mut Blippy<'_>) {
@@ -537,6 +540,55 @@ fn check_datasets(blippy: &mut Blippy<'_>) {
537540
}
538541
}
539542

543+
fn check_mupdate_override(blippy: &mut Blippy<'_>) {
544+
// Perform checks for invariants that should be upheld if
545+
// remove_mupdate_override is set for a sled.
546+
for (&sled_id, sled) in &blippy.blueprint().sleds {
547+
if !sled.state.matches(SledFilter::InService) {
548+
continue;
549+
}
550+
551+
if let Some(mupdate_override_id) = sled.remove_mupdate_override {
552+
// All in-service zones should be set to InstallDataset.
553+
for zone in &sled.zones {
554+
if zone.disposition.is_in_service() {
555+
match &zone.image_source {
556+
BlueprintZoneImageSource::InstallDataset => {
557+
// This is valid.
558+
}
559+
BlueprintZoneImageSource::Artifact {
560+
version,
561+
hash,
562+
} => {
563+
// This is invalid -- if remove_mupdate_override is
564+
// set, all zones must be InstallDataset.
565+
blippy.push_sled_note(
566+
sled_id,
567+
Severity::Fatal,
568+
SledKind::MupdateOverrideWithArtifactZone {
569+
mupdate_override_id,
570+
zone: zone.clone(),
571+
version: version.clone(),
572+
hash: *hash,
573+
},
574+
);
575+
}
576+
}
577+
}
578+
}
579+
580+
// TODO: The host phase 2 contents should be set to CurrentContents
581+
// (waiting for
582+
// https://github.com/oxidecomputer/omicron/issues/8542).
583+
584+
// TODO: PendingMgsUpdates for this sled should be empty. Mapping
585+
// sled IDs to their MGS identifiers (baseboard ID) requires a map
586+
// that's not currently part of the blueprint. We may want to either
587+
// include that map in the blueprint, or pass it in via blippy.
588+
}
589+
}
590+
}
591+
540592
#[cfg(test)]
541593
mod tests {
542594
use super::*;
@@ -545,10 +597,14 @@ mod tests {
545597
use crate::blippy::Note;
546598
use nexus_reconfigurator_planning::example::ExampleSystemBuilder;
547599
use nexus_reconfigurator_planning::example::example;
600+
use nexus_types::deployment::BlueprintZoneImageVersion;
548601
use nexus_types::deployment::BlueprintZoneType;
549602
use nexus_types::deployment::blueprint_zone_type;
550603
use omicron_test_utils::dev::test_setup_log;
604+
use omicron_uuid_kinds::MupdateOverrideUuid;
551605
use std::mem;
606+
use tufaceous_artifact::ArtifactHash;
607+
use tufaceous_artifact::ArtifactVersion;
552608

553609
// The tests below all take the example blueprint, mutate in some invalid
554610
// way, and confirm that blippy reports the invalidity. This test confirms
@@ -1561,4 +1617,61 @@ mod tests {
15611617

15621618
logctx.cleanup_successful();
15631619
}
1620+
1621+
#[test]
1622+
fn test_mupdate_override_with_artifact_image_source() {
1623+
static TEST_NAME: &str =
1624+
"test_remove_mupdate_override_with_artifact_image_source";
1625+
let logctx = test_setup_log(TEST_NAME);
1626+
let (_, _, mut blueprint) = example(&logctx.log, TEST_NAME);
1627+
1628+
// Find a sled with zones and set remove_mupdate_override on it.
1629+
let (&sled_id, sled) = blueprint
1630+
.sleds
1631+
.iter_mut()
1632+
.find(|(_, config)| !config.zones.is_empty())
1633+
.expect("at least one sled with zones");
1634+
1635+
// Set the remove_mupdate_override field on the sled.
1636+
let mupdate_override_id = MupdateOverrideUuid::max();
1637+
sled.remove_mupdate_override = Some(mupdate_override_id);
1638+
1639+
// Find a zone and set it to use an artifact image source.
1640+
let kind = {
1641+
let mut zone = sled
1642+
.zones
1643+
.iter_mut()
1644+
.find(|z| z.disposition.is_in_service())
1645+
.expect("at least one in-service zone");
1646+
1647+
let version = BlueprintZoneImageVersion::Available {
1648+
version: ArtifactVersion::new_const("1.0.0"),
1649+
};
1650+
let hash = ArtifactHash([1u8; 32]);
1651+
zone.image_source = BlueprintZoneImageSource::Artifact {
1652+
version: version.clone(),
1653+
hash,
1654+
};
1655+
1656+
SledKind::MupdateOverrideWithArtifactZone {
1657+
mupdate_override_id,
1658+
zone: zone.clone(),
1659+
version,
1660+
hash,
1661+
}
1662+
};
1663+
1664+
let expected_note = Note {
1665+
severity: Severity::Fatal,
1666+
kind: Kind::Sled { sled_id, kind },
1667+
};
1668+
1669+
let report =
1670+
Blippy::new(&blueprint).into_report(BlippyReportSortKey::Kind);
1671+
eprintln!("{}", report.display());
1672+
assert_eq!(report.notes().len(), 1, "exactly one note expected");
1673+
assert_eq!(report.notes()[0], expected_note);
1674+
1675+
logctx.cleanup_successful();
1676+
}
15641677
}

0 commit comments

Comments
 (0)