From fb4894e22274273953e3497784df41c2e11a45af Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Wed, 1 Jul 2026 16:33:21 -0400 Subject: [PATCH] Oximeter: Expose `kind` in `ProducerDetails` and plumb to omdb. I noticed that `omdb oximeter list-producers` and `omdb oximeter producer-details` both omit the `kind` of each producer. This can be useful to show! This patch adds `kind` to `ProducerDetails`, so that we can plumb it to `omdb oximeter producer-details`, adding a new oximeter api version in the process. We also expose the existing `kind` field of `ProducerEndpoint` to `omdb oximeter list-producers`. --- dev-tools/omdb/src/bin/omdb/oximeter.rs | 6 ++ .../oximeter-1.0.0-8e3b9c.json.gitstub | 1 + ...8e3b9c.json => oximeter-2.0.0-9d0503.json} | 11 ++- openapi/oximeter/oximeter-latest.json | 2 +- oximeter/api/src/lib.rs | 20 +++++- .../versions/src/add_producer_kind/mod.rs | 9 +++ .../src/add_producer_kind/producer.rs | 67 +++++++++++++++++++ oximeter/types/versions/src/impls/producer.rs | 1 + oximeter/types/versions/src/latest.rs | 3 +- oximeter/types/versions/src/lib.rs | 2 + 10 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 openapi/oximeter/oximeter-1.0.0-8e3b9c.json.gitstub rename openapi/oximeter/{oximeter-1.0.0-8e3b9c.json => oximeter-2.0.0-9d0503.json} (98%) create mode 100644 oximeter/types/versions/src/add_producer_kind/mod.rs create mode 100644 oximeter/types/versions/src/add_producer_kind/producer.rs diff --git a/dev-tools/omdb/src/bin/omdb/oximeter.rs b/dev-tools/omdb/src/bin/omdb/oximeter.rs index b3d6bcafd67..d0e4a3e998c 100644 --- a/dev-tools/omdb/src/bin/omdb/oximeter.rs +++ b/dev-tools/omdb/src/bin/omdb/oximeter.rs @@ -140,6 +140,7 @@ impl OximeterArgs { #[tabled(rename_all = "SCREAMING_SNAKE_CASE")] struct Producer { id: Uuid, + kind: String, address: SocketAddr, interval: String, } @@ -148,6 +149,7 @@ impl From for Producer { fn from(p: ProducerEndpoint) -> Self { Self { id: p.id, + kind: p.kind.to_string(), address: p.address.parse().unwrap(), interval: duration_to_humantime(&p.interval), } @@ -164,6 +166,7 @@ const WIDTH: usize = 12; fn print_producer_details(details: ProducerDetails) { println!(); println!("{:>WIDTH$}: {}", "ID", details.id); + println!("{:>WIDTH$}: {:?}", "Kind", details.kind); println!("{:>WIDTH$}: {}", "Address", details.address); println!( "{:>WIDTH$}: {}", @@ -258,6 +261,7 @@ mod tests { use chrono::Utc; use oximeter_client::types::FailedCollection; use oximeter_client::types::ProducerDetails; + use oximeter_client::types::ProducerKind; use oximeter_client::types::SuccessfulCollection; use std::time::Duration; use uuid::Uuid; @@ -267,6 +271,7 @@ mod tests { let now = Utc::now(); let details = ProducerDetails { id: Uuid::new_v4(), + kind: ProducerKind::SledAgent, address: "[::1]:12345".parse().unwrap(), interval: Duration::from_secs(10).into(), last_success: Some(SuccessfulCollection { @@ -289,6 +294,7 @@ mod tests { let now = Utc::now(); let details = ProducerDetails { id: Uuid::new_v4(), + kind: ProducerKind::SledAgent, interval: Duration::from_secs(10).into(), address: "[::1]:12345".parse().unwrap(), last_success: Some(SuccessfulCollection { diff --git a/openapi/oximeter/oximeter-1.0.0-8e3b9c.json.gitstub b/openapi/oximeter/oximeter-1.0.0-8e3b9c.json.gitstub new file mode 100644 index 00000000000..118b1b2381a --- /dev/null +++ b/openapi/oximeter/oximeter-1.0.0-8e3b9c.json.gitstub @@ -0,0 +1 @@ +a582b520e5926304437d844dd68dcb03dd1d04cf:openapi/oximeter/oximeter-1.0.0-8e3b9c.json diff --git a/openapi/oximeter/oximeter-1.0.0-8e3b9c.json b/openapi/oximeter/oximeter-2.0.0-9d0503.json similarity index 98% rename from openapi/oximeter/oximeter-1.0.0-8e3b9c.json rename to openapi/oximeter/oximeter-2.0.0-9d0503.json index e78c47d07f0..da7f1b760aa 100644 --- a/openapi/oximeter/oximeter-1.0.0-8e3b9c.json +++ b/openapi/oximeter/oximeter-2.0.0-9d0503.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "1.0.0" + "version": "2.0.0" }, "paths": { "/info": { @@ -261,6 +261,14 @@ } ] }, + "kind": { + "description": "The kind of producer.", + "allOf": [ + { + "$ref": "#/components/schemas/ProducerKind" + } + ] + }, "last_failure": { "nullable": true, "description": "Details about the last failed collection.\n\nThis is None if we've never failed to collect from the producer.", @@ -306,6 +314,7 @@ "address", "id", "interval", + "kind", "n_collections", "n_failures", "registered", diff --git a/openapi/oximeter/oximeter-latest.json b/openapi/oximeter/oximeter-latest.json index e07d9a8f248..4363e44b88b 120000 --- a/openapi/oximeter/oximeter-latest.json +++ b/openapi/oximeter/oximeter-latest.json @@ -1 +1 @@ -oximeter-1.0.0-8e3b9c.json \ No newline at end of file +oximeter-2.0.0-9d0503.json \ No newline at end of file diff --git a/oximeter/api/src/lib.rs b/oximeter/api/src/lib.rs index dd5b5de94d7..f0ce81239f2 100644 --- a/oximeter/api/src/lib.rs +++ b/oximeter/api/src/lib.rs @@ -8,7 +8,7 @@ use dropshot::{ }; use dropshot_api_manager_types::api_versions; use omicron_common::api::internal::nexus::ProducerEndpoint; -use oximeter_types_versions::latest; +use oximeter_types_versions::{latest, v1}; api_versions!([ // WHEN CHANGING THE API (part 1 of 2): @@ -22,6 +22,7 @@ api_versions!([ // | example for the next person. // v // (next_int, IDENT), + (2, ADD_PRODUCER_KIND), (1, INITIAL), ]); @@ -57,12 +58,29 @@ pub trait OximeterApi { #[endpoint { method = GET, path = "/producers/{producer_id}", + versions = VERSION_ADD_PRODUCER_KIND.., }] async fn producer_details( request_context: RequestContext, path: dropshot::Path, ) -> Result, HttpError>; + /// Get details about a producer by ID. + #[endpoint { + operation_id = "producer_details", + method = GET, + path = "/producers/{producer_id}", + versions = ..VERSION_ADD_PRODUCER_KIND, + }] + async fn producer_details_v1( + request_context: RequestContext, + path: dropshot::Path, + ) -> Result, HttpError> { + Ok(Self::producer_details(request_context, path) + .await? + .map(v1::producer::ProducerDetails::from)) + } + /// Delete a producer by ID. #[endpoint { method = DELETE, diff --git a/oximeter/types/versions/src/add_producer_kind/mod.rs b/oximeter/types/versions/src/add_producer_kind/mod.rs new file mode 100644 index 00000000000..63b8b905a45 --- /dev/null +++ b/oximeter/types/versions/src/add_producer_kind/mod.rs @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_PRODUCER_KIND` of the Oximeter API. +//! +//! Adds the producer's `kind` to `ProducerDetails`. + +pub mod producer; diff --git a/oximeter/types/versions/src/add_producer_kind/producer.rs b/oximeter/types/versions/src/add_producer_kind/producer.rs new file mode 100644 index 00000000000..7e0d8eea9b4 --- /dev/null +++ b/oximeter/types/versions/src/add_producer_kind/producer.rs @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Producer-related types for the Oximeter collector. + +use crate::v1; +use chrono::{DateTime, Utc}; +use omicron_common::api::internal::nexus::ProducerKind; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::net::SocketAddr; +use std::time::Duration; +use uuid::Uuid; + +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ProducerDetails { + /// The producer's ID. + pub id: Uuid, + + /// The kind of producer. + pub kind: ProducerKind, + + /// The current collection interval. + pub interval: Duration, + + /// The current collection address. + pub address: SocketAddr, + + /// The time the producer was first registered with us. + pub registered: DateTime, + + /// The last time the producer's information was updated. + pub updated: DateTime, + + /// Details about the last successful collection. + /// + /// This is None if we've never successfully collected from the producer. + pub last_success: Option, + + /// Details about the last failed collection. + /// + /// This is None if we've never failed to collect from the producer. + pub last_failure: Option, + + /// The total number of successful collections we've made. + pub n_collections: u64, + + /// The total number of failed collections. + pub n_failures: u64, +} + +impl From for v1::producer::ProducerDetails { + fn from(new: ProducerDetails) -> Self { + Self { + id: new.id, + interval: new.interval, + address: new.address, + registered: new.registered, + updated: new.updated, + last_success: new.last_success, + last_failure: new.last_failure, + n_collections: new.n_collections, + n_failures: new.n_failures, + } + } +} diff --git a/oximeter/types/versions/src/impls/producer.rs b/oximeter/types/versions/src/impls/producer.rs index 001ea2a75ea..0e21d4941d2 100644 --- a/oximeter/types/versions/src/impls/producer.rs +++ b/oximeter/types/versions/src/impls/producer.rs @@ -15,6 +15,7 @@ impl ProducerDetails { let now = Utc::now(); Self { id: info.id, + kind: info.kind, interval: info.interval, address: info.address, registered: now, diff --git a/oximeter/types/versions/src/latest.rs b/oximeter/types/versions/src/latest.rs index e1cf1506c34..7cc19b271bc 100644 --- a/oximeter/types/versions/src/latest.rs +++ b/oximeter/types/versions/src/latest.rs @@ -24,10 +24,11 @@ pub mod histogram { pub mod producer { pub use crate::v1::producer::FailedCollection; - pub use crate::v1::producer::ProducerDetails; pub use crate::v1::producer::ProducerIdPathParams; pub use crate::v1::producer::ProducerPage; pub use crate::v1::producer::SuccessfulCollection; + + pub use crate::v3::producer::ProducerDetails; } pub mod quantile { diff --git a/oximeter/types/versions/src/lib.rs b/oximeter/types/versions/src/lib.rs index e6ba0613f44..a4fab024e78 100644 --- a/oximeter/types/versions/src/lib.rs +++ b/oximeter/types/versions/src/lib.rs @@ -35,3 +35,5 @@ pub mod latest; pub mod v1; #[path = "add_joules/mod.rs"] pub mod v2; +#[path = "add_producer_kind/mod.rs"] +pub mod v3;