diff --git a/Cargo.lock b/Cargo.lock index 407837ea..ee63d90e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3391,6 +3391,7 @@ dependencies = [ "chrono", "clap", "dropshot", + "gateway-client", "hostname 0.3.1", "http", "mg-api", diff --git a/Cargo.toml b/Cargo.toml index 4cfbac84..b2618729 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -114,6 +114,7 @@ proptest = "1.4" serial_test = "3.2" ddm-api = { path = "ddm-api" } ddm-types = { path = "ddm-types" } +gateway-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" } [workspace.dependencies.opte-ioctl] git = "https://github.com/oxidecomputer/opte" diff --git a/mg-api/src/lib.rs b/mg-api/src/lib.rs index 513755ac..69721dab 100644 --- a/mg-api/src/lib.rs +++ b/mg-api/src/lib.rs @@ -40,6 +40,7 @@ api_versions!([ // | example for the next person. // v // (next_int, IDENT), + (3, SWITCH_IDENTIFIERS), (2, IPV6_BASIC), (1, INITIAL), ]); @@ -371,6 +372,20 @@ pub trait MgAdminApi { async fn static_list_v6_routes( ctx: RequestContext, ) -> Result, HttpError>; + + #[endpoint {method = GET, path = "/switch/identifiers", versions = VERSION_SWITCH_IDENTIFIERS.. }] + async fn switch_identifiers( + ctx: RequestContext, + ) -> Result, HttpError>; +} + +/// Identifiers for a switch. +#[derive(Clone, Debug, JsonSchema, Serialize)] +pub struct SwitchIdentifiers { + /// The slot number of the switch being managed. + /// + /// MGS uses u16 for this internally. + pub slot: Option, } #[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)] diff --git a/mgd/Cargo.toml b/mgd/Cargo.toml index dfdc61e6..ddda43e3 100644 --- a/mgd/Cargo.toml +++ b/mgd/Cargo.toml @@ -26,6 +26,7 @@ omicron-common.workspace = true hostname.workspace = true uuid.workspace = true smf.workspace = true +gateway-client.workspace = true [dev-dependencies] tempfile = "3" diff --git a/mgd/src/admin.rs b/mgd/src/admin.rs index 2526e89e..63a0cd0f 100644 --- a/mgd/src/admin.rs +++ b/mgd/src/admin.rs @@ -438,6 +438,12 @@ impl MgAdminApi for MgAdminApiImpl { ) -> Result, HttpError> { static_admin::static_list_v6_routes(ctx).await } + + async fn switch_identifiers( + ctx: RequestContext, + ) -> Result, HttpError> { + static_admin::switch_identifiers(ctx).await + } } pub fn api_description() -> ApiDescription> { diff --git a/mgd/src/main.rs b/mgd/src/main.rs index 9e105007..96fac887 100644 --- a/mgd/src/main.rs +++ b/mgd/src/main.rs @@ -17,7 +17,7 @@ use rdb::{BfdPeerConfig, BgpNeighborInfo, BgpRouterInfo}; use signal::handle_signals; use slog::Logger; use std::collections::{BTreeMap, BTreeSet}; -use std::net::{IpAddr, Ipv6Addr}; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::sync::{Arc, Mutex}; use std::thread::Builder; use uuid::Uuid; @@ -88,6 +88,10 @@ struct RunArgs { /// Id of the sled this router is running on. #[arg(long)] sled_uuid: Option, + + /// SocketAddr the MGS service is listening on. + #[arg(long, default_value = "[::1]:12225")] + mgs_addr: SocketAddr, } fn main() { @@ -123,6 +127,12 @@ async fn run(args: RunArgs) { oximeter_port: args.oximeter_port, }); + detect_switch_slot( + context.clone(), + args.mgs_addr, + tokio::runtime::Handle::current(), + ); + if let Err(e) = sig_tx.send(context.clone()).await { dlog!(log, error, "error sending handler context to signal handler: {e}"; "params" => format!("tep {tep_ula}, dir {}, oximeter_port {}", @@ -202,6 +212,44 @@ async fn run(args: RunArgs) { j.await.expect("API server quit unexpectedly"); } +fn detect_switch_slot( + ctx: Arc, + mgs_socket_addr: SocketAddr, + rt: tokio::runtime::Handle, +) { + let url = format!("http://{mgs_socket_addr}"); + let client_log = ctx.log.new(slog::o!("unit" => "gateway-client")); + let task = async move || { + let client = gateway_client::Client::new(&url, client_log); + let ctx = ctx.clone(); + + loop { + // check in with gateway + let gateway_client::types::SpIdentifier { slot, .. } = match client + .sp_local_switch_id() + .await + { + Ok(v) => *v, + Err(e) => { + slog::error!(ctx.log, "failed to resolve switch slot"; "error" => %e); + tokio::time::sleep(tokio::time::Duration::from_secs(10)) + .await; + continue; + } + }; + + slog::info!(ctx.log, "we are in switch slot {slot}"); + + // update db + let mut db = ctx.db.clone(); + db.set_slot(Some(slot)); + break; + } + }; + + rt.spawn(task()); +} + fn init_bgp(args: &RunArgs, log: &Logger) -> BgpContext { let addr_to_session = Arc::new(Mutex::new(BTreeMap::new())); if !args.no_bgp_dispatcher { diff --git a/mgd/src/static_admin.rs b/mgd/src/static_admin.rs index 2c3bcd09..098ac726 100644 --- a/mgd/src/static_admin.rs +++ b/mgd/src/static_admin.rs @@ -134,3 +134,10 @@ pub async fn static_list_v6_routes( Ok(HttpResponseOk(static_rib)) } + +pub(crate) async fn switch_identifiers( + ctx: RequestContext>, +) -> Result, HttpError> { + let slot = ctx.context().db.slot(); + Ok(HttpResponseOk(mg_api::SwitchIdentifiers { slot })) +} diff --git a/openapi/mg-admin/mg-admin-3.0.0-08e2f2.json b/openapi/mg-admin/mg-admin-3.0.0-08e2f2.json new file mode 100644 index 00000000..4527a3f6 --- /dev/null +++ b/openapi/mg-admin/mg-admin-3.0.0-08e2f2.json @@ -0,0 +1,3957 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Maghemite Admin", + "contact": { + "url": "https://oxide.computer", + "email": "api@oxide.computer" + }, + "version": "3.0.0" + }, + "paths": { + "/bfd/peers": { + "get": { + "summary": "Get all the peers and their associated BFD state. Peers are identified by IP", + "description": "address.", + "operationId": "get_bfd_peers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Array_of_BfdPeerInfo", + "type": "array", + "items": { + "$ref": "#/components/schemas/BfdPeerInfo" + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "summary": "Add a new peer to the daemon. A session for the specified peer will start", + "description": "immediately.", + "operationId": "add_bfd_peer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BfdPeerConfig" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bfd/peers/{addr}": { + "delete": { + "summary": "Remove the specified peer from the daemon. The associated peer session will", + "description": "be stopped immediately.", + "operationId": "remove_bfd_peer", + "parameters": [ + { + "in": "path", + "name": "addr", + "description": "Address of the peer to remove.", + "required": true, + "schema": { + "type": "string", + "format": "ip" + } + } + ], + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/clear/neighbor": { + "post": { + "operationId": "clear_neighbor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NeighborResetRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/checker": { + "get": { + "operationId": "read_checker", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckerSource" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_checker", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckerSource" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_checker", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckerSource" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_checker", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/neighbor": { + "get": { + "operationId": "read_neighbor", + "parameters": [ + { + "in": "query", + "name": "addr", + "required": true, + "schema": { + "type": "string", + "format": "ip" + } + }, + { + "in": "query", + "name": "asn", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Neighbor" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_neighbor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Neighbor" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_neighbor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Neighbor" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_neighbor", + "parameters": [ + { + "in": "query", + "name": "addr", + "required": true, + "schema": { + "type": "string", + "format": "ip" + } + }, + { + "in": "query", + "name": "asn", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/neighbors": { + "get": { + "operationId": "read_neighbors", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Array_of_Neighbor", + "type": "array", + "items": { + "$ref": "#/components/schemas/Neighbor" + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/origin4": { + "get": { + "operationId": "read_origin4", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin4" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_origin4", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin4" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_origin4", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin4" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_origin4", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/origin6": { + "get": { + "operationId": "read_origin6", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin6" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_origin6", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin6" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_origin6", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Origin6" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_origin6", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/router": { + "get": { + "operationId": "read_router", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Router" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_router", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Router" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_router", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Router" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_router", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/routers": { + "get": { + "operationId": "read_routers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Array_of_Router", + "type": "array", + "items": { + "$ref": "#/components/schemas/Router" + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/config/shaper": { + "get": { + "operationId": "read_shaper", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShaperSource" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "create_shaper", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShaperSource" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_shaper", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShaperSource" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "delete_shaper", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/history/fsm": { + "get": { + "operationId": "fsm_history", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FsmHistoryRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FsmHistoryResponse" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/history/message": { + "get": { + "operationId": "message_history_v2", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageHistoryRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageHistoryResponse" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/omicron/apply": { + "post": { + "operationId": "bgp_apply", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplyRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/status/exported": { + "get": { + "operationId": "get_exported", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsnSelector" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Map_of_Array_of_Prefix", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix" + } + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/bgp/status/neighbors": { + "get": { + "operationId": "get_neighbors_v2", + "parameters": [ + { + "in": "query", + "name": "asn", + "description": "ASN of the router to get imported prefixes from.", + "required": true, + "schema": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Map_of_PeerInfo", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/PeerInfo" + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/rib/config/bestpath/fanout": { + "get": { + "operationId": "read_rib_bestpath_fanout", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BestpathFanoutResponse" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "post": { + "operationId": "update_rib_bestpath_fanout", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BestpathFanoutRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/rib/status/imported": { + "get": { + "operationId": "get_rib_imported", + "parameters": [ + { + "in": "query", + "name": "address_family", + "description": "Filter by address family (None means all families)", + "schema": { + "$ref": "#/components/schemas/AddressFamily" + } + }, + { + "in": "query", + "name": "protocol", + "description": "Filter by protocol (optional)", + "schema": { + "$ref": "#/components/schemas/ProtocolFilter" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Rib" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/rib/status/selected": { + "get": { + "operationId": "get_rib_selected", + "parameters": [ + { + "in": "query", + "name": "address_family", + "description": "Filter by address family (None means all families)", + "schema": { + "$ref": "#/components/schemas/AddressFamily" + } + }, + { + "in": "query", + "name": "protocol", + "description": "Filter by protocol (optional)", + "schema": { + "$ref": "#/components/schemas/ProtocolFilter" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Rib" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/static/route4": { + "get": { + "operationId": "static_list_v4_routes", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Map_of_Set_of_Path", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Path" + }, + "uniqueItems": true + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "static_add_v4_route", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddStaticRoute4Request" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "static_remove_v4_route", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteStaticRoute4Request" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/static/route6": { + "get": { + "operationId": "static_list_v6_routes", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Map_of_Set_of_Path", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Path" + }, + "uniqueItems": true + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "operationId": "static_add_v6_route", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddStaticRoute6Request" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "static_remove_v6_route", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteStaticRoute6Request" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/switch/identifiers": { + "get": { + "operationId": "switch_identifiers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SwitchIdentifiers" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + } + }, + "components": { + "schemas": { + "AddPathElement": { + "description": "The add path element comes as a BGP capability extension as described in RFC 7911.", + "type": "object", + "properties": { + "afi": { + "description": "Address family identifier. ", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "safi": { + "description": "Subsequent address family identifier. There are a large pile of these ", + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "send_receive": { + "description": "This field indicates whether the sender is (a) able to receive multiple paths from its peer (value 1), (b) able to send multiple paths to its peer (value 2), or (c) both (value 3) for the .", + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "afi", + "safi", + "send_receive" + ] + }, + "AddStaticRoute4Request": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/components/schemas/StaticRoute4List" + } + }, + "required": [ + "routes" + ] + }, + "AddStaticRoute6Request": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/components/schemas/StaticRoute6List" + } + }, + "required": [ + "routes" + ] + }, + "ApplyRequest": { + "description": "Apply changes to an ASN.", + "type": "object", + "properties": { + "asn": { + "description": "ASN to apply changes to.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "checker": { + "nullable": true, + "description": "Checker rhai code to apply to ingress open and update messages.", + "allOf": [ + { + "$ref": "#/components/schemas/CheckerSource" + } + ] + }, + "originate": { + "description": "Complete set of prefixes to originate. Any active prefixes not in this list will be removed. All prefixes in this list are ensured to be in the originating set.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix4" + } + }, + "peers": { + "description": "Lists of peers indexed by peer group. Set's within a peer group key are a total set. For example, the value\n\n```text {\"foo\": [a, b, d]} ``` Means that the peer group \"foo\" only contains the peers `a`, `b` and `d`. If there is a peer `c` currently in the peer group \"foo\", it will be removed.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BgpPeerConfig" + } + } + }, + "shaper": { + "nullable": true, + "description": "Checker rhai code to apply to egress open and update messages.", + "allOf": [ + { + "$ref": "#/components/schemas/ShaperSource" + } + ] + } + }, + "required": [ + "asn", + "originate", + "peers" + ] + }, + "As4PathSegment": { + "type": "object", + "properties": { + "typ": { + "$ref": "#/components/schemas/AsPathType" + }, + "value": { + "type": "array", + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + }, + "required": [ + "typ", + "value" + ] + }, + "AsPathType": { + "description": "Enumeration describes possible AS path types", + "oneOf": [ + { + "description": "The path is to be interpreted as a set", + "type": "string", + "enum": [ + "as_set" + ] + }, + { + "description": "The path is to be interpreted as a sequence", + "type": "string", + "enum": [ + "as_sequence" + ] + } + ] + }, + "AsnSelector": { + "type": "object", + "properties": { + "asn": { + "description": "ASN of the router to get imported prefixes from.", + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "asn" + ] + }, + "BestpathFanoutRequest": { + "type": "object", + "properties": { + "fanout": { + "description": "Maximum number of equal-cost paths for ECMP forwarding", + "type": "integer", + "format": "uint8", + "minimum": 1 + } + }, + "required": [ + "fanout" + ] + }, + "BestpathFanoutResponse": { + "type": "object", + "properties": { + "fanout": { + "description": "Current maximum number of equal-cost paths for ECMP forwarding", + "type": "integer", + "format": "uint8", + "minimum": 1 + } + }, + "required": [ + "fanout" + ] + }, + "BfdPeerConfig": { + "type": "object", + "properties": { + "detection_threshold": { + "description": "Detection threshold for connectivity as a multipler to required_rx", + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "listen": { + "description": "Address to listen on for control messages from the peer.", + "type": "string", + "format": "ip" + }, + "mode": { + "description": "Mode is single-hop (RFC 5881) or multi-hop (RFC 5883).", + "allOf": [ + { + "$ref": "#/components/schemas/SessionMode" + } + ] + }, + "peer": { + "description": "Address of the peer to add.", + "type": "string", + "format": "ip" + }, + "required_rx": { + "description": "Acceptable time between control messages in microseconds.", + "type": "integer", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "detection_threshold", + "listen", + "mode", + "peer", + "required_rx" + ] + }, + "BfdPeerInfo": { + "type": "object", + "properties": { + "config": { + "$ref": "#/components/schemas/BfdPeerConfig" + }, + "state": { + "$ref": "#/components/schemas/BfdPeerState" + } + }, + "required": [ + "config", + "state" + ] + }, + "BfdPeerState": { + "description": "The possible peer states. See the `State` trait implementations `Down`, `Init`, and `Up` for detailed semantics. Data representation is u8 as this enum is used as a part of the BFD wire protocol.", + "oneOf": [ + { + "description": "A stable down state. Non-responsive to incoming messages.", + "type": "string", + "enum": [ + "AdminDown" + ] + }, + { + "description": "The initial state.", + "type": "string", + "enum": [ + "Down" + ] + }, + { + "description": "The peer has detected a remote peer in the down state.", + "type": "string", + "enum": [ + "Init" + ] + }, + { + "description": "The peer has detected a remote peer in the up or init state while in the init state.", + "type": "string", + "enum": [ + "Up" + ] + } + ] + }, + "BgpPathProperties": { + "type": "object", + "properties": { + "as_path": { + "type": "array", + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "id": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "local_pref": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "med": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "origin_as": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "peer": { + "type": "string", + "format": "ip" + }, + "stale": { + "nullable": true, + "type": "string", + "format": "date-time" + } + }, + "required": [ + "as_path", + "id", + "origin_as", + "peer" + ] + }, + "BgpPeerConfig": { + "type": "object", + "properties": { + "allow_export": { + "$ref": "#/components/schemas/ImportExportPolicy" + }, + "allow_import": { + "$ref": "#/components/schemas/ImportExportPolicy" + }, + "communities": { + "type": "array", + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "connect_retry": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "delay_open": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "enforce_first_as": { + "type": "boolean" + }, + "hold_time": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "host": { + "type": "string" + }, + "idle_hold_time": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "keepalive": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "local_pref": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "md5_auth_key": { + "nullable": true, + "type": "string" + }, + "min_ttl": { + "nullable": true, + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "multi_exit_discriminator": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "name": { + "type": "string" + }, + "passive": { + "type": "boolean" + }, + "remote_asn": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "resolution": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "allow_export", + "allow_import", + "communities", + "connect_retry", + "delay_open", + "enforce_first_as", + "hold_time", + "host", + "idle_hold_time", + "keepalive", + "name", + "passive", + "resolution" + ] + }, + "Capability": { + "description": "Optional capabilities supported by a BGP implementation.", + "oneOf": [ + { + "description": "Multiprotocol extensions as defined in RFC 2858", + "type": "object", + "properties": { + "multiprotocol_extensions": { + "type": "object", + "properties": { + "afi": { + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "safi": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "afi", + "safi" + ] + } + }, + "required": [ + "multiprotocol_extensions" + ], + "additionalProperties": false + }, + { + "description": "Route refresh capability as defined in RFC 2918.", + "type": "object", + "properties": { + "route_refresh": { + "type": "object" + } + }, + "required": [ + "route_refresh" + ], + "additionalProperties": false + }, + { + "description": "Outbound filtering capability as defined in RFC 5291. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "outbound_route_filtering": { + "type": "object" + } + }, + "required": [ + "outbound_route_filtering" + ], + "additionalProperties": false + }, + { + "description": "Multiple routes to destination capability as defined in RFC 8277 (deprecated). Note this capability is not yet implemented.", + "type": "object", + "properties": { + "multiple_routes_to_destination": { + "type": "object" + } + }, + "required": [ + "multiple_routes_to_destination" + ], + "additionalProperties": false + }, + { + "description": "Multiple nexthop encoding capability as defined in RFC 8950. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "extended_next_hop_encoding": { + "type": "object" + } + }, + "required": [ + "extended_next_hop_encoding" + ], + "additionalProperties": false + }, + { + "description": "Extended message capability as defined in RFC 8654. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "b_g_p_extended_message": { + "type": "object" + } + }, + "required": [ + "b_g_p_extended_message" + ], + "additionalProperties": false + }, + { + "description": "BGPSec as defined in RFC 8205. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "bgp_sec": { + "type": "object" + } + }, + "required": [ + "bgp_sec" + ], + "additionalProperties": false + }, + { + "description": "Multiple label support as defined in RFC 8277. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "multiple_labels": { + "type": "object" + } + }, + "required": [ + "multiple_labels" + ], + "additionalProperties": false + }, + { + "description": "BGP role capability as defined in RFC 9234. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "bgp_role": { + "type": "object" + } + }, + "required": [ + "bgp_role" + ], + "additionalProperties": false + }, + { + "description": "Graceful restart as defined in RFC 4724. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "graceful_restart": { + "type": "object" + } + }, + "required": [ + "graceful_restart" + ], + "additionalProperties": false + }, + { + "description": "Four octet AS numbers as defined in RFC 6793.", + "type": "object", + "properties": { + "four_octet_as": { + "type": "object", + "properties": { + "asn": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "asn" + ] + } + }, + "required": [ + "four_octet_as" + ], + "additionalProperties": false + }, + { + "description": "Dynamic capabilities as defined in draft-ietf-idr-dynamic-cap. Note this capability is not yet implemented.", + "type": "object", + "properties": { + "dynamic_capability": { + "type": "object" + } + }, + "required": [ + "dynamic_capability" + ], + "additionalProperties": false + }, + { + "description": "Multi session support as defined in draft-ietf-idr-bgp-multisession. Note this capability is not yet supported.", + "type": "object", + "properties": { + "multisession_bgp": { + "type": "object" + } + }, + "required": [ + "multisession_bgp" + ], + "additionalProperties": false + }, + { + "description": "Add path capability as defined in RFC 7911.", + "type": "object", + "properties": { + "add_path": { + "type": "object", + "properties": { + "elements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AddPathElement" + }, + "uniqueItems": true + } + }, + "required": [ + "elements" + ] + } + }, + "required": [ + "add_path" + ], + "additionalProperties": false + }, + { + "description": "Enhanced route refresh as defined in RFC 7313. Note this capability is not yet supported.", + "type": "object", + "properties": { + "enhanced_route_refresh": { + "type": "object" + } + }, + "required": [ + "enhanced_route_refresh" + ], + "additionalProperties": false + }, + { + "description": "Long-lived graceful restart as defined in draft-uttaro-idr-bgp-persistence. Note this capability is not yet supported.", + "type": "object", + "properties": { + "long_lived_graceful_restart": { + "type": "object" + } + }, + "required": [ + "long_lived_graceful_restart" + ], + "additionalProperties": false + }, + { + "description": "Routing policy distribution as defined indraft-ietf-idr-rpd-04. Note this capability is not yet supported.", + "type": "object", + "properties": { + "routing_policy_distribution": { + "type": "object" + } + }, + "required": [ + "routing_policy_distribution" + ], + "additionalProperties": false + }, + { + "description": "Fully qualified domain names as defined intdraft-walton-bgp-hostname-capability. Note this capability is not yet supported.", + "type": "object", + "properties": { + "fqdn": { + "type": "object" + } + }, + "required": [ + "fqdn" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard route refresh as defined in RFC 8810 (deprecated). Note this capability is not yet supported.", + "type": "object", + "properties": { + "prestandard_route_refresh": { + "type": "object" + } + }, + "required": [ + "prestandard_route_refresh" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard prefix-based outbound route filtering as defined in RFC 8810 (deprecated). Note this is not yet implemented.", + "type": "object", + "properties": { + "prestandard_orf_and_pd": { + "type": "object" + } + }, + "required": [ + "prestandard_orf_and_pd" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard outbound route filtering as defined in RFC 8810 (deprecated). Note this is not yet implemented.", + "type": "object", + "properties": { + "prestandard_outbound_route_filtering": { + "type": "object" + } + }, + "required": [ + "prestandard_outbound_route_filtering" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard multisession as defined in RFC 8810 (deprecated). Note this is not yet implemented.", + "type": "object", + "properties": { + "prestandard_multisession": { + "type": "object" + } + }, + "required": [ + "prestandard_multisession" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard fully qualified domain names as defined in RFC 8810 (deprecated). Note this is not yet implemented.", + "type": "object", + "properties": { + "prestandard_fqdn": { + "type": "object" + } + }, + "required": [ + "prestandard_fqdn" + ], + "additionalProperties": false + }, + { + "description": "Pre-standard operational messages as defined in RFC 8810 (deprecated). Note this is not yet implemented.", + "type": "object", + "properties": { + "prestandard_operational_message": { + "type": "object" + } + }, + "required": [ + "prestandard_operational_message" + ], + "additionalProperties": false + }, + { + "description": "Experimental capability as defined in RFC 8810.", + "type": "object", + "properties": { + "experimental": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "code" + ] + } + }, + "required": [ + "experimental" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "unassigned": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "code" + ] + } + }, + "required": [ + "unassigned" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "reserved": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "code" + ] + } + }, + "required": [ + "reserved" + ], + "additionalProperties": false + } + ] + }, + "CeaseErrorSubcode": { + "description": "Cease error subcode types from RFC 4486", + "type": "string", + "enum": [ + "unspecific", + "maximum_numberof_prefixes_reached", + "administrative_shutdown", + "peer_deconfigured", + "administrative_reset", + "connection_rejected", + "other_configuration_change", + "connection_collision_resolution", + "out_of_resources" + ] + }, + "CheckerSource": { + "type": "object", + "properties": { + "asn": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "code": { + "type": "string" + } + }, + "required": [ + "asn", + "code" + ] + }, + "Community": { + "description": "BGP community value", + "oneOf": [ + { + "description": "All routes received carrying a communities attribute containing this value MUST NOT be advertised outside a BGP confederation boundary (a stand-alone autonomous system that is not part of a confederation should be considered a confederation itself)", + "type": "string", + "enum": [ + "no_export" + ] + }, + { + "description": "All routes received carrying a communities attribute containing this value MUST NOT be advertised to other BGP peers.", + "type": "string", + "enum": [ + "no_advertise" + ] + }, + { + "description": "All routes received carrying a communities attribute containing this value MUST NOT be advertised to external BGP peers (this includes peers in other members autonomous systems inside a BGP confederation).", + "type": "string", + "enum": [ + "no_export_sub_confed" + ] + }, + { + "description": "All routes received carrying a communities attribute containing this value must set the local preference for the received routes to a low value, preferably zero.", + "type": "string", + "enum": [ + "graceful_shutdown" + ] + }, + { + "description": "A user defined community", + "type": "object", + "properties": { + "user_defined": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "user_defined" + ], + "additionalProperties": false + } + ] + }, + "ConnectionId": { + "description": "Unique identifier for a BGP connection instance", + "type": "object", + "properties": { + "local": { + "description": "Local socket address for this connection", + "type": "string" + }, + "remote": { + "description": "Remote socket address for this connection", + "type": "string" + }, + "uuid": { + "description": "Unique identifier for this connection instance", + "type": "string", + "format": "uuid" + } + }, + "required": [ + "local", + "remote", + "uuid" + ] + }, + "DeleteStaticRoute4Request": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/components/schemas/StaticRoute4List" + } + }, + "required": [ + "routes" + ] + }, + "DeleteStaticRoute6Request": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/components/schemas/StaticRoute6List" + } + }, + "required": [ + "routes" + ] + }, + "Duration": { + "type": "object", + "properties": { + "nanos": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "secs": { + "type": "integer", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "nanos", + "secs" + ] + }, + "DynamicTimerInfo": { + "type": "object", + "properties": { + "configured": { + "$ref": "#/components/schemas/Duration" + }, + "negotiated": { + "$ref": "#/components/schemas/Duration" + } + }, + "required": [ + "configured", + "negotiated" + ] + }, + "Error": { + "description": "Error information from a response.", + "type": "object", + "properties": { + "error_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "request_id": { + "type": "string" + } + }, + "required": [ + "message", + "request_id" + ] + }, + "ErrorCode": { + "description": "This enumeration contains possible notification error codes.", + "type": "string", + "enum": [ + "header", + "open", + "update", + "hold_timer_expired", + "fsm", + "cease" + ] + }, + "ErrorSubcode": { + "description": "This enumeration contains possible notification error subcodes.", + "oneOf": [ + { + "type": "object", + "properties": { + "header": { + "$ref": "#/components/schemas/HeaderErrorSubcode" + } + }, + "required": [ + "header" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "open": { + "$ref": "#/components/schemas/OpenErrorSubcode" + } + }, + "required": [ + "open" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "update": { + "$ref": "#/components/schemas/UpdateErrorSubcode" + } + }, + "required": [ + "update" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "hold_time": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "hold_time" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "fsm": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "fsm" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "cease": { + "$ref": "#/components/schemas/CeaseErrorSubcode" + } + }, + "required": [ + "cease" + ], + "additionalProperties": false + } + ] + }, + "FsmEventBuffer": { + "oneOf": [ + { + "description": "All FSM events (high frequency, includes all timers)", + "type": "string", + "enum": [ + "all" + ] + }, + { + "description": "Major events only (state transitions, admin, new connections)", + "type": "string", + "enum": [ + "major" + ] + } + ] + }, + "FsmEventCategory": { + "description": "Category of FSM event for filtering and display purposes", + "type": "string", + "enum": [ + "Admin", + "Connection", + "Session", + "StateTransition" + ] + }, + "FsmEventRecord": { + "description": "Serializable record of an FSM event with full context", + "type": "object", + "properties": { + "connection_id": { + "nullable": true, + "description": "Connection ID if event is connection-specific", + "allOf": [ + { + "$ref": "#/components/schemas/ConnectionId" + } + ] + }, + "current_state": { + "description": "FSM state at time of event", + "allOf": [ + { + "$ref": "#/components/schemas/FsmStateKind" + } + ] + }, + "details": { + "nullable": true, + "description": "Additional event details (e.g., \"Received OPEN\", \"Admin command\")", + "type": "string" + }, + "event_category": { + "description": "High-level event category", + "allOf": [ + { + "$ref": "#/components/schemas/FsmEventCategory" + } + ] + }, + "event_type": { + "description": "Specific event type as string (e.g., \"ManualStart\", \"HoldTimerExpires\")", + "type": "string" + }, + "previous_state": { + "nullable": true, + "description": "Previous state if this caused a transition", + "allOf": [ + { + "$ref": "#/components/schemas/FsmStateKind" + } + ] + }, + "timestamp": { + "description": "UTC timestamp when event occurred", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "current_state", + "event_category", + "event_type", + "timestamp" + ] + }, + "FsmHistoryRequest": { + "type": "object", + "properties": { + "asn": { + "description": "ASN of the BGP router", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "buffer": { + "nullable": true, + "description": "Which buffer to retrieve - if None, returns major buffer", + "allOf": [ + { + "$ref": "#/components/schemas/FsmEventBuffer" + } + ] + }, + "peer": { + "nullable": true, + "description": "Optional peer filter - if None, returns history for all peers", + "type": "string", + "format": "ip" + } + }, + "required": [ + "asn" + ] + }, + "FsmHistoryResponse": { + "type": "object", + "properties": { + "by_peer": { + "description": "Events organized by peer address Each peer's value contains only the events from the requested buffer", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FsmEventRecord" + } + } + } + }, + "required": [ + "by_peer" + ] + }, + "FsmStateKind": { + "description": "Simplified representation of a BGP state without having to carry a connection.", + "oneOf": [ + { + "description": "Initial state. Refuse all incomming BGP connections. No resources allocated to peer.", + "type": "string", + "enum": [ + "Idle" + ] + }, + { + "description": "Waiting for the TCP connection to be completed.", + "type": "string", + "enum": [ + "Connect" + ] + }, + { + "description": "Trying to acquire peer by listening for and accepting a TCP connection.", + "type": "string", + "enum": [ + "Active" + ] + }, + { + "description": "Waiting for open message from peer.", + "type": "string", + "enum": [ + "OpenSent" + ] + }, + { + "description": "Waiting for keepaliave or notification from peer.", + "type": "string", + "enum": [ + "OpenConfirm" + ] + }, + { + "description": "Handler for Connection Collisions (RFC 4271 6.8)", + "type": "string", + "enum": [ + "ConnectionCollision" + ] + }, + { + "description": "Sync up with peers.", + "type": "string", + "enum": [ + "SessionSetup" + ] + }, + { + "description": "Able to exchange update, notification and keepliave messages with peers.", + "type": "string", + "enum": [ + "Established" + ] + } + ] + }, + "HeaderErrorSubcode": { + "description": "Header error subcode types", + "type": "string", + "enum": [ + "unspecific", + "connection_not_synchronized", + "bad_message_length", + "bad_message_type" + ] + }, + "ImportExportPolicy": { + "oneOf": [ + { + "type": "string", + "enum": [ + "NoFiltering" + ] + }, + { + "type": "object", + "properties": { + "Allow": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix" + }, + "uniqueItems": true + } + }, + "required": [ + "Allow" + ], + "additionalProperties": false + } + ] + }, + "Message": { + "description": "Holds a BGP message. May be an Open, Update, Notification or Keep Alive message.", + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "open" + ] + }, + "value": { + "$ref": "#/components/schemas/OpenMessage" + } + }, + "required": [ + "type", + "value" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "update" + ] + }, + "value": { + "$ref": "#/components/schemas/UpdateMessage" + } + }, + "required": [ + "type", + "value" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "notification" + ] + }, + "value": { + "$ref": "#/components/schemas/NotificationMessage" + } + }, + "required": [ + "type", + "value" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "keep_alive" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "route_refresh" + ] + }, + "value": { + "$ref": "#/components/schemas/RouteRefreshMessage" + } + }, + "required": [ + "type", + "value" + ] + } + ] + }, + "MessageDirection": { + "type": "string", + "enum": [ + "sent", + "received" + ] + }, + "MessageHistory": { + "description": "Message history for a BGP session", + "type": "object", + "properties": { + "received": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessageHistoryEntry" + } + }, + "sent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessageHistoryEntry" + } + } + }, + "required": [ + "received", + "sent" + ] + }, + "MessageHistoryEntry": { + "description": "A message history entry is a BGP message with an associated timestamp and connection ID", + "type": "object", + "properties": { + "connection_id": { + "$ref": "#/components/schemas/ConnectionId" + }, + "message": { + "$ref": "#/components/schemas/Message" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "connection_id", + "message", + "timestamp" + ] + }, + "MessageHistoryRequest": { + "type": "object", + "properties": { + "asn": { + "description": "ASN of the BGP router", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "direction": { + "nullable": true, + "description": "Optional direction filter - if None, returns both sent and received", + "allOf": [ + { + "$ref": "#/components/schemas/MessageDirection" + } + ] + }, + "peer": { + "nullable": true, + "description": "Optional peer filter - if None, returns history for all peers", + "type": "string", + "format": "ip" + } + }, + "required": [ + "asn" + ] + }, + "MessageHistoryResponse": { + "type": "object", + "properties": { + "by_peer": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/MessageHistory" + } + } + }, + "required": [ + "by_peer" + ] + }, + "Neighbor": { + "type": "object", + "properties": { + "allow_export": { + "$ref": "#/components/schemas/ImportExportPolicy" + }, + "allow_import": { + "$ref": "#/components/schemas/ImportExportPolicy" + }, + "asn": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "communities": { + "type": "array", + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "connect_retry": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "delay_open": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "enforce_first_as": { + "type": "boolean" + }, + "group": { + "type": "string" + }, + "hold_time": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "host": { + "type": "string" + }, + "idle_hold_time": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "keepalive": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "local_pref": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "md5_auth_key": { + "nullable": true, + "type": "string" + }, + "min_ttl": { + "nullable": true, + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "multi_exit_discriminator": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "name": { + "type": "string" + }, + "passive": { + "type": "boolean" + }, + "remote_asn": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "resolution": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "allow_export", + "allow_import", + "asn", + "communities", + "connect_retry", + "delay_open", + "enforce_first_as", + "group", + "hold_time", + "host", + "idle_hold_time", + "keepalive", + "name", + "passive", + "resolution" + ] + }, + "NeighborResetOp": { + "type": "string", + "enum": [ + "Hard", + "SoftInbound", + "SoftOutbound" + ] + }, + "NeighborResetRequest": { + "type": "object", + "properties": { + "addr": { + "type": "string", + "format": "ip" + }, + "asn": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "op": { + "$ref": "#/components/schemas/NeighborResetOp" + } + }, + "required": [ + "addr", + "asn", + "op" + ] + }, + "NotificationMessage": { + "description": "Notification messages are exchanged between BGP peers when an exceptional event has occurred.", + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "error_code": { + "description": "Error code associated with the notification", + "allOf": [ + { + "$ref": "#/components/schemas/ErrorCode" + } + ] + }, + "error_subcode": { + "description": "Error subcode associated with the notification", + "allOf": [ + { + "$ref": "#/components/schemas/ErrorSubcode" + } + ] + } + }, + "required": [ + "data", + "error_code", + "error_subcode" + ] + }, + "OpenErrorSubcode": { + "description": "Open message error subcode types", + "type": "string", + "enum": [ + "unspecific", + "unsupported_version_number", + "bad_peer_a_s", + "bad_bgp_identifier", + "unsupported_optional_parameter", + "deprecated", + "unacceptable_hold_time", + "unsupported_capability" + ] + }, + "OpenMessage": { + "description": "The first message sent by each side once a TCP connection is established.\n\n```text 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Version | My Autonomous System | Hold Time : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : | BGP Identifier : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : | Opt Parm Len | Optional Parameters : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : : : Optional Parameters (cont, variable) : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ```\n\nRef: RFC 4271 §4.2", + "type": "object", + "properties": { + "asn": { + "description": "Autonomous system number of the sender. When 4-byte ASNs are in use this value is set to AS_TRANS which has a value of 23456.\n\nRef: RFC 4893 §7", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "hold_time": { + "description": "Number of seconds the sender proposes for the hold timer.", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "id": { + "description": "BGP identifier of the sender", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "parameters": { + "description": "A list of optional parameters.", + "type": "array", + "items": { + "$ref": "#/components/schemas/OptionalParameter" + } + }, + "version": { + "description": "BGP protocol version.", + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "asn", + "hold_time", + "id", + "parameters", + "version" + ] + }, + "OptionalParameter": { + "description": "The IANA/IETF currently defines the following optional parameter types.", + "oneOf": [ + { + "description": "Code 0", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "reserved" + ] + } + }, + "required": [ + "type" + ] + }, + { + "description": "Code 1: RFC 4217, RFC 5492 (deprecated)", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "authentication" + ] + } + }, + "required": [ + "type" + ] + }, + { + "description": "Code 2: RFC 5492", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "capabilities" + ] + }, + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Capability" + }, + "uniqueItems": true + } + }, + "required": [ + "type", + "value" + ] + }, + { + "description": "Unassigned", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "unassigned" + ] + } + }, + "required": [ + "type" + ] + }, + { + "description": "Code 255: RFC 9072", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "extended_length" + ] + } + }, + "required": [ + "type" + ] + } + ] + }, + "Origin4": { + "type": "object", + "properties": { + "asn": { + "description": "ASN of the router to originate from.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "prefixes": { + "description": "Set of prefixes to originate.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix4" + } + } + }, + "required": [ + "asn", + "prefixes" + ] + }, + "Origin6": { + "type": "object", + "properties": { + "asn": { + "description": "ASN of the router to originate from.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "prefixes": { + "description": "Set of prefixes to originate.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix6" + } + } + }, + "required": [ + "asn", + "prefixes" + ] + }, + "Path": { + "type": "object", + "properties": { + "bgp": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/BgpPathProperties" + } + ] + }, + "nexthop": { + "type": "string", + "format": "ip" + }, + "rib_priority": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "shutdown": { + "type": "boolean" + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "nexthop", + "rib_priority", + "shutdown" + ] + }, + "PathAttribute": { + "description": "A self-describing BGP path attribute", + "type": "object", + "properties": { + "typ": { + "description": "Type encoding for the attribute", + "allOf": [ + { + "$ref": "#/components/schemas/PathAttributeType" + } + ] + }, + "value": { + "description": "Value of the attribute", + "allOf": [ + { + "$ref": "#/components/schemas/PathAttributeValue" + } + ] + } + }, + "required": [ + "typ", + "value" + ] + }, + "PathAttributeType": { + "description": "Type encoding for a path attribute.", + "type": "object", + "properties": { + "flags": { + "description": "Flags may include, Optional, Transitive, Partial and Extended Length.", + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "type_code": { + "description": "Type code for the path attribute.", + "allOf": [ + { + "$ref": "#/components/schemas/PathAttributeTypeCode" + } + ] + } + }, + "required": [ + "flags", + "type_code" + ] + }, + "PathAttributeTypeCode": { + "description": "An enumeration describing available path attribute type codes.", + "oneOf": [ + { + "type": "string", + "enum": [ + "as_path", + "next_hop", + "multi_exit_disc", + "local_pref", + "atomic_aggregate", + "aggregator", + "communities", + "as4_aggregator" + ] + }, + { + "description": "RFC 4271", + "type": "string", + "enum": [ + "origin" + ] + }, + { + "description": "RFC 6793", + "type": "string", + "enum": [ + "as4_path" + ] + } + ] + }, + "PathAttributeValue": { + "description": "The value encoding of a path attribute.", + "oneOf": [ + { + "description": "The type of origin associated with a path", + "type": "object", + "properties": { + "origin": { + "$ref": "#/components/schemas/PathOrigin" + } + }, + "required": [ + "origin" + ], + "additionalProperties": false + }, + { + "description": "The AS set associated with a path", + "type": "object", + "properties": { + "as_path": { + "type": "array", + "items": { + "$ref": "#/components/schemas/As4PathSegment" + } + } + }, + "required": [ + "as_path" + ], + "additionalProperties": false + }, + { + "description": "The nexthop associated with a path", + "type": "object", + "properties": { + "next_hop": { + "type": "string", + "format": "ip" + } + }, + "required": [ + "next_hop" + ], + "additionalProperties": false + }, + { + "description": "A metric used for external (inter-AS) links to discriminate among multiple entry or exit points.", + "type": "object", + "properties": { + "multi_exit_disc": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "multi_exit_disc" + ], + "additionalProperties": false + }, + { + "description": "Local pref is included in update messages sent to internal peers and indicates a degree of preference.", + "type": "object", + "properties": { + "local_pref": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "local_pref" + ], + "additionalProperties": false + }, + { + "description": "This attribute is included in routes that are formed by aggregation.", + "type": "object", + "properties": { + "aggregator": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "minItems": 6, + "maxItems": 6 + } + }, + "required": [ + "aggregator" + ], + "additionalProperties": false + }, + { + "description": "Indicates communities associated with a path.", + "type": "object", + "properties": { + "communities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Community" + } + } + }, + "required": [ + "communities" + ], + "additionalProperties": false + }, + { + "description": "The 4-byte encoded AS set associated with a path", + "type": "object", + "properties": { + "as4_path": { + "type": "array", + "items": { + "$ref": "#/components/schemas/As4PathSegment" + } + } + }, + "required": [ + "as4_path" + ], + "additionalProperties": false + }, + { + "description": "This attribute is included in routes that are formed by aggregation.", + "type": "object", + "properties": { + "as4_aggregator": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "minItems": 8, + "maxItems": 8 + } + }, + "required": [ + "as4_aggregator" + ], + "additionalProperties": false + } + ] + }, + "PathOrigin": { + "description": "An enumeration indicating the origin type of a path.", + "oneOf": [ + { + "description": "Interior gateway protocol", + "type": "string", + "enum": [ + "igp" + ] + }, + { + "description": "Exterior gateway protocol", + "type": "string", + "enum": [ + "egp" + ] + }, + { + "description": "Incomplete path origin", + "type": "string", + "enum": [ + "incomplete" + ] + } + ] + }, + "PeerInfo": { + "type": "object", + "properties": { + "asn": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "duration_millis": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "state": { + "$ref": "#/components/schemas/FsmStateKind" + }, + "timers": { + "$ref": "#/components/schemas/PeerTimers" + } + }, + "required": [ + "duration_millis", + "state", + "timers" + ] + }, + "PeerTimers": { + "type": "object", + "properties": { + "hold": { + "$ref": "#/components/schemas/DynamicTimerInfo" + }, + "keepalive": { + "$ref": "#/components/schemas/DynamicTimerInfo" + } + }, + "required": [ + "hold", + "keepalive" + ] + }, + "Prefix": { + "oneOf": [ + { + "type": "object", + "properties": { + "V4": { + "$ref": "#/components/schemas/Prefix4" + } + }, + "required": [ + "V4" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "V6": { + "$ref": "#/components/schemas/Prefix6" + } + }, + "required": [ + "V6" + ], + "additionalProperties": false + } + ] + }, + "Prefix4": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "value": { + "type": "string", + "format": "ipv4" + } + }, + "required": [ + "length", + "value" + ] + }, + "Prefix6": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "value": { + "type": "string", + "format": "ipv6" + } + }, + "required": [ + "length", + "value" + ] + }, + "Rib": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Path" + }, + "uniqueItems": true + } + }, + "RouteRefreshMessage": { + "type": "object", + "properties": { + "afi": { + "description": "Address family identifier.", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "safi": { + "description": "Subsequent address family identifier.", + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "required": [ + "afi", + "safi" + ] + }, + "Router": { + "type": "object", + "properties": { + "asn": { + "description": "Autonomous system number for this router", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "graceful_shutdown": { + "description": "Gracefully shut this router down.", + "type": "boolean" + }, + "id": { + "description": "Id for this router", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "listen": { + "description": "Listening address :", + "type": "string" + } + }, + "required": [ + "asn", + "graceful_shutdown", + "id", + "listen" + ] + }, + "SessionMode": { + "type": "string", + "enum": [ + "SingleHop", + "MultiHop" + ] + }, + "ShaperSource": { + "type": "object", + "properties": { + "asn": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "code": { + "type": "string" + } + }, + "required": [ + "asn", + "code" + ] + }, + "StaticRoute4": { + "type": "object", + "properties": { + "nexthop": { + "type": "string", + "format": "ipv4" + }, + "prefix": { + "$ref": "#/components/schemas/Prefix4" + }, + "rib_priority": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "nexthop", + "prefix", + "rib_priority" + ] + }, + "StaticRoute4List": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaticRoute4" + } + } + }, + "required": [ + "list" + ] + }, + "StaticRoute6": { + "type": "object", + "properties": { + "nexthop": { + "type": "string", + "format": "ipv6" + }, + "prefix": { + "$ref": "#/components/schemas/Prefix6" + }, + "rib_priority": { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "nexthop", + "prefix", + "rib_priority" + ] + }, + "StaticRoute6List": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaticRoute6" + } + } + }, + "required": [ + "list" + ] + }, + "SwitchIdentifiers": { + "description": "Identifiers for a switch.", + "type": "object", + "properties": { + "slot": { + "nullable": true, + "description": "The slot number of the switch being managed.\n\nMGS uses u16 for this internally.", + "type": "integer", + "format": "uint16", + "minimum": 0 + } + } + }, + "UpdateErrorSubcode": { + "description": "Update message error subcode types", + "type": "string", + "enum": [ + "unspecific", + "malformed_attribute_list", + "unrecognized_well_known_attribute", + "missing_well_known_attribute", + "attribute_flags", + "attribute_length", + "invalid_origin_attribute", + "deprecated", + "invalid_nexthop_attribute", + "optional_attribute", + "invalid_network_field", + "malformed_as_path" + ] + }, + "UpdateMessage": { + "description": "An update message is used to advertise feasible routes that share common path attributes to a peer, or to withdraw multiple unfeasible routes from service.\n\n```text 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Witdrawn Length | Withdrawn Routes : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : : : Withdrawn Routes (cont, variable) : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Path Attribute Length | Path Attributes : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : : : Path Attributes (cont, variable) : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : : : Network Layer Reachability Information (variable) : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ```\n\nRef: RFC 4271 §4.3", + "type": "object", + "properties": { + "nlri": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix" + } + }, + "path_attributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PathAttribute" + } + }, + "withdrawn": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prefix" + } + } + }, + "required": [ + "nlri", + "path_attributes", + "withdrawn" + ] + }, + "AddressFamily": { + "description": "Represents the address family (protocol version) for network routes.\n\nThis is the canonical source of truth for address family definitions across the entire codebase. All routing-related components (RIB operations, BGP messages, API filtering, CLI tools) use this single enum rather than defining their own.\n\n# Semantics\n\nWhen used in filtering contexts (e.g., database queries or API parameters), `Option` is preferred: - `None` = no filter (match all address families) - `Some(Ipv4)` = IPv4 routes only - `Some(Ipv6)` = IPv6 routes only\n\n# Examples\n\n``` use rdb_types::AddressFamily;\n\nlet ipv4 = AddressFamily::Ipv4; let ipv6 = AddressFamily::Ipv6;\n\n// For filtering, use Option let filter: Option = Some(AddressFamily::Ipv4); let no_filter: Option = None; // matches all families ```", + "oneOf": [ + { + "description": "Internet Protocol Version 4 (IPv4)", + "type": "string", + "enum": [ + "Ipv4" + ] + }, + { + "description": "Internet Protocol Version 6 (IPv6)", + "type": "string", + "enum": [ + "Ipv6" + ] + } + ] + }, + "ProtocolFilter": { + "oneOf": [ + { + "description": "BGP routes only", + "type": "string", + "enum": [ + "Bgp" + ] + }, + { + "description": "Static routes only", + "type": "string", + "enum": [ + "Static" + ] + } + ] + } + }, + "responses": { + "Error": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } +} diff --git a/openapi/mg-admin/mg-admin-latest.json b/openapi/mg-admin/mg-admin-latest.json index b4b0dc8d..64bfad20 120000 --- a/openapi/mg-admin/mg-admin-latest.json +++ b/openapi/mg-admin/mg-admin-latest.json @@ -1 +1 @@ -mg-admin-2.0.0-b4b255.json \ No newline at end of file +mg-admin-3.0.0-08e2f2.json \ No newline at end of file diff --git a/rdb/src/db.rs b/rdb/src/db.rs index 30110339..52c7c119 100644 --- a/rdb/src/db.rs +++ b/rdb/src/db.rs @@ -16,7 +16,7 @@ use crate::types::*; use chrono::Utc; use mg_common::{lock, read_lock, write_lock}; use sled::Tree; -use slog::Logger; +use slog::{Logger, error}; use std::cmp::Ordering as CmpOrdering; use std::collections::{BTreeMap, BTreeSet}; use std::net::{IpAddr, Ipv6Addr}; @@ -104,6 +104,10 @@ pub struct Db { /// Reaps expired routes from the local RIB reaper: Arc, + /// Switch slot reported from MGS. + /// Information is not available until first successful communication with MGS. + slot: Arc>>, + log: Logger, } unsafe impl Sync for Db {} @@ -129,6 +133,7 @@ impl Db { generation: Arc::new(AtomicU64::new(0)), watchers: Arc::new(RwLock::new(Vec::new())), reaper: Reaper::new(rib_loc), + slot: Arc::new(RwLock::new(None)), log, }) } @@ -1266,6 +1271,21 @@ impl Db { } }); } + + pub fn slot(&self) -> Option { + match self.slot.read() { + Ok(v) => *v, + Err(e) => { + error!(self.log, "unable to read switch slot"; "error" => %e); + None + } + } + } + + pub fn set_slot(&mut self, slot: Option) { + let mut value = self.slot.write().unwrap(); + *value = slot; + } } struct Reaper {