Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions dev-tools/reconfigurator-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2134,8 +2134,11 @@ fn cmd_blueprint_blippy(
let resolved_id =
state.system().resolve_blueprint_id(args.blueprint_id.into())?;
let blueprint = state.system().get_blueprint(&resolved_id)?;
let report =
Blippy::new(&blueprint).into_report(BlippyReportSortKey::Severity);
let planning_input = sim
.planning_input(blueprint)
.context("failed to construct planning input")?;
let report = Blippy::new(&blueprint, &planning_input)
.into_report(BlippyReportSortKey::Severity);
Ok(Some(format!("{}", report.display())))
}

Expand Down
83 changes: 81 additions & 2 deletions nexus/reconfigurator/blippy/src/blippy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use nexus_types::deployment::Blueprint;
use nexus_types::deployment::BlueprintArtifactVersion;
use nexus_types::deployment::BlueprintDatasetConfig;
use nexus_types::deployment::BlueprintZoneConfig;
use nexus_types::deployment::OmicronZoneExternalIp;
use nexus_types::deployment::OmicronZoneNicEntry;
use nexus_types::deployment::PlanningInput;
use nexus_types::inventory::ZpoolName;
use omicron_common::address::DnsSubnet;
use omicron_common::address::Ipv6Subnet;
Expand Down Expand Up @@ -55,48 +58,55 @@ impl fmt::Display for Severity {
pub enum Kind {
Blueprint(BlueprintKind),
Sled { sled_id: SledUuid, kind: Box<SledKind> },
PlanningInput(PlanningInputKind),
}

impl Kind {
pub fn display_component(&self) -> impl fmt::Display + '_ {
enum Component<'a> {
Blueprint,
Sled(&'a SledUuid),
PlanningInput,
}

impl fmt::Display for Component<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Component::Blueprint => write!(f, "blueprint"),
Component::Sled(id) => write!(f, "sled {id}"),
Component::PlanningInput => write!(f, "planning input"),
}
}
}

match self {
Kind::Blueprint { .. } => Component::Blueprint,
Kind::Blueprint(_) => Component::Blueprint,
Kind::Sled { sled_id, .. } => Component::Sled(sled_id),
Kind::PlanningInput(_) => Component::PlanningInput,
}
}

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

impl fmt::Display for Subkind<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Subkind::Blueprint(kind) => write!(f, "{kind}"),
Subkind::Sled(kind) => write!(f, "{kind}"),
Subkind::PlanningInput(kind) => write!(f, "{kind}"),
}
}
}

match self {
Kind::Blueprint(kind) => Subkind::Blueprint(kind),
Kind::Sled { kind, .. } => Subkind::Sled(kind),
Kind::PlanningInput(kind) => Subkind::PlanningInput(kind),
}
}
}
Expand Down Expand Up @@ -488,6 +498,54 @@ impl fmt::Display for SledKind {
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PlanningInputKind {
IpNotInBlueprint(OmicronZoneExternalIp),
NicMacNotInBluperint(OmicronZoneNicEntry),
NicIpNotInBlueprint(OmicronZoneNicEntry),
NicWithUnknownOpteSubnet(OmicronZoneNicEntry),
}

impl fmt::Display for PlanningInputKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PlanningInputKind::IpNotInBlueprint(ip) => {
write!(
f,
"planning input contains an external IP \
not described by the blueprint: {} ({})",
ip.ip(),
ip.id()
)
}
PlanningInputKind::NicMacNotInBluperint(nic) => {
write!(
f,
"planning input contains a NIC with a MAC address \
not described by the blueprint: {} (NIC {} in zone {})",
nic.nic.mac, nic.nic.id, nic.zone_id,
)
}
PlanningInputKind::NicIpNotInBlueprint(nic) => {
write!(
f,
"planning input contains a NIC with an IP address \
not described by the blueprint: {} (NIC {} in zone {})",
nic.nic.ip, nic.nic.id, nic.zone_id,
)
}
PlanningInputKind::NicWithUnknownOpteSubnet(nic) => {
write!(
f,
"planning input contains a NIC with an IP not in a known
OPTE subnet: {} (NIC {} in zone {})",
nic.nic.ip, nic.nic.id, nic.zone_id,
)
}
}
}
}

impl Note {
pub fn display(&self, sort_key: BlippyReportSortKey) -> NoteDisplay<'_> {
NoteDisplay { note: self, sort_key }
Expand Down Expand Up @@ -532,7 +590,20 @@ pub struct Blippy<'a> {
}

impl<'a> Blippy<'a> {
pub fn new(blueprint: &'a Blueprint) -> Self {
/// Check `blueprint` for internal inconsistencies and check for
/// inconsistencies between `blueprint` and `planning_input`.
pub fn new(
blueprint: &'a Blueprint,
planning_input: &PlanningInput,
) -> Self {
let mut slf = Self { blueprint, notes: Vec::new() };
checks::perform_all_blueprint_only_checks(&mut slf);
checks::perform_planning_input_checks(&mut slf, planning_input);
slf
}

/// Check `blueprint` for internal inconsistencies.
pub fn new_blueprint_only(blueprint: &'a Blueprint) -> Self {
let mut slf = Self { blueprint, notes: Vec::new() };
checks::perform_all_blueprint_only_checks(&mut slf);
slf
Expand Down Expand Up @@ -562,6 +633,14 @@ impl<'a> Blippy<'a> {
});
}

pub(crate) fn push_planning_input_note(
&mut self,
severity: Severity,
kind: PlanningInputKind,
) {
self.notes.push(Note { severity, kind: Kind::PlanningInput(kind) });
}

pub fn into_report(
self,
sort_key: BlippyReportSortKey,
Expand Down
Loading
Loading