Skip to content
Draft
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
54 changes: 52 additions & 2 deletions dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ use nexus_types::internal_api::background::TufArtifactReplicationStatus;
use nexus_types::internal_api::background::TufRepoPrunerStatus;
use nexus_types::internal_api::background::fm_rendezvous;
use nexus_types::support_bundle::BundleDataCategory;
use nexus_types::support_bundle::BundleZoneType;
use omicron_uuid_kinds::BlueprintUuid;
use omicron_uuid_kinds::CollectionUuid;
use omicron_uuid_kinds::DemoSagaUuid;
Expand Down Expand Up @@ -673,6 +674,12 @@ struct SupportBundleCreateArgs {
#[clap(long, value_enum)]
include: Vec<BundleDataCategory>,

/// Only collect logs from zones of these types. May be supplied
/// multiple times. Defaults to all zones. Requires host-info
/// collection.
#[clap(long, value_enum)]
zone_type: Vec<BundleZoneType>,

#[command(flatten)]
window: TimeWindowArgs,
}
Expand Down Expand Up @@ -5659,6 +5666,29 @@ async fn lookup_sled_by_id(
Ok(sled)
}

/// Maps the zone-type flag values onto the generated client enum.
fn zone_type_to_api(
zone_type: BundleZoneType,
) -> nexus_lockstep_client::types::SupportBundleZoneType {
use nexus_lockstep_client::types::SupportBundleZoneType as Api;
match zone_type {
BundleZoneType::Global => Api::Global,
BundleZoneType::Switch => Api::Switch,
BundleZoneType::Propolis => Api::Propolis,
BundleZoneType::Ntp => Api::Ntp,
BundleZoneType::Clickhouse => Api::Clickhouse,
BundleZoneType::ClickhouseKeeper => Api::ClickhouseKeeper,
BundleZoneType::ClickhouseServer => Api::ClickhouseServer,
BundleZoneType::CockroachDb => Api::Cockroachdb,
BundleZoneType::Crucible => Api::Crucible,
BundleZoneType::CruciblePantry => Api::CruciblePantry,
BundleZoneType::ExternalDns => Api::ExternalDns,
BundleZoneType::InternalDns => Api::InternalDns,
BundleZoneType::Nexus => Api::Nexus,
BundleZoneType::Oximeter => Api::Oximeter,
}
}

/// Runs `omdb nexus support-bundles create`
async fn cmd_nexus_support_bundles_create(
client: &nexus_lockstep_client::Client,
Expand All @@ -5670,7 +5700,7 @@ async fn cmd_nexus_support_bundles_create(
// No --include collects everything, matching `support-bundle collect`.
// The API's other reading of an empty selection, "collect nothing",
// cannot be specified by clap, which rejects --include without a value.
let data = if args.include.is_empty() {
let data = if args.include.is_empty() && args.zone_type.is_empty() {
types::SupportBundleData::All
} else {
let mut reconfigurator = false;
Expand All @@ -5679,14 +5709,22 @@ async fn cmd_nexus_support_bundles_create(
let mut host_info = None;
let mut ereports = None;

for category in &args.include {
// A zone-type restriction needs the explicit form to be expressed,
// so with no --include it applies to every category.
let categories: &[BundleDataCategory] = if args.include.is_empty() {
clap::ValueEnum::value_variants()
} else {
args.include.as_slice()
};
for category in categories {
match category {
BundleDataCategory::Reconfigurator => reconfigurator = true,
BundleDataCategory::SledCubbyInfo => sled_cubby_info = true,
BundleDataCategory::SpDumps => sp_dumps = true,
BundleDataCategory::HostInfo => {
host_info = Some(types::SupportBundleHostInfo {
sleds: types::SupportBundleSledSelection::All,
zones: types::SupportBundleZoneSelection::All,
})
}
BundleDataCategory::Ereports => {
Expand All @@ -5698,6 +5736,18 @@ async fn cmd_nexus_support_bundles_create(
}
}

if !args.zone_type.is_empty() {
let Some(host_info) = host_info.as_mut() else {
bail!(
"--zone-type only affects zone logs, which are part of \
host-info collection; add --include host-info"
);
};
host_info.zones = types::SupportBundleZoneSelection::Specific(
args.zone_type.iter().copied().map(zone_type_to_api).collect(),
);
}

types::SupportBundleData::Explicit {
reconfigurator,
sled_cubby_info,
Expand Down
16 changes: 16 additions & 0 deletions dev-tools/omdb/src/bin/omdb/support_bundle_collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use nexus_types::fm::ereport::EreportFilters;
use nexus_types::support_bundle::BundleDataCategory;
use nexus_types::support_bundle::BundleDataSelection;
use nexus_types::support_bundle::BundleTimeRange;
use nexus_types::support_bundle::BundleZoneType;
use omicron_uuid_kinds::SupportBundleUuid;
use std::io::Write;
use std::sync::Arc;
Expand Down Expand Up @@ -81,6 +82,12 @@ struct CollectArgs {
#[clap(long, value_enum)]
include: Vec<BundleDataCategory>,

/// Only collect logs from zones of these types. May be supplied
/// multiple times. Defaults to all zones. Requires host-info
/// collection.
#[clap(long, value_enum)]
zone_type: Vec<BundleZoneType>,

#[command(flatten)]
window: TimeWindowArgs,
}
Expand All @@ -106,6 +113,15 @@ impl CollectArgs {
};
}

if !self.zone_type.is_empty() {
anyhow::ensure!(
categories.contains(&BundleDataCategory::HostInfo),
"--zone-type only affects zone logs, which are part of \
host-info collection; add --include host-info",
);
sel = sel.with_zone_types(self.zone_type.iter().copied());
}

let window = self.window.bounds()?;
Ok(sel.with_time_range(BundleTimeRange::new(window.start, window.end)?))
}
Expand Down
36 changes: 29 additions & 7 deletions nexus/db-model/src/fm/support_bundle_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use nexus_db_schema::schema::{
};
use nexus_types::fm;
use nexus_types::fm::ereport::EreportFilters;
use nexus_types::support_bundle::{BundleData, BundleTimeRange, SledSelection};
use nexus_types::support_bundle::{
BundleData, BundleTimeRange, SledSelection, ZoneSelection,
};
use omicron_uuid_kinds::{
CaseKind, GenericUuid, SitrepKind, SledUuid, SupportBundleKind,
};
Expand Down Expand Up @@ -94,13 +96,16 @@ pub struct HostInfo {
pub request_id: DbTypedUuid<SupportBundleKind>,
pub all_sleds: bool,
pub sled_ids: Vec<uuid::Uuid>,
pub all_zone_types: bool,
pub zone_types: Vec<String>,
}

impl HostInfo {
pub fn from_sitrep(
sitrep_id: impl Into<DbTypedUuid<SitrepKind>>,
request_id: impl Into<DbTypedUuid<SupportBundleKind>>,
sleds: SledSelection,
zones: ZoneSelection,
) -> Self {
let (all_sleds, sled_ids) = match sleds {
SledSelection::All => (true, Vec::new()),
Expand All @@ -109,26 +114,43 @@ impl HostInfo {
ids.into_iter().map(|id| id.into_untyped_uuid()).collect(),
),
};
let (all_zone_types, zone_types) =
crate::support_bundle::zone_selection_to_columns(zones);
HostInfo {
sitrep_id: sitrep_id.into(),
request_id: request_id.into(),
all_sleds,
sled_ids,
all_zone_types,
zone_types,
}
}
}

impl From<HostInfo> for BundleData {
fn from(row: HostInfo) -> Self {
let HostInfo { sitrep_id: _, request_id: _, all_sleds, sled_ids } = row;
let selection = if all_sleds {
impl TryFrom<HostInfo> for BundleData {
type Error = omicron_common::api::external::Error;

fn try_from(row: HostInfo) -> Result<Self, Self::Error> {
let HostInfo {
sitrep_id: _,
request_id: _,
all_sleds,
sled_ids,
all_zone_types,
zone_types,
} = row;
let sleds = if all_sleds {
SledSelection::All
} else {
SledSelection::Specific(
sled_ids.into_iter().map(SledUuid::from_untyped_uuid).collect(),
)
};
BundleData::HostInfo(selection)
let zones = crate::support_bundle::zone_selection_from_columns(
all_zone_types,
zone_types,
)?;
Ok(BundleData::HostInfo { sleds, zones })
}
}

Expand Down Expand Up @@ -239,7 +261,7 @@ impl TryFrom<BundleDataSelection>
selection.insert(BundleData::SpDumps);
}
if let Some(host_info) = row.host_info {
selection.insert(host_info.into());
selection.insert(host_info.try_into()?);
}
if let Some(ereports) = row.ereports {
selection.insert(ereports.into());
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: Version = Version::new(297, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(298, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ pub static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(298, "support-bundle-zone-types"),
KnownVersion::new(297, "support-bundle-time-range"),
KnownVersion::new(296, "vmm-stop-for-update-disposition-generation"),
KnownVersion::new(295, "add-inv-sled-update-disposition"),
Expand Down
70 changes: 63 additions & 7 deletions nexus/db-model/src/support_bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use nexus_types::internal_api::views as internal_views;
use nexus_types::support_bundle::BundleData;
use nexus_types::support_bundle::BundleTimeRange;
use nexus_types::support_bundle::SledSelection;
use nexus_types::support_bundle::ZoneSelection;
use omicron_uuid_kinds::CaseKind;
use omicron_uuid_kinds::CaseUuid;
use omicron_uuid_kinds::DatasetKind;
Expand Down Expand Up @@ -182,12 +183,15 @@ pub struct HostInfo {
pub bundle_id: DbTypedUuid<SupportBundleKind>,
pub all_sleds: bool,
pub sled_ids: Vec<uuid::Uuid>,
pub all_zone_types: bool,
pub zone_types: Vec<String>,
}

impl HostInfo {
pub fn new(
bundle_id: impl Into<DbTypedUuid<SupportBundleKind>>,
sleds: SledSelection,
zones: ZoneSelection,
) -> Self {
let (all_sleds, sled_ids) = match sleds {
SledSelection::All => (true, Vec::new()),
Expand All @@ -196,22 +200,74 @@ impl HostInfo {
ids.into_iter().map(|id| id.into_untyped_uuid()).collect(),
),
};
HostInfo { bundle_id: bundle_id.into(), all_sleds, sled_ids }
let (all_zone_types, zone_types) = zone_selection_to_columns(zones);
HostInfo {
bundle_id: bundle_id.into(),
all_sleds,
sled_ids,
all_zone_types,
zone_types,
}
}
}

impl From<HostInfo> for BundleData {
fn from(row: HostInfo) -> Self {
let HostInfo { bundle_id: _, all_sleds, sled_ids } = row;
let selection = if all_sleds {
impl TryFrom<HostInfo> for BundleData {
type Error = omicron_common::api::external::Error;

fn try_from(row: HostInfo) -> Result<Self, Self::Error> {
let HostInfo {
bundle_id: _,
all_sleds,
sled_ids,
all_zone_types,
zone_types,
} = row;
let sleds = if all_sleds {
SledSelection::All
} else {
SledSelection::Specific(
sled_ids.into_iter().map(SledUuid::from_untyped_uuid).collect(),
)
};
BundleData::HostInfo(selection)
let zones = zone_selection_from_columns(all_zone_types, zone_types)?;
Ok(BundleData::HostInfo { sleds, zones })
}
}

/// Converts a [`ZoneSelection`] into its host-info column pair.
pub(crate) fn zone_selection_to_columns(
zones: ZoneSelection,
) -> (bool, Vec<String>) {
match zones {
ZoneSelection::All => (true, Vec::new()),
ZoneSelection::Types(types) => (
false,
types.into_iter().map(|t| t.as_str().to_string()).collect(),
),
}
}

/// Converts a host-info column pair back into a [`ZoneSelection`],
/// erroring on a stored zone type this version does not recognize
/// (rather than silently dropping it from the selection).
pub(crate) fn zone_selection_from_columns(
all_zone_types: bool,
zone_types: Vec<String>,
) -> Result<ZoneSelection, omicron_common::api::external::Error> {
if all_zone_types {
return Ok(ZoneSelection::All);
}
zone_types
.iter()
.map(|s| {
s.parse().map_err(|err| {
omicron_common::api::external::Error::internal_error(&format!(
"reading stored zone-type selection: {err}"
))
})
})
.collect::<Result<_, _>>()
.map(ZoneSelection::Types)
}

#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
Expand Down Expand Up @@ -308,7 +364,7 @@ impl TryFrom<BundleDataSelection>
selection.insert(BundleData::SpDumps);
}
if let Some(host_info) = row.host_info {
selection.insert(host_info.into());
selection.insert(host_info.try_into()?);
}
if let Some(ereports) = row.ereports {
selection.insert(ereports.into());
Expand Down
Loading
Loading