diff --git a/Cargo.lock b/Cargo.lock index 5b032f8058b..74f4c56284b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,7 +400,7 @@ dependencies = [ "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", "salty", "serde", - "serde_with", + "serde_with 3.17.0", "sha3", "static_assertions", "thiserror 2.0.18", @@ -419,7 +419,7 @@ dependencies = [ "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", "salty", "serde", - "serde_with", + "serde_with 3.17.0", "sha3", "static_assertions", "thiserror 2.0.18", @@ -438,7 +438,7 @@ dependencies = [ "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", "salty", "serde", - "serde_with", + "serde_with 3.17.0", "sha3", "static_assertions", "thiserror 2.0.18", @@ -830,7 +830,7 @@ dependencies = [ "rand 0.8.6", "secrecy 0.10.3", "serde", - "serde_with", + "serde_with 3.17.0", "sha3", "sled-hardware-types", "slog", @@ -1419,7 +1419,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", "terminal_size 0.4.3", ] @@ -2362,6 +2362,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.20.11" @@ -2392,6 +2402,20 @@ dependencies = [ "darling_macro 0.23.0", ] +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + [[package]] name = "darling_core" version = "0.20.11" @@ -2402,7 +2426,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.117", ] @@ -2416,7 +2440,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.117", ] @@ -2429,10 +2453,21 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.117", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.11" @@ -3611,6 +3646,15 @@ dependencies = [ "typeid", ] +[[package]] +name = "erebor" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/erebor?rev=48512bf970474ff0fd0868c833fd504c8d169064#48512bf970474ff0fd0868c833fd504c8d169064" +dependencies = [ + "pmbus", + "serde_json", +] + [[package]] name = "ereport-types" version = "0.1.0" @@ -5617,7 +5661,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "test-strategy", "thiserror 2.0.18", "tokio", @@ -6867,7 +6911,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sled-agent-types", "tokio-postgres", "toml 0.8.23", @@ -7065,7 +7109,7 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sha2", "sled-agent-client", "sled-agent-types", @@ -7812,7 +7856,7 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sled-agent-types", "sled-agent-types-versions", "sled-hardware-types", @@ -8415,7 +8459,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "serde_with", + "serde_with 3.17.0", "slog", "slog-error-chain", "strum 0.27.2", @@ -8807,7 +8851,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "serde_with", + "serde_with 3.17.0", "sha2", "similar-asserts", "sled-agent-client", @@ -8909,6 +8953,7 @@ dependencies = [ "diesel", "dropshot 0.17.0", "dyn-clone", + "erebor", "ereport-types", "expectorate", "fmd-adm-sys", @@ -9035,7 +9080,7 @@ dependencies = [ "schemars 0.8.22", "secrecy 0.10.3", "serde", - "serde_with", + "serde_with 3.17.0", "thiserror 2.0.18", ] @@ -9478,8 +9523,8 @@ dependencies = [ "serde", "serde_core", "serde_json", - "serde_with", - "serde_with_macros", + "serde_with 3.17.0", + "serde_with_macros 3.17.0", "sha1", "sha2", "sha3", @@ -10850,6 +10895,21 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "pmbus" +version = "0.1.7" +source = "git+https://github.com/oxidecomputer/pmbus?rev=3e681b17aafce32e80e06aff2dd841c9153cf069#3e681b17aafce32e80e06aff2dd841c9153cf069" +dependencies = [ + "anyhow", + "convert_case 0.4.0", + "libm", + "num-derive 0.4.2", + "num-traits", + "ron", + "serde", + "serde_with 1.14.0", +] + [[package]] name = "polar-core" version = "0.27.3" @@ -11845,7 +11905,7 @@ dependencies = [ "clap", "hex", "serde", - "serde_with", + "serde_with 3.17.0", "strum 0.26.3", "thiserror 2.0.18", ] @@ -11860,7 +11920,7 @@ dependencies = [ "clap", "hex", "serde", - "serde_with", + "serde_with 3.17.0", "strum 0.26.3", "thiserror 2.0.18", ] @@ -13303,6 +13363,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros 1.5.2", +] + [[package]] name = "serde_with" version = "3.17.0" @@ -13319,10 +13389,22 @@ dependencies = [ "schemars 1.2.1", "serde_core", "serde_json", - "serde_with_macros", + "serde_with_macros 3.17.0", "time", ] +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "serde_with_macros" version = "3.17.0" @@ -13845,7 +13927,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sha3", "sled-hardware-types", "slog", @@ -14478,6 +14560,12 @@ dependencies = [ "vte", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -15770,7 +15858,7 @@ dependencies = [ "secrecy 0.10.3", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sha3", "sled-agent-measurements", "sled-agent-types", @@ -15817,7 +15905,7 @@ dependencies = [ "secrecy 0.10.3", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "sha3", "sled-agent-types", "sled-hardware-types", @@ -15877,7 +15965,7 @@ dependencies = [ "rand 0.9.2", "schemars 0.8.22", "serde", - "serde_with", + "serde_with 3.17.0", "sled-hardware-types", "slog", "slog-error-chain", @@ -16368,7 +16456,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "serde_with", + "serde_with 3.17.0", "slog", "supports-color 3.0.2", "swrite", diff --git a/Cargo.toml b/Cargo.toml index 0a9cc97e5c7..aba7ff9ac89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -507,6 +507,7 @@ dropshot-api-manager = "0.7.1" dropshot-api-manager-types = "0.7.1" dyn-clone = "1.0.20" either = "1.15.0" +erebor = { git = "https://github.com/oxidecomputer/erebor", rev = "48512bf970474ff0fd0868c833fd504c8d169064" } ereport-types = { path = "ereport/types" } expectorate = "1.2.0" fatfs = "0.3.6" diff --git a/dev-tools/omdb/Cargo.toml b/dev-tools/omdb/Cargo.toml index 777a715aa7d..09780e918d5 100644 --- a/dev-tools/omdb/Cargo.toml +++ b/dev-tools/omdb/Cargo.toml @@ -30,6 +30,7 @@ daft.workspace = true diesel.workspace = true dropshot.workspace = true dyn-clone.workspace = true +erebor = { workspace = true, features = ["pmbus"] } ereport-types.workspace = true futures.workspace = true gateway-client.workspace = true diff --git a/dev-tools/omdb/src/bin/omdb/db/ereport.rs b/dev-tools/omdb/src/bin/omdb/db/ereport.rs index 44888e1314c..d9653772928 100644 --- a/dev-tools/omdb/src/bin/omdb/db/ereport.rs +++ b/dev-tools/omdb/src/bin/omdb/db/ereport.rs @@ -83,6 +83,22 @@ struct InfoArgs { restart_id: EreporterRestartUuid, /// The ENA of the ereport within the reporter restart ena: Ena, + + /// If set, output the ereport as JSON, rather than using the default + /// human-readable format. + /// + /// Note that this will output a JSON object containing the raw ereport data + /// along with additional metadata from the database record for this + /// ereport. To emit *only* the original JSON received from the reporter, + /// add the `--raw` flag in addition to `--json`. + #[clap(long, short)] + json: bool, + + /// If outputting the ereport as JSON, output *only* the raw JSON received + /// from the reporter in its original form, omitting additional metadata + /// from the database. + #[clap(long, short, requires("json"))] + raw: bool, } #[derive(Debug, Args, Clone)] @@ -158,6 +174,7 @@ pub(super) async fn cmd_db_ereport( Commands::List(ref args) => { cmd_db_ereport_list(datastore, fetch_opts, args).await } + Commands::Info(ref args) => { cmd_db_ereport_info(datastore, fetch_opts, args).await } @@ -343,7 +360,7 @@ async fn cmd_db_ereport_info( fetch_opts: &DbFetchOptions, args: &InfoArgs, ) -> anyhow::Result<()> { - let &InfoArgs { restart_id, ena } = args; + let &InfoArgs { restart_id, ena, json, raw } = args; let ereport_id = ereport_types::EreportId { restart_id, ena }; let conn = datastore.pool_connection_for_tests().await?; let ereport = ereport_fetch(&conn, fetch_opts, ereport_id).await?; @@ -370,62 +387,95 @@ async fn cmd_db_ereport_info( SERIAL_NUMBER, MARKED_SEEN_IN, ]); - let db::model::Ereport { - ena: DbEna(ena), - restart_id, - time_deleted, - time_collected, - collector_id, - ref part_number, - ref serial_number, - ref class, - ref report, - reporter, - marked_seen_in, - } = ereport; - println!("\n{:=<80}", "== EREPORT METADATA "); - println!(" {ENA:>WIDTH$}: {ena}"); - match class { - Some(class) => println!(" {CLASS:>WIDTH$}: {class}"), - None => println!("/!\\ {CLASS:>WIDTH$}: "), - } - if let Some(time_deleted) = time_deleted { - println!("(i) {TIME_DELETED:>WIDTH$}: {time_deleted}"); - } - println!(" {TIME_COLLECTED:>WIDTH$}: {time_collected}"); - println!(" {COLLECTOR_ID:>WIDTH$}: {collector_id}"); - match Reporter::try_from(reporter) { - Err(err) => eprintln!("{err}"), - Ok(Reporter::Sp { sp_type, slot }) => { - println!( - " {REPORTER:>WIDTH$}: {sp_type:?} {slot} (service processor)" - ) + + if json && raw { + let ereport = ereport.report; + serde_json::to_writer_pretty(std::io::stdout(), &ereport) + .with_context(|| { + format!("failed to serialize raw ereport: {ereport:?}") + })?; + } else if json { + let raw_report = ereport.report.clone(); + match nexus_types::fm::Ereport::try_from(ereport) { + Ok(ereport) => { + serde_json::to_writer_pretty(std::io::stdout(), &ereport) + .with_context(|| { + format!( + "failed to serialize ereport with metadata: {ereport:?}" + ) + })?; + } + Err(e) => { + eprintln!( + "WARNING: failed to interpret ereport metadata, falling \ + back to raw JSON: {e}" + ); + serde_json::to_writer_pretty(std::io::stdout(), &raw_report) + .with_context(|| { + format!( + "failed to serialize raw ereport: {raw_report:?}" + ) + })?; + } } - Ok(Reporter::HostOs { sled, slot }) => { - if let Some(slot) = slot { - println!(" {REPORTER:>WIDTH$}: sled {slot} (host OS)"); - } else { + } else { + let db::model::Ereport { + ena: DbEna(ena), + restart_id, + time_deleted, + time_collected, + collector_id, + ref part_number, + ref serial_number, + ref class, + ref report, + reporter, + marked_seen_in, + } = ereport; + println!("\n{:=<80}", "== EREPORT METADATA "); + println!(" {ENA:>WIDTH$}: {ena}"); + match class { + Some(class) => println!(" {CLASS:>WIDTH$}: {class}"), + None => println!("/!\\ {CLASS:>WIDTH$}: "), + } + if let Some(time_deleted) = time_deleted { + println!("(i) {TIME_DELETED:>WIDTH$}: {time_deleted}"); + } + println!(" {TIME_COLLECTED:>WIDTH$}: {time_collected}"); + println!(" {COLLECTOR_ID:>WIDTH$}: {collector_id}"); + match Reporter::try_from(reporter) { + Err(err) => eprintln!("{err}"), + Ok(Reporter::Sp { sp_type, slot }) => { println!( - " {REPORTER:>WIDTH$}: (host OS)" - ); + " {REPORTER:>WIDTH$}: {sp_type:?} {slot} \ + (service processor)" + ) + } + Ok(Reporter::HostOs { sled, slot }) => { + if let Some(slot) = slot { + println!(" {REPORTER:>WIDTH$}: sled {slot} (host OS)"); + } else { + println!( + " {REPORTER:>WIDTH$}: (host OS)" + ); + } + println!(" {SLED_ID:>WIDTH$}: {sled:?}") } - println!(" {SLED_ID:>WIDTH$}: {sled:?}") } + println!(" {RESTART_ID:>WIDTH$}: {restart_id}"); + println!( + " {PART_NUMBER:>WIDTH$}: {}", + part_number.as_deref().unwrap_or("") + ); + println!( + " {SERIAL_NUMBER:>WIDTH$}: {}", + serial_number.as_deref().unwrap_or("") + ); + println!(" {MARKED_SEEN_IN:>WIDTH$}: {marked_seen_in:?}",); + + println!("\n{:=<80}", "== EREPORT "); + println!("{}", erebor::Displayer::new(&report)); } - println!(" {RESTART_ID:>WIDTH$}: {restart_id}"); - println!( - " {PART_NUMBER:>WIDTH$}: {}", - part_number.as_deref().unwrap_or("") - ); - println!( - " {SERIAL_NUMBER:>WIDTH$}: {}", - serial_number.as_deref().unwrap_or("") - ); - println!(" {MARKED_SEEN_IN:>WIDTH$}: {marked_seen_in:?}",); - - println!("\n{:=<80}", "== EREPORT "); - serde_json::to_writer_pretty(std::io::stdout(), &report) - .with_context(|| format!("failed to serialize ereport: {report:?}"))?; println!(); Ok(()) diff --git a/dev-tools/omdb/tests/usage_errors.out b/dev-tools/omdb/tests/usage_errors.out index 3795a6be563..b439a8d3cb7 100644 --- a/dev-tools/omdb/tests/usage_errors.out +++ b/dev-tools/omdb/tests/usage_errors.out @@ -682,26 +682,61 @@ Show an ereport Usage: omdb db ereport info [OPTIONS] Arguments: - The reporter restart UUID of the ereport to show - The ENA of the ereport within the reporter restart + + The reporter restart UUID of the ereport to show + + + The ENA of the ereport within the reporter restart Options: - --log-level log level filter [env: LOG_LEVEL=] [default: warn] - --color Color output [default: auto] [possible values: auto, always, never] - -h, --help Print help + -j, --json + If set, output the ereport as JSON, rather than using the default human-readable format. + + Note that this will output a JSON object containing the raw ereport data along with + additional metadata from the database record for this ereport. To emit *only* the original + JSON received from the reporter, add the `--raw` flag in addition to `--json`. + + --log-level + log level filter + + [env: LOG_LEVEL=] + [default: warn] + + -r, --raw + If outputting the ereport as JSON, output *only* the raw JSON received from the reporter + in its original form, omitting additional metadata from the database + + --color + Color output + + [default: auto] + [possible values: auto, always, never] + + -h, --help + Print help (see a summary with '-h') Connection Options: - --db-url URL of the database SQL interface [env: OMDB_DB_URL=] - --dns-server [env: OMDB_DNS_SERVER=] + --db-url + URL of the database SQL interface + + [env: OMDB_DB_URL=] + + --dns-server + [env: OMDB_DNS_SERVER=] Database Options: - --fetch-limit limit to apply to queries that fetch rows [env: - OMDB_FETCH_LIMIT=] [default: 500] - --include-deleted whether to include soft-deleted records when enumerating objects - that can be soft-deleted + --fetch-limit + limit to apply to queries that fetch rows + + [env: OMDB_FETCH_LIMIT=] + [default: 500] + + --include-deleted + whether to include soft-deleted records when enumerating objects that can be soft-deleted Safety Options: - -w, --destructive Allow potentially-destructive subcommands + -w, --destructive + Allow potentially-destructive subcommands --------------------------------------------- stderr: =============================================