Skip to content

Commit bac3385

Browse files
authored
[omdb] Basic commands to access support bundles (#7972)
Testing somewhat manually: ```bash $ cargo xtask omicron-dev run-all ... # In another terminal... $ export OMDB_NEXUS_URL=http://[::1]:12221 $ ./target/debug/omdb nexus -w support-bundles create note: using Nexus URL http://[::1]:12221 created support bundle: 24c837af-dda4-4569-9baf-029bf9bd628d $ ./target/debug/omdb nexus support-bundles list note: using Nexus URL http://[::1]:12221 ID TIME_CREATED REASON_FOR_CREATION REASON_FOR_FAILURE STATE 24c837af-dda4-4569-9baf-029bf9bd628d 2025-04-15 22:54:25.182963 UTC Created by external API - Active # It's expected that these are returning "_err.txt" variants -- the simulated sled agent returns "not implemented" for # most of them. $ ./target/debug/omdb nexus -w support-bundles get-index 24c837af-dda4-4569-9baf-029bf9bd628d note: using Nexus URL http://[::1]:12221 bundle_id.txt rack/ rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/ rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/ rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/ rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/dladm_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/ipadm_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/nvmeadm_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/pargs_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/pfiles_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/pstack_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/sled.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/zfs_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/zoneadm_err.txt rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/zpool_err.txt # Grabbing the contents of a file, dumping it to stdout $ ./target/debug/omdb nexus -w support-bundles get-file 24c837af-dda4-4569-9baf-029bf9bd628d rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/sled.txt note: using Nexus URL http://[::1]:12221 Sled { identity: SledIdentity { id: b6d65341-167c-41df-9b5c-41cded99c229, time_created: 2025-04-15T22:54:05.449256Z, time_modified: 2025-04-15T22:54:05.449256Z }, time_deleted: None, rcgen: Generation(Generation(10)), rack_id: c19a698f-c6f9-4a17-ae30-20d711b8f7dc, is_scrimlet: true, serial_number: "sim-b6d65341", part_number: "Unknown", revision: SqlU32(0), usable_hardware_threads: SqlU32(16), usable_physical_ram: ByteCount(ByteCount(34359738368)), reservoir_size: ByteCount(ByteCount(17179869184)), ip: ::1, port: SqlU16(46313), last_used_address: ::ffff, policy: InService, state: Active, sled_agent_gen: Generation(Generation(1)), repo_depot_port: SqlU16(42437) } ```
1 parent 7f0ac3d commit bac3385

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

Cargo.lock

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

dev-tools/omdb/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ omicron-rpaths.workspace = true
1313
[dependencies]
1414
anyhow.workspace = true
1515
async-bb8-diesel.workspace = true
16+
bytes.workspace = true
1617
camino.workspace = true
1718
chrono.workspace = true
1819
clap.workspace = true
@@ -50,6 +51,7 @@ oximeter-db = { workspace = true, default-features = false, features = [ "oxql"
5051
pq-sys = "*"
5152
ratatui.workspace = true
5253
reedline.workspace = true
54+
reqwest.workspace = true
5355
serde.workspace = true
5456
serde_json.workspace = true
5557
sled-agent-client.workspace = true

dev-tools/omdb/src/bin/omdb/nexus.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use clap::Args;
2222
use clap::ColorChoice;
2323
use clap::Subcommand;
2424
use clap::ValueEnum;
25+
use futures::StreamExt;
2526
use futures::TryStreamExt;
2627
use futures::future::try_join;
2728
use http::StatusCode;
@@ -70,6 +71,7 @@ use omicron_uuid_kinds::GenericUuid;
7071
use omicron_uuid_kinds::ParseError;
7172
use omicron_uuid_kinds::PhysicalDiskUuid;
7273
use omicron_uuid_kinds::SledUuid;
74+
use omicron_uuid_kinds::SupportBundleUuid;
7375
use serde::Deserialize;
7476
use slog_error_chain::InlineErrorChain;
7577
use std::collections::BTreeMap;
@@ -127,6 +129,9 @@ enum NexusCommands {
127129
Sagas(SagasArgs),
128130
/// interact with sleds
129131
Sleds(SledsArgs),
132+
/// interact with support bundles
133+
#[command(visible_alias = "sb")]
134+
SupportBundles(SupportBundleArgs),
130135
}
131136

132137
#[derive(Debug, Args)]
@@ -475,6 +480,49 @@ struct DiskExpungeArgs {
475480
physical_disk_id: PhysicalDiskUuid,
476481
}
477482

483+
#[derive(Debug, Args)]
484+
struct SupportBundleArgs {
485+
#[command(subcommand)]
486+
command: SupportBundleCommands,
487+
}
488+
489+
#[derive(Debug, Subcommand)]
490+
#[allow(clippy::large_enum_variant)]
491+
enum SupportBundleCommands {
492+
/// List all support bundles
493+
List,
494+
/// Create a new support bundle
495+
Create,
496+
/// Delete a support bundle
497+
Delete(SupportBundleDeleteArgs),
498+
/// Download the index of a support bundle
499+
///
500+
/// This is a "list of files", from which individual files can be accessed
501+
GetIndex(SupportBundleIndexArgs),
502+
/// View a file within a support bundle
503+
GetFile(SupportBundleFileArgs),
504+
}
505+
506+
#[derive(Debug, Args)]
507+
struct SupportBundleDeleteArgs {
508+
id: SupportBundleUuid,
509+
}
510+
511+
#[derive(Debug, Args)]
512+
struct SupportBundleIndexArgs {
513+
id: SupportBundleUuid,
514+
}
515+
516+
#[derive(Debug, Args)]
517+
struct SupportBundleFileArgs {
518+
id: SupportBundleUuid,
519+
path: Utf8PathBuf,
520+
/// Optional output path where the file should be written,
521+
/// instead of stdout.
522+
#[arg(short, long)]
523+
output: Option<Utf8PathBuf>,
524+
}
525+
478526
impl NexusArgs {
479527
/// Run a `omdb nexus` subcommand.
480528
pub(crate) async fn run_cmd(
@@ -668,6 +716,27 @@ impl NexusArgs {
668716
cmd_nexus_sled_expunge_disk(&client, args, omdb, log, token)
669717
.await
670718
}
719+
NexusCommands::SupportBundles(SupportBundleArgs {
720+
command: SupportBundleCommands::List,
721+
}) => cmd_nexus_support_bundles_list(&client).await,
722+
NexusCommands::SupportBundles(SupportBundleArgs {
723+
command: SupportBundleCommands::Create,
724+
}) => {
725+
let token = omdb.check_allow_destructive()?;
726+
cmd_nexus_support_bundles_create(&client, token).await
727+
}
728+
NexusCommands::SupportBundles(SupportBundleArgs {
729+
command: SupportBundleCommands::Delete(args),
730+
}) => {
731+
let token = omdb.check_allow_destructive()?;
732+
cmd_nexus_support_bundles_delete(&client, args, token).await
733+
}
734+
NexusCommands::SupportBundles(SupportBundleArgs {
735+
command: SupportBundleCommands::GetIndex(args),
736+
}) => cmd_nexus_support_bundles_get_index(&client, args).await,
737+
NexusCommands::SupportBundles(SupportBundleArgs {
738+
command: SupportBundleCommands::GetFile(args),
739+
}) => cmd_nexus_support_bundles_get_file(&client, args).await,
671740
}
672741
}
673742
}
@@ -3682,3 +3751,132 @@ async fn cmd_nexus_sled_expunge_disk_with_datastore(
36823751
eprintln!("expunged disk {}", args.physical_disk_id);
36833752
Ok(())
36843753
}
3754+
3755+
/// Runs `omdb nexus support-bundles list`
3756+
async fn cmd_nexus_support_bundles_list(
3757+
client: &nexus_client::Client,
3758+
) -> Result<(), anyhow::Error> {
3759+
let support_bundle_stream = client.support_bundle_list_stream(None, None);
3760+
3761+
let support_bundles = support_bundle_stream
3762+
.try_collect::<Vec<_>>()
3763+
.await
3764+
.context("listing support bundles")?;
3765+
3766+
#[derive(Tabled)]
3767+
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
3768+
struct SupportBundleInfo {
3769+
id: Uuid,
3770+
time_created: DateTime<Utc>,
3771+
reason_for_creation: String,
3772+
reason_for_failure: String,
3773+
state: String,
3774+
}
3775+
let rows = support_bundles.into_iter().map(|sb| SupportBundleInfo {
3776+
id: *sb.id,
3777+
time_created: sb.time_created,
3778+
reason_for_creation: sb.reason_for_creation,
3779+
reason_for_failure: sb
3780+
.reason_for_failure
3781+
.unwrap_or_else(|| "-".to_string()),
3782+
state: format!("{:?}", sb.state),
3783+
});
3784+
let table = tabled::Table::new(rows)
3785+
.with(tabled::settings::Style::empty())
3786+
.with(tabled::settings::Padding::new(0, 1, 0, 0))
3787+
.to_string();
3788+
println!("{}", table);
3789+
Ok(())
3790+
}
3791+
3792+
/// Runs `omdb nexus support-bundles create`
3793+
async fn cmd_nexus_support_bundles_create(
3794+
client: &nexus_client::Client,
3795+
_destruction_token: DestructiveOperationToken,
3796+
) -> Result<(), anyhow::Error> {
3797+
let support_bundle_id = client
3798+
.support_bundle_create()
3799+
.await
3800+
.context("creating support bundle")?
3801+
.into_inner()
3802+
.id;
3803+
println!("created support bundle: {support_bundle_id}");
3804+
Ok(())
3805+
}
3806+
3807+
/// Runs `omdb nexus support-bundles delete`
3808+
async fn cmd_nexus_support_bundles_delete(
3809+
client: &nexus_client::Client,
3810+
args: &SupportBundleDeleteArgs,
3811+
_destruction_token: DestructiveOperationToken,
3812+
) -> Result<(), anyhow::Error> {
3813+
let _ = client
3814+
.support_bundle_delete(args.id.as_untyped_uuid())
3815+
.await
3816+
.with_context(|| format!("deleting support bundle {}", args.id))?;
3817+
println!("support bundle {} deleted", args.id);
3818+
Ok(())
3819+
}
3820+
3821+
async fn write_stream_to_sink(
3822+
mut stream: impl futures::Stream<Item = reqwest::Result<bytes::Bytes>>
3823+
+ std::marker::Unpin,
3824+
mut sink: impl std::io::Write,
3825+
) -> Result<(), anyhow::Error> {
3826+
while let Some(data) = stream.next().await {
3827+
match data {
3828+
Err(err) => return Err(anyhow::anyhow!(err)),
3829+
Ok(data) => sink.write_all(&data)?,
3830+
}
3831+
}
3832+
Ok(())
3833+
}
3834+
3835+
/// Runs `omdb nexus support-bundles get-index`
3836+
async fn cmd_nexus_support_bundles_get_index(
3837+
client: &nexus_client::Client,
3838+
args: &SupportBundleIndexArgs,
3839+
) -> Result<(), anyhow::Error> {
3840+
let stream = client
3841+
.support_bundle_index(args.id.as_untyped_uuid())
3842+
.await
3843+
.with_context(|| {
3844+
format!("downloading support bundle index {}", args.id)
3845+
})?
3846+
.into_inner_stream();
3847+
3848+
write_stream_to_sink(stream, std::io::stdout()).await.with_context(
3849+
|| format!("streaming support bundle index {}", args.id),
3850+
)?;
3851+
Ok(())
3852+
}
3853+
3854+
/// Runs `omdb nexus support-bundles get-file`
3855+
async fn cmd_nexus_support_bundles_get_file(
3856+
client: &nexus_client::Client,
3857+
args: &SupportBundleFileArgs,
3858+
) -> Result<(), anyhow::Error> {
3859+
let stream = client
3860+
.support_bundle_download_file(
3861+
args.id.as_untyped_uuid(),
3862+
args.path.as_str(),
3863+
)
3864+
.await
3865+
.with_context(|| {
3866+
format!(
3867+
"downloading support bundle file {}: {}",
3868+
args.id, args.path
3869+
)
3870+
})?
3871+
.into_inner_stream();
3872+
3873+
let sink: Box<dyn std::io::Write> = match &args.output {
3874+
Some(path) => Box::new(std::fs::File::create(path)?),
3875+
None => Box::new(std::io::stdout()),
3876+
};
3877+
3878+
write_stream_to_sink(stream, sink).await.with_context(|| {
3879+
format!("streaming support bundle file {}: {}", args.id, args.path)
3880+
})?;
3881+
Ok(())
3882+
}

dev-tools/omdb/tests/usage_errors.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ Commands:
706706
oximeter-read-policy interact with oximeter read policy
707707
sagas view sagas, create and complete demo sagas
708708
sleds interact with sleds
709+
support-bundles interact with support bundles [aliases: sb]
709710
help Print this message or the help of the given subcommand(s)
710711

711712
Options:

0 commit comments

Comments
 (0)