diff --git a/Cargo.lock b/Cargo.lock index ff5bdf53..6badbfc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1196,7 +1196,7 @@ dependencies = [ name = "ddm-api" version = "0.1.0" dependencies = [ - "ddm-types", + "ddm-types-versions", "dropshot", "dropshot-api-manager-types", "mg-common", @@ -1209,12 +1209,20 @@ dependencies = [ [[package]] name = "ddm-types" version = "0.1.0" +dependencies = [ + "ddm-types-versions", +] + +[[package]] +name = "ddm-types-versions" +version = "0.1.0" dependencies = [ "mg-common", "oxnet", "schemars", "serde", "serde_repr", + "uuid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d76b38ca..edfa8ef0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ default-members = [ "ddm-admin-client", "ddm-api", "ddm-types", + "ddm-types/versions", "dropshot-apis", "bfd", "util", @@ -33,6 +34,7 @@ members = [ "ddm-admin-client", "ddm-api", "ddm-types", + "ddm-types/versions", "dropshot-apis", "bfd", "package", @@ -114,6 +116,7 @@ proptest = "1.4" serial_test = "3.2" ddm-api = { path = "ddm-api" } ddm-types = { path = "ddm-types" } +ddm-types-versions = { path = "ddm-types/versions" } gateway-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" } [workspace.dependencies.opte-ioctl] diff --git a/ddm-api/Cargo.toml b/ddm-api/Cargo.toml index 112eb116..2e8bfb6a 100644 --- a/ddm-api/Cargo.toml +++ b/ddm-api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -ddm-types.workspace = true +ddm-types-versions.workspace = true dropshot.workspace = true dropshot-api-manager-types.workspace = true mg-common.workspace = true diff --git a/ddm-api/src/lib.rs b/ddm-api/src/lib.rs index 56b6de28..546797dc 100644 --- a/ddm-api/src/lib.rs +++ b/ddm-api/src/lib.rs @@ -2,9 +2,7 @@ // 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/. -use ddm_types::db::PeerInfo; -use ddm_types::db::TunnelRoute; -use ddm_types::exchange::PathVector; +use ddm_types_versions::latest; use dropshot::HttpError; use dropshot::HttpResponseOk; use dropshot::HttpResponseUpdatedNoContent; @@ -14,13 +12,7 @@ use dropshot::TypedBody; use dropshot_api_manager_types::api_versions; use mg_common::net::TunnelOrigin; use oxnet::Ipv6Net; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::net::Ipv6Addr; -use uuid::Uuid; - -pub type PrefixMap = BTreeMap>; +use std::collections::{HashMap, HashSet}; api_versions!([ // WHEN CHANGING THE API (part 1 of 2): @@ -56,12 +48,12 @@ pub trait DdmAdminApi { #[endpoint { method = GET, path = "/peers" }] async fn get_peers( ctx: RequestContext, - ) -> Result>, HttpError>; + ) -> Result>, HttpError>; #[endpoint { method = DELETE, path = "/peers/{addr}" }] async fn expire_peer( ctx: RequestContext, - params: Path, + params: Path, ) -> Result; #[endpoint { method = GET, path = "/originated" }] @@ -77,12 +69,12 @@ pub trait DdmAdminApi { #[endpoint { method = GET, path = "/prefixes" }] async fn get_prefixes( ctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; #[endpoint { method = GET, path = "/tunnel_endpoints" }] async fn get_tunnel_endpoints( ctx: RequestContext, - ) -> Result>, HttpError>; + ) -> Result>, HttpError>; #[endpoint { method = PUT, path = "/prefix" }] async fn advertise_prefixes( @@ -116,7 +108,7 @@ pub trait DdmAdminApi { #[endpoint { method = POST, path = "/enable-stats" }] async fn enable_stats( ctx: RequestContext, - request: TypedBody, + request: TypedBody, ) -> Result; #[endpoint { method = POST, path = "/disable-stats" }] @@ -124,14 +116,3 @@ pub trait DdmAdminApi { ctx: RequestContext, ) -> Result; } - -#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] -pub struct ExpirePathParams { - pub addr: Ipv6Addr, -} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct EnableStatsRequest { - pub sled_id: Uuid, - pub rack_id: Uuid, -} diff --git a/ddm-types/Cargo.toml b/ddm-types/Cargo.toml index e4a90270..fe34245a 100644 --- a/ddm-types/Cargo.toml +++ b/ddm-types/Cargo.toml @@ -4,8 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] -mg-common.workspace = true -oxnet.workspace = true -schemars.workspace = true -serde.workspace = true -serde_repr.workspace = true +ddm-types-versions.workspace = true diff --git a/ddm-types/src/admin.rs b/ddm-types/src/admin.rs new file mode 100644 index 00000000..5bdb45cd --- /dev/null +++ b/ddm-types/src/admin.rs @@ -0,0 +1,5 @@ +// 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/. + +pub use ddm_types_versions::latest::admin::*; diff --git a/ddm-types/src/db.rs b/ddm-types/src/db.rs index a68625b4..8efdc088 100644 --- a/ddm-types/src/db.rs +++ b/ddm-types/src/db.rs @@ -2,89 +2,4 @@ // 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/. -use std::net::Ipv6Addr; - -use mg_common::net::TunnelOrigin; -use schemars::{JsonSchema, JsonSchema_repr}; -use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; - -#[derive( - Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema, -)] -pub enum PeerStatus { - NoContact, - Active, - Expired, -} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct PeerInfo { - pub status: PeerStatus, - pub addr: Ipv6Addr, - pub host: String, - pub kind: RouterKind, -} - -#[derive( - Debug, - PartialEq, - Eq, - Clone, - Copy, - Serialize_repr, - Deserialize_repr, - JsonSchema_repr, -)] -#[repr(u8)] -pub enum RouterKind { - Server, - Transit, -} - -impl std::fmt::Display for RouterKind { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - match self { - Self::Server => write!(f, "server"), - Self::Transit => write!(f, "transit"), - } - } -} - -impl std::str::FromStr for RouterKind { - type Err = &'static str; - fn from_str(s: &str) -> Result { - match s { - "server" => Ok(Self::Server), - "transit" => Ok(Self::Transit), - _ => Err(r#"Router kind must be "server" or "transit""#), - } - } -} - -#[derive( - Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, -)] -pub struct TunnelRoute { - pub origin: TunnelOrigin, - - // The nexthop is only used to associate the route with a peer allowing us - // to remove the route if the peer expires. It does not influence what goes - // into the underlaying underlay routing platform. Tunnel routes only - // influence the state of the underlying encapsulation service. - pub nexthop: Ipv6Addr, -} - -impl From for TunnelOrigin { - fn from(x: TunnelRoute) -> Self { - Self { - overlay_prefix: x.origin.overlay_prefix, - boundary_addr: x.origin.boundary_addr, - vni: x.origin.vni, - metric: x.origin.metric, - } - } -} +pub use ddm_types_versions::latest::db::*; diff --git a/ddm-types/src/exchange.rs b/ddm-types/src/exchange.rs index d24445db..55d570a6 100644 --- a/ddm-types/src/exchange.rs +++ b/ddm-types/src/exchange.rs @@ -2,50 +2,4 @@ // 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/. -use mg_common::net::Ipv6Prefix; -use oxnet::Ipv6Net; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -#[derive( - Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, -)] -pub struct PathVector { - pub destination: Ipv6Net, - pub path: Vec, -} - -/// THIS TYPE IS FOR DDM PROTOCOL VERSION 2. IT SHALL NEVER CHANGE. THIS TYPE -/// CAN BE REMOVED WHEN DDMV2 CLIENTS AND SERVERS NO LONGER EXIST BUT ITS -/// DEFINITION SHALL NEVER CHANGE. -#[derive( - Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, -)] -pub struct PathVectorV2 { - pub destination: Ipv6Prefix, - pub path: Vec, -} - -impl From for PathVector { - fn from(value: PathVectorV2) -> Self { - PathVector { - destination: Ipv6Net::new_unchecked( - value.destination.addr, - value.destination.len, - ), - path: value.path, - } - } -} - -impl From for PathVectorV2 { - fn from(value: PathVector) -> Self { - PathVectorV2 { - destination: Ipv6Prefix { - addr: value.destination.addr(), - len: value.destination.width(), - }, - path: value.path, - } - } -} +pub use ddm_types_versions::latest::exchange::*; diff --git a/ddm-types/src/lib.rs b/ddm-types/src/lib.rs index ea5d4bb5..decfc1a0 100644 --- a/ddm-types/src/lib.rs +++ b/ddm-types/src/lib.rs @@ -2,5 +2,6 @@ // 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/. +pub mod admin; pub mod db; pub mod exchange; diff --git a/ddm-types/versions/Cargo.toml b/ddm-types/versions/Cargo.toml new file mode 100644 index 00000000..87c7fe6d --- /dev/null +++ b/ddm-types/versions/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ddm-types-versions" +version = "0.1.0" +edition = "2024" + +[dependencies] +mg-common.workspace = true +oxnet.workspace = true +schemars.workspace = true +serde.workspace = true +serde_repr.workspace = true +uuid.workspace = true diff --git a/ddm-types/versions/src/initial/admin.rs b/ddm-types/versions/src/initial/admin.rs new file mode 100644 index 00000000..97746089 --- /dev/null +++ b/ddm-types/versions/src/initial/admin.rs @@ -0,0 +1,25 @@ +// 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/. + +use std::collections::{BTreeMap, HashSet}; +use std::net::Ipv6Addr; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::exchange::PathVector; + +pub type PrefixMap = BTreeMap>; + +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +pub struct ExpirePathParams { + pub addr: Ipv6Addr, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct EnableStatsRequest { + pub sled_id: Uuid, + pub rack_id: Uuid, +} diff --git a/ddm-types/versions/src/initial/db.rs b/ddm-types/versions/src/initial/db.rs new file mode 100644 index 00000000..a68625b4 --- /dev/null +++ b/ddm-types/versions/src/initial/db.rs @@ -0,0 +1,90 @@ +// 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/. + +use std::net::Ipv6Addr; + +use mg_common::net::TunnelOrigin; +use schemars::{JsonSchema, JsonSchema_repr}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +#[derive( + Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema, +)] +pub enum PeerStatus { + NoContact, + Active, + Expired, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct PeerInfo { + pub status: PeerStatus, + pub addr: Ipv6Addr, + pub host: String, + pub kind: RouterKind, +} + +#[derive( + Debug, + PartialEq, + Eq, + Clone, + Copy, + Serialize_repr, + Deserialize_repr, + JsonSchema_repr, +)] +#[repr(u8)] +pub enum RouterKind { + Server, + Transit, +} + +impl std::fmt::Display for RouterKind { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> Result<(), std::fmt::Error> { + match self { + Self::Server => write!(f, "server"), + Self::Transit => write!(f, "transit"), + } + } +} + +impl std::str::FromStr for RouterKind { + type Err = &'static str; + fn from_str(s: &str) -> Result { + match s { + "server" => Ok(Self::Server), + "transit" => Ok(Self::Transit), + _ => Err(r#"Router kind must be "server" or "transit""#), + } + } +} + +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, +)] +pub struct TunnelRoute { + pub origin: TunnelOrigin, + + // The nexthop is only used to associate the route with a peer allowing us + // to remove the route if the peer expires. It does not influence what goes + // into the underlaying underlay routing platform. Tunnel routes only + // influence the state of the underlying encapsulation service. + pub nexthop: Ipv6Addr, +} + +impl From for TunnelOrigin { + fn from(x: TunnelRoute) -> Self { + Self { + overlay_prefix: x.origin.overlay_prefix, + boundary_addr: x.origin.boundary_addr, + vni: x.origin.vni, + metric: x.origin.metric, + } + } +} diff --git a/ddm-types/versions/src/initial/exchange.rs b/ddm-types/versions/src/initial/exchange.rs new file mode 100644 index 00000000..d24445db --- /dev/null +++ b/ddm-types/versions/src/initial/exchange.rs @@ -0,0 +1,51 @@ +// 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/. + +use mg_common::net::Ipv6Prefix; +use oxnet::Ipv6Net; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, +)] +pub struct PathVector { + pub destination: Ipv6Net, + pub path: Vec, +} + +/// THIS TYPE IS FOR DDM PROTOCOL VERSION 2. IT SHALL NEVER CHANGE. THIS TYPE +/// CAN BE REMOVED WHEN DDMV2 CLIENTS AND SERVERS NO LONGER EXIST BUT ITS +/// DEFINITION SHALL NEVER CHANGE. +#[derive( + Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, +)] +pub struct PathVectorV2 { + pub destination: Ipv6Prefix, + pub path: Vec, +} + +impl From for PathVector { + fn from(value: PathVectorV2) -> Self { + PathVector { + destination: Ipv6Net::new_unchecked( + value.destination.addr, + value.destination.len, + ), + path: value.path, + } + } +} + +impl From for PathVectorV2 { + fn from(value: PathVector) -> Self { + PathVectorV2 { + destination: Ipv6Prefix { + addr: value.destination.addr(), + len: value.destination.width(), + }, + path: value.path, + } + } +} diff --git a/ddm-types/versions/src/initial/mod.rs b/ddm-types/versions/src/initial/mod.rs new file mode 100644 index 00000000..1442370a --- /dev/null +++ b/ddm-types/versions/src/initial/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 `INITIAL` of the DDM Admin API. + +pub mod admin; +pub mod db; +pub mod exchange; diff --git a/ddm-types/versions/src/latest.rs b/ddm-types/versions/src/latest.rs new file mode 100644 index 00000000..08057601 --- /dev/null +++ b/ddm-types/versions/src/latest.rs @@ -0,0 +1,23 @@ +// 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/. + +//! Re-exports of the latest versions of types. + +pub mod admin { + pub use crate::v1::admin::EnableStatsRequest; + pub use crate::v1::admin::ExpirePathParams; + pub use crate::v1::admin::PrefixMap; +} + +pub mod db { + pub use crate::v1::db::PeerInfo; + pub use crate::v1::db::PeerStatus; + pub use crate::v1::db::RouterKind; + pub use crate::v1::db::TunnelRoute; +} + +pub mod exchange { + pub use crate::v1::exchange::PathVector; + pub use crate::v1::exchange::PathVectorV2; +} diff --git a/ddm-types/versions/src/lib.rs b/ddm-types/versions/src/lib.rs new file mode 100644 index 00000000..9f8dcdc7 --- /dev/null +++ b/ddm-types/versions/src/lib.rs @@ -0,0 +1,34 @@ +// 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/. + +//! Versioned types for the DDM Admin API. +//! +//! # Adding a new API version +//! +//! When adding a new API version N with added or changed types: +//! +//! 1. Create /mod.rs, where is the lowercase +//! form of the new version's identifier, as defined in the API trait's +//! `api_versions!` macro. +//! +//! 2. Add to the end of this list: +//! +//! ```rust,ignore +//! #[path = "/mod.rs"] +//! pub mod vN; +//! ``` +//! +//! 3. Add your types to the new module, mirroring the module structure from +//! earlier versions. +//! +//! 4. Update `latest.rs` with new and updated types from the new version. +//! +//! For more information, see the [detailed guide] and [RFD 619]. +//! +//! [detailed guide]: https://github.com/oxidecomputer/dropshot-api-manager/blob/main/guides/new-version.md +//! [RFD 619]: https://rfd.shared.oxide.computer/rfd/619 + +pub mod latest; +#[path = "initial/mod.rs"] +pub mod v1; diff --git a/ddm/src/admin.rs b/ddm/src/admin.rs index 6ec98ab7..6d49a368 100644 --- a/ddm/src/admin.rs +++ b/ddm/src/admin.rs @@ -4,7 +4,9 @@ use crate::db::Db; use crate::sm::{AdminEvent, Event, PrefixSet, SmContext}; -use ddm_api::*; +use ddm_api::DdmAdminApi; +use ddm_api::ddm_admin_api_mod; +use ddm_types::admin::{EnableStatsRequest, ExpirePathParams, PrefixMap}; use ddm_types::db::{PeerInfo, TunnelRoute}; use ddm_types::exchange::PathVector; use dropshot::ApiDescription;