Skip to content

Commit 426970a

Browse files
authored
[ereport] intial types and dropshot API crate (#7833)
This branch adds an initial version of the HTTP API for the control plane's ereport ingestion mechanism, as described in [RFD 520]. In particularr, this branch adds the following: - an `ereport-types` crate, containing the shared type definitions for ereport data - an `ereporter-api` crate defining the Dropshot HTTP API that will be implemented by MGS and sled-agents in order to expose ereports to Nexus - the generated `ereporter-client` crate for that API, which Nexus will use to ingest ereports from reporters. Actually implementing the API in MGS and sled-agent, and implementing the Nexus RPW for collecting ereports using this interface, is left as an exercise for ~~the reader~~ future Eliza. I also intend to add a simulated version of this API to the simulated sled-agent and MGS, to to use for testing the ingestion bits in Nexus. The ereporter HTTP API has changed a bit from the strawman version proposed in RFD 520, as the request to collect new ereports and the request to flush already-committed ereports are now contained in the same HTTP request, rather than requiring separate endpoints for these operations. This is due to @cbiffle: in [his proposal][545-4.4.1] for the MGS-to-SP hop in the ereport pipeline in [RFD 545], Cliff has combined these into one message, which I thought seemed nice to do here as well. Closes #6835 (obsoleted by this change) [RFD 520]: https://rfd.shared.oxide.computer/rfd/520 [RFD 545]: https://rfd.shared.oxide.computer/rfd/0545 [545-4.4.1]: https://rfd.shared.oxide.computer/rfd/0545#_mgs_interface
1 parent 823317d commit 426970a

File tree

18 files changed

+712
-0
lines changed

18 files changed

+712
-0
lines changed

Cargo.lock

+51
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = [
1414
"clients/ddm-admin-client",
1515
"clients/dns-service-client",
1616
"clients/dpd-client",
17+
"clients/ereport-client",
1718
"clients/gateway-client",
1819
"clients/installinator-client",
1920
"clients/nexus-client",
@@ -49,6 +50,8 @@ members = [
4950
"dns-server",
5051
"dns-server-api",
5152
"end-to-end-tests",
53+
"ereport/api",
54+
"ereport/types",
5255
"gateway",
5356
"gateway-api",
5457
"gateway-cli",
@@ -150,6 +153,7 @@ default-members = [
150153
"clients/ddm-admin-client",
151154
"clients/dns-service-client",
152155
"clients/dpd-client",
156+
"clients/ereport-client",
153157
"clients/gateway-client",
154158
"clients/installinator-client",
155159
"clients/nexus-client",
@@ -187,6 +191,8 @@ default-members = [
187191
"dns-server",
188192
"dns-server-api",
189193
"end-to-end-tests",
194+
"ereport/api",
195+
"ereport/types",
190196
"gateway",
191197
"gateway-api",
192198
"gateway-cli",
@@ -385,6 +391,9 @@ dpd-client = { path = "clients/dpd-client" }
385391
dropshot = { version = "0.16.0", features = [ "usdt-probes" ] }
386392
dyn-clone = "1.0.19"
387393
either = "1.14.0"
394+
ereport-api = { path = "ereport/api" }
395+
ereport-client = { path = "clients/ereport-client" }
396+
ereport-types = { path = "ereport/types" }
388397
expectorate = "1.1.0"
389398
fatfs = "0.3.6"
390399
filetime = "0.2.25"

clients/ereport-client/Cargo.toml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "ereport-client"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lints]
7+
workspace = true
8+
9+
[dependencies]
10+
chrono.workspace = true
11+
expectorate.workspace = true
12+
ereport-types.workspace = true
13+
http.workspace = true
14+
progenitor.workspace = true
15+
reqwest = { workspace = true, features = ["json", "rustls-tls", "stream"] }
16+
schemars.workspace = true
17+
serde.workspace = true
18+
serde_json.workspace = true
19+
slog.workspace = true
20+
omicron-uuid-kinds.workspace = true
21+
omicron-workspace-hack.workspace = true
22+
uuid.workspace = true

clients/ereport-client/src/lib.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
progenitor::generate_api!(
6+
spec = "../../openapi/ereport/ereport-latest.json",
7+
inner_type = slog::Logger,
8+
derives = [schemars::JsonSchema, Clone, Eq, PartialEq],
9+
pre_hook = (|log: &slog::Logger, request: &reqwest::Request| {
10+
slog::debug!(log, "client request";
11+
"method" => %request.method(),
12+
"uri" => %request.url(),
13+
"body" => ?&request.body(),
14+
);
15+
}),
16+
post_hook = (|log: &slog::Logger, result: &Result<_, _>| {
17+
slog::debug!(log, "client response"; "result" => ?result);
18+
}),
19+
replace = {
20+
Ena = ereport_types::Ena,
21+
TypedUuidForEreporterGenerationKind = omicron_uuid_kinds::EreporterGenerationUuid,
22+
}
23+
);

dev-tools/ls-apis/api-manifest.toml

+12
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,18 @@ and exists as a client library within omicron. This is because the Dendrite \
266266
repo is not currently open source.
267267
"""
268268

269+
[[apis]]
270+
client_package_name = "ereport-client"
271+
label = "ereport"
272+
server_package_name = "ereport-api"
273+
versioned_how = "server"
274+
notes = """
275+
Implemented by sled-agents and by MGS, and consumed by Nexus to collect \
276+
ereports.
277+
278+
The sled-agent and MGS APIs are server-versioned, so this can be as well.
279+
"""
280+
269281
[[apis]]
270282
client_package_name = "lldpd-client"
271283
label = "LLDP daemon"

dev-tools/ls-apis/tests/api_dependencies.out

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ Dendrite DPD (client: dpd-client)
4242

4343
Downstairs Controller (debugging only) (client: dsc-client)
4444

45+
ereport (client: ereport-client)
46+
consumed by: omicron-nexus (omicron/nexus) via 1 path
47+
4548
Management Gateway Service (client: gateway-client)
4649
consumed by: dpd (dendrite/dpd) via 1 path
4750
consumed by: omicron-nexus (omicron/nexus) via 3 paths

dev-tools/openapi-manager/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ cockroach-admin-api.workspace = true
1818
debug-ignore.workspace = true
1919
dns-server-api.workspace = true
2020
dropshot.workspace = true
21+
ereport-api.workspace = true
2122
hex.workspace = true
2223
fs-err.workspace = true
2324
gateway-api.workspace = true

dev-tools/openapi-manager/src/omicron.rs

+12
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ pub fn all_apis() -> Vec<ManagedApiConfig> {
8585
ident: "dns-server",
8686
extra_validation: None,
8787
},
88+
ManagedApiConfig {
89+
title: "Ereport Reporter API",
90+
versions: Versions::new_versioned(
91+
ereport_api::supported_versions()
92+
),
93+
description: "API for ereport producers",
94+
boundary: ApiBoundary::Internal,
95+
api_description:
96+
ereport_api::ereport_api_mod::stub_api_description,
97+
ident: "ereport",
98+
extra_validation: None,
99+
},
88100
ManagedApiConfig {
89101
title: "Installinator API",
90102
versions: Versions::new_lockstep(semver::Version::new(0,0,1)),

ereport/api/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "ereport-api"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
ereport-types.workspace = true
8+
dropshot.workspace = true
9+
openapi-manager-types.workspace = true
10+
schemars.workspace = true
11+
semver.workspace = true
12+
serde.workspace = true
13+
serde_json.workspace = true
14+
thiserror.workspace = true
15+
uuid.workspace = true
16+
omicron-workspace-hack.workspace = true
17+
18+
[lints]
19+
workspace = true

ereport/api/src/lib.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
//! HTTP API for ereport producers.
6+
7+
use dropshot::HttpError;
8+
use dropshot::HttpResponseOk;
9+
use dropshot::Path;
10+
use dropshot::Query;
11+
use dropshot::RequestContext;
12+
use dropshot::ResultsPage;
13+
pub use ereport_types::Ena;
14+
pub use ereport_types::Ereport;
15+
pub use ereport_types::EreporterGenerationUuid;
16+
pub use ereport_types::Reporter;
17+
use openapi_manager_types::{
18+
SupportedVersion, SupportedVersions, api_versions,
19+
};
20+
use schemars::JsonSchema;
21+
use serde::Deserialize;
22+
use serde::Serialize;
23+
use std::num::NonZeroU32;
24+
use uuid::Uuid;
25+
26+
api_versions!([
27+
// WHEN CHANGING THE API (part 1 of 2):
28+
//
29+
// +- Pick a new semver and define it in the list below. The list MUST
30+
// | remain sorted, which generally means that your version should go at
31+
// | the very top.
32+
// |
33+
// | Duplicate this line, uncomment the *second* copy, update that copy for
34+
// | your new API version, and leave the first copy commented out as an
35+
// | example for the next person.
36+
// v
37+
// (next_int, IDENT),
38+
(1, INITIAL),
39+
]);
40+
41+
// WHEN CHANGING THE API (part 2 of 2):
42+
//
43+
// The call to `api_versions!` above defines constants of type
44+
// `semver::Version` that you can use in your Dropshot API definition to specify
45+
// the version when a particular endpoint was added or removed. For example, if
46+
// you used:
47+
//
48+
// (2, ADD_FOOBAR)
49+
//
50+
// Then you could use `VERSION_ADD_FOOBAR` as the version in which endpoints
51+
// were added or removed.
52+
53+
/// API for ereport producers.
54+
#[dropshot::api_description]
55+
pub trait EreportApi {
56+
type Context;
57+
58+
/// Collect a tranche of ereports from this reporter.
59+
#[endpoint {
60+
method = POST,
61+
path = "/ereports/{reporter_id}",
62+
}]
63+
async fn ereports_collect(
64+
rqctx: RequestContext<Self::Context>,
65+
path: Path<ReporterPath>,
66+
query: Query<EreportQuery>,
67+
) -> Result<HttpResponseOk<Ereports>, HttpError>;
68+
}
69+
70+
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
71+
pub struct ReporterPath {
72+
/// The UUID of the reporter from which to collect ereports.
73+
pub reporter_id: Uuid,
74+
}
75+
76+
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
77+
pub struct EreportQuery {
78+
/// The generation (restart nonce) of the reporter at which all other query
79+
/// parameters are valid.
80+
///
81+
/// If this value does not match the reporter's current generation, the
82+
/// reporter's response will include the current generation, and will start
83+
/// at the earliest known ENA, rather than the provided `last_seen` ENA.`
84+
pub generation: EreporterGenerationUuid,
85+
86+
/// If present, the reporter should not include ENAs earlier than this one
87+
/// in its response, provided that the query's requested generation matches
88+
/// the current generation.
89+
pub start_at: Option<Ena>,
90+
91+
/// The ENA of the last ereport committed to persistent storage from the
92+
/// requested reporter generation.
93+
///
94+
/// If the generation parameter matches the reporter's current generation,
95+
/// it is permitted to discard any ereports with ENAs up to and including
96+
/// this value. If the generation has changed from the provided generation,
97+
/// the reporter will not discard data.
98+
pub committed: Option<Ena>,
99+
100+
/// Maximum number of ereports to return in this tranche.
101+
pub limit: NonZeroU32,
102+
}
103+
104+
/// A tranche of ereports received from a reporter.
105+
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
106+
pub struct Ereports {
107+
/// The reporter's current generation ID.
108+
///
109+
/// If this is not equal to the current known generation, then the reporter
110+
/// has restarted.
111+
pub generation: EreporterGenerationUuid,
112+
/// The ereports in this tranche, and the ENA of the next page of ereports
113+
/// (if one exists).)
114+
#[serde(flatten)]
115+
pub reports: ResultsPage<Ereport>,
116+
}

ereport/types/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "ereport-types"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
omicron-uuid-kinds.workspace = true
8+
schemars.workspace = true
9+
serde.workspace = true
10+
serde_json.workspace = true
11+
uuid.workspace = true
12+
omicron-workspace-hack.workspace = true
13+
14+
[lints]
15+
workspace = true

0 commit comments

Comments
 (0)