Skip to content

Commit

Permalink
Add sled-agent endpoint for fetching sled identifiers (#6086)
Browse files Browse the repository at this point in the history
- Adds a `SledIdentifiers` type, with the most salient bits of
identifying metadata for a single sled
- Adds sled-agent endpoint `/sled-identifiers` for fetching the above
from the sled. The main goal is to provide the main data for #5267,
attaching sled identifiers to most timeseries.
- Small improvement to instructions in package-manifest.toml.
- Small bugfix to error-reporting in `xtask download`.
  • Loading branch information
bnaecker authored Jul 16, 2024
1 parent 6344feb commit 8316247
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 6 deletions.
30 changes: 30 additions & 0 deletions clients/sled-agent-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,36 @@ impl From<omicron_common::api::internal::shared::NetworkInterfaceKind>
}
}

impl From<omicron_common::api::internal::shared::SledIdentifiers>
for types::SledIdentifiers
{
fn from(
value: omicron_common::api::internal::shared::SledIdentifiers,
) -> Self {
Self {
model: value.model,
rack_id: value.rack_id,
revision: value.revision,
serial: value.serial,
sled_id: value.sled_id,
}
}
}

impl From<types::SledIdentifiers>
for omicron_common::api::internal::shared::SledIdentifiers
{
fn from(value: types::SledIdentifiers) -> Self {
Self {
model: value.model,
rack_id: value.rack_id,
revision: value.revision,
serial: value.serial,
sled_id: value.sled_id,
}
}
}

/// Exposes additional [`Client`] interfaces for use by the test suite. These
/// are bonus endpoints, not generated in the real client.
#[async_trait]
Expand Down
20 changes: 20 additions & 0 deletions common/src/api/internal/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,26 @@ pub struct ResolvedVpcRouteSet {
pub routes: HashSet<ResolvedVpcRoute>,
}

/// Identifiers for a single sled.
///
/// This is intended primarily to be used in timeseries, to identify
/// sled from which metric data originates.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
pub struct SledIdentifiers {
/// Control plane ID of the rack this sled is a member of
pub rack_id: Uuid,
/// Control plane ID for the sled itself
pub sled_id: Uuid,
/// Model name of the sled
pub model: String,
/// Revision number of the sled
pub revision: u32,
/// Serial number of the sled
//
// NOTE: This is only guaranteed to be unique within a model.
pub serial: String,
}

#[cfg(test)]
mod tests {
use crate::api::internal::shared::AllowedSourceIps;
Expand Down
2 changes: 1 addition & 1 deletion dev-tools/xtask/src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ async fn get_values_from_file<const N: usize>(

let content = tokio::fs::read_to_string(&path)
.await
.context("Failed to read {path}")?;
.with_context(|| format!("Failed to read {path}"))?;
for line in content.lines() {
let line = line.trim();
let Some((key, value)) = line.split_once('=') else {
Expand Down
61 changes: 61 additions & 0 deletions openapi/sled-agent.json
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,30 @@
}
}
},
"/sled-identifiers": {
"get": {
"summary": "Fetch sled identifiers",
"operationId": "sled_identifiers",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SledIdentifiers"
}
}
}
},
"4XX": {
"$ref": "#/components/responses/Error"
},
"5XX": {
"$ref": "#/components/responses/Error"
}
}
}
},
"/sled-role": {
"get": {
"operationId": "sled_role_get",
Expand Down Expand Up @@ -4549,6 +4573,43 @@
"type": "string",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
},
"SledIdentifiers": {
"description": "Identifiers for a single sled.\n\nThis is intended primarily to be used in timeseries, to identify sled from which metric data originates.",
"type": "object",
"properties": {
"model": {
"description": "Model name of the sled",
"type": "string"
},
"rack_id": {
"description": "Control plane ID of the rack this sled is a member of",
"type": "string",
"format": "uuid"
},
"revision": {
"description": "Revision number of the sled",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"serial": {
"description": "Serial number of the sled",
"type": "string"
},
"sled_id": {
"description": "Control plane ID for the sled itself",
"type": "string",
"format": "uuid"
}
},
"required": [
"model",
"rack_id",
"revision",
"serial",
"sled_id"
]
},
"SledInstanceState": {
"description": "A wrapper type containing a sled's total knowledge of the state of a specific VMM and the instance it incarnates.",
"type": "object",
Expand Down
12 changes: 9 additions & 3 deletions package-manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,10 @@ only_for_targets.image = "standard"
# 1. Build the zone image manually
# 1a. cd <dendrite tree>
# 1b. cargo build --features=tofino_stub --release
# 1c. cargo xtask dist -o -r --features tofino_stub
# 1c. cargo xtask dist --format omicron --release --features tofino_stub
# 2. Copy dendrite.tar.gz from dendrite/out to omicron/out
# 3. Change the below `source.type` key to `"manual"` and comment out or remove
# the other `source.*` keys.
source.type = "prebuilt"
source.repo = "dendrite"
source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36"
Expand All @@ -664,8 +666,10 @@ only_for_targets.image = "standard"
# 1. Build the zone image manually
# 1a. cd <dendrite tree>
# 1b. cargo build --features=tofino_asic --release
# 1c. cargo xtask dist -o -r --features tofino_asic
# 1c. cargo xtask dist --format omicron --release --features tofino_asic
# 2. Copy the output zone image from dendrite/out to omicron/out
# 3. Change the below `source.type` key to `"manual"` and comment out or remove
# the other `source.*` keys.
source.type = "prebuilt"
source.repo = "dendrite"
source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36"
Expand All @@ -682,8 +686,10 @@ only_for_targets.image = "standard"
# 1. Build the zone image manually
# 1a. cd <dendrite tree>
# 1b. cargo build --features=softnpu --release
# 1c. cargo xtask dist -o -r --features softnpu
# 1c. cargo xtask dist --format omicron --release --features softnpu
# 2. Copy dendrite.tar.gz from dendrite/out to omicron/out/dendrite-softnpu.tar.gz
# 3. Change the below `source.type` key to `"manual"` and comment out or remove
# the other `source.*` keys.
source.type = "prebuilt"
source.repo = "dendrite"
source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36"
Expand Down
19 changes: 18 additions & 1 deletion sled-agent/src/http_entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use omicron_common::api::internal::nexus::{
DiskRuntimeState, SledInstanceState, UpdateArtifactId,
};
use omicron_common::api::internal::shared::{
ResolvedVpcRouteSet, ResolvedVpcRouteState, SwitchPorts,
ResolvedVpcRouteSet, ResolvedVpcRouteState, SledIdentifiers, SwitchPorts,
};
use omicron_uuid_kinds::{GenericUuid, InstanceUuid};
use schemars::JsonSchema;
Expand Down Expand Up @@ -89,6 +89,7 @@ pub fn api() -> SledApiDescription {
api.register(host_os_write_status_get)?;
api.register(host_os_write_status_delete)?;
api.register(inventory)?;
api.register(sled_identifiers)?;
api.register(bootstore_status)?;
api.register(list_vpc_routes)?;
api.register(set_vpc_routes)?;
Expand Down Expand Up @@ -1012,6 +1013,22 @@ async fn inventory(
Ok(HttpResponseOk(sa.inventory().await?))
}

/// Fetch sled identifiers
#[endpoint {
method = GET,
path = "/sled-identifiers",
}]
async fn sled_identifiers(
request_context: RequestContext<SledAgent>,
) -> Result<HttpResponseOk<SledIdentifiers>, HttpError> {
request_context
.context()
.sled_identifiers()
.await
.map(HttpResponseOk)
.map_err(HttpError::from)
}

/// Get the internal state of the local bootstore node
#[endpoint {
method = GET,
Expand Down
30 changes: 29 additions & 1 deletion sled-agent/src/sled_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use omicron_common::api::internal::nexus::{
};
use omicron_common::api::internal::shared::{
HostPortConfig, RackNetworkConfig, ResolvedVpcRouteSet,
ResolvedVpcRouteState,
ResolvedVpcRouteState, SledIdentifiers,
};
use omicron_common::api::{
internal::nexus::DiskRuntimeState, internal::nexus::InstanceRuntimeState,
Expand Down Expand Up @@ -156,6 +156,9 @@ pub enum Error {

#[error("Metrics error: {0}")]
Metrics(#[from] crate::metrics::Error),

#[error("Expected revision to fit in a u32, but found {0}")]
UnexpectedRevision(i64),
}

impl From<Error> for omicron_common::api::external::Error {
Expand Down Expand Up @@ -1185,6 +1188,31 @@ impl SledAgent {
&self.inner.boot_disk_os_writer
}

/// Return identifiers for this sled.
///
/// This is mostly used to identify timeseries data with the originating
/// sled.
///
/// NOTE: This only returns the identifiers for the _sled_ itself. If you're
/// interested in the switch identifiers, MGS is the current best way to do
/// that, by asking for the local switch's slot, and then that switch's SP
/// state.
pub(crate) async fn sled_identifiers(
&self,
) -> Result<SledIdentifiers, Error> {
let baseboard = self.inner.hardware.baseboard();
Ok(SledIdentifiers {
rack_id: self.inner.start_request.body.rack_id,
sled_id: self.inner.id,
model: baseboard.model().to_string(),
revision: baseboard
.revision()
.try_into()
.map_err(|_| Error::UnexpectedRevision(baseboard.revision()))?,
serial: baseboard.identifier().to_string(),
})
}

/// Return basic information about ourselves: identity and status
///
/// This is basically a GET version of the information we push to Nexus on
Expand Down

0 comments on commit 8316247

Please sign in to comment.