Skip to content

Commit b07cb79

Browse files
authored
Add Blippy::check_against_planning_input() (#9356)
This PR adds a new method to (optionally) check a blueprint against a given planning input. The only check included in this PR is a line-for-line port of the builder's `ensure_input_networking_records_appear_in_parent_blueprint()` function (which I'm removing from the builder in #9347). I updated most of the `Blippy` call sites to use this method; I skipped the one in an RSS test because it has no planning input available - RSS just generates a raw blueprint to hand off to Nexus.
1 parent 509e97b commit b07cb79

File tree

7 files changed

+331
-72
lines changed

7 files changed

+331
-72
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,8 +2134,11 @@ fn cmd_blueprint_blippy(
21342134
let resolved_id =
21352135
state.system().resolve_blueprint_id(args.blueprint_id.into())?;
21362136
let blueprint = state.system().get_blueprint(&resolved_id)?;
2137-
let report =
2138-
Blippy::new(&blueprint).into_report(BlippyReportSortKey::Severity);
2137+
let planning_input = sim
2138+
.planning_input(blueprint)
2139+
.context("failed to construct planning input")?;
2140+
let report = Blippy::new(&blueprint, &planning_input)
2141+
.into_report(BlippyReportSortKey::Severity);
21392142
Ok(Some(format!("{}", report.display())))
21402143
}
21412144

nexus/reconfigurator/blippy/src/blippy.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use nexus_types::deployment::Blueprint;
1010
use nexus_types::deployment::BlueprintArtifactVersion;
1111
use nexus_types::deployment::BlueprintDatasetConfig;
1212
use nexus_types::deployment::BlueprintZoneConfig;
13+
use nexus_types::deployment::OmicronZoneExternalIp;
14+
use nexus_types::deployment::OmicronZoneNicEntry;
15+
use nexus_types::deployment::PlanningInput;
1316
use nexus_types::inventory::ZpoolName;
1417
use omicron_common::address::DnsSubnet;
1518
use omicron_common::address::Ipv6Subnet;
@@ -55,48 +58,55 @@ impl fmt::Display for Severity {
5558
pub enum Kind {
5659
Blueprint(BlueprintKind),
5760
Sled { sled_id: SledUuid, kind: Box<SledKind> },
61+
PlanningInput(PlanningInputKind),
5862
}
5963

6064
impl Kind {
6165
pub fn display_component(&self) -> impl fmt::Display + '_ {
6266
enum Component<'a> {
6367
Blueprint,
6468
Sled(&'a SledUuid),
69+
PlanningInput,
6570
}
6671

6772
impl fmt::Display for Component<'_> {
6873
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6974
match self {
7075
Component::Blueprint => write!(f, "blueprint"),
7176
Component::Sled(id) => write!(f, "sled {id}"),
77+
Component::PlanningInput => write!(f, "planning input"),
7278
}
7379
}
7480
}
7581

7682
match self {
77-
Kind::Blueprint { .. } => Component::Blueprint,
83+
Kind::Blueprint(_) => Component::Blueprint,
7884
Kind::Sled { sled_id, .. } => Component::Sled(sled_id),
85+
Kind::PlanningInput(_) => Component::PlanningInput,
7986
}
8087
}
8188

8289
pub fn display_subkind(&self) -> impl fmt::Display + '_ {
8390
enum Subkind<'a> {
8491
Blueprint(&'a BlueprintKind),
8592
Sled(&'a SledKind),
93+
PlanningInput(&'a PlanningInputKind),
8694
}
8795

8896
impl fmt::Display for Subkind<'_> {
8997
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9098
match self {
9199
Subkind::Blueprint(kind) => write!(f, "{kind}"),
92100
Subkind::Sled(kind) => write!(f, "{kind}"),
101+
Subkind::PlanningInput(kind) => write!(f, "{kind}"),
93102
}
94103
}
95104
}
96105

97106
match self {
98107
Kind::Blueprint(kind) => Subkind::Blueprint(kind),
99108
Kind::Sled { kind, .. } => Subkind::Sled(kind),
109+
Kind::PlanningInput(kind) => Subkind::PlanningInput(kind),
100110
}
101111
}
102112
}
@@ -488,6 +498,54 @@ impl fmt::Display for SledKind {
488498
}
489499
}
490500

501+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
502+
pub enum PlanningInputKind {
503+
IpNotInBlueprint(OmicronZoneExternalIp),
504+
NicMacNotInBluperint(OmicronZoneNicEntry),
505+
NicIpNotInBlueprint(OmicronZoneNicEntry),
506+
NicWithUnknownOpteSubnet(OmicronZoneNicEntry),
507+
}
508+
509+
impl fmt::Display for PlanningInputKind {
510+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511+
match self {
512+
PlanningInputKind::IpNotInBlueprint(ip) => {
513+
write!(
514+
f,
515+
"planning input contains an external IP \
516+
not described by the blueprint: {} ({})",
517+
ip.ip(),
518+
ip.id()
519+
)
520+
}
521+
PlanningInputKind::NicMacNotInBluperint(nic) => {
522+
write!(
523+
f,
524+
"planning input contains a NIC with a MAC address \
525+
not described by the blueprint: {} (NIC {} in zone {})",
526+
nic.nic.mac, nic.nic.id, nic.zone_id,
527+
)
528+
}
529+
PlanningInputKind::NicIpNotInBlueprint(nic) => {
530+
write!(
531+
f,
532+
"planning input contains a NIC with an IP address \
533+
not described by the blueprint: {} (NIC {} in zone {})",
534+
nic.nic.ip, nic.nic.id, nic.zone_id,
535+
)
536+
}
537+
PlanningInputKind::NicWithUnknownOpteSubnet(nic) => {
538+
write!(
539+
f,
540+
"planning input contains a NIC with an IP not in a known
541+
OPTE subnet: {} (NIC {} in zone {})",
542+
nic.nic.ip, nic.nic.id, nic.zone_id,
543+
)
544+
}
545+
}
546+
}
547+
}
548+
491549
impl Note {
492550
pub fn display(&self, sort_key: BlippyReportSortKey) -> NoteDisplay<'_> {
493551
NoteDisplay { note: self, sort_key }
@@ -532,7 +590,20 @@ pub struct Blippy<'a> {
532590
}
533591

534592
impl<'a> Blippy<'a> {
535-
pub fn new(blueprint: &'a Blueprint) -> Self {
593+
/// Check `blueprint` for internal inconsistencies and check for
594+
/// inconsistencies between `blueprint` and `planning_input`.
595+
pub fn new(
596+
blueprint: &'a Blueprint,
597+
planning_input: &PlanningInput,
598+
) -> Self {
599+
let mut slf = Self { blueprint, notes: Vec::new() };
600+
checks::perform_all_blueprint_only_checks(&mut slf);
601+
checks::perform_planning_input_checks(&mut slf, planning_input);
602+
slf
603+
}
604+
605+
/// Check `blueprint` for internal inconsistencies.
606+
pub fn new_blueprint_only(blueprint: &'a Blueprint) -> Self {
536607
let mut slf = Self { blueprint, notes: Vec::new() };
537608
checks::perform_all_blueprint_only_checks(&mut slf);
538609
slf
@@ -562,6 +633,14 @@ impl<'a> Blippy<'a> {
562633
});
563634
}
564635

636+
pub(crate) fn push_planning_input_note(
637+
&mut self,
638+
severity: Severity,
639+
kind: PlanningInputKind,
640+
) {
641+
self.notes.push(Note { severity, kind: Kind::PlanningInput(kind) });
642+
}
643+
565644
pub fn into_report(
566645
self,
567646
sort_key: BlippyReportSortKey,

0 commit comments

Comments
 (0)