diff --git a/nexus/db-queries/src/db/datastore/bgp.rs b/nexus/db-queries/src/db/datastore/bgp.rs index fdb9629543..c0c320424f 100644 --- a/nexus/db-queries/src/db/datastore/bgp.rs +++ b/nexus/db-queries/src/db/datastore/bgp.rs @@ -223,7 +223,7 @@ impl DataStore { pub async fn bgp_config_delete( &self, opctx: &OpContext, - sel: ¶ms::BgpConfigSelector, + sel: &NameOrId, ) -> DeleteResult { use db::schema::bgp_config; use db::schema::bgp_config::dsl as bgp_config_dsl; @@ -237,7 +237,7 @@ impl DataStore { .transaction(&conn, |conn| { let err = err.clone(); async move { - let name_or_id = sel.name_or_id.clone(); + let name_or_id = sel.clone(); let id: Uuid = match name_or_id { NameOrId::Id(id) => bgp_config_dsl::bgp_config diff --git a/nexus/db-queries/src/db/datastore/switch_port.rs b/nexus/db-queries/src/db/datastore/switch_port.rs index 48ea99542b..e2760b8634 100644 --- a/nexus/db-queries/src/db/datastore/switch_port.rs +++ b/nexus/db-queries/src/db/datastore/switch_port.rs @@ -26,8 +26,8 @@ use crate::transaction_retry::OptionalError; use async_bb8_diesel::{AsyncRunQueryDsl, Connection}; use diesel::CombineDsl; use diesel::{ - ExpressionMethods, JoinOnDsl, NullableExpressionMethods, PgConnection, - QueryDsl, SelectableHelper, + ExpressionMethods, JoinOnDsl, NullableExpressionMethods, OptionalExtension, + PgConnection, QueryDsl, SelectableHelper, }; use diesel_dtrace::DTraceConnection; use ipnetwork::IpNetwork; @@ -1823,8 +1823,7 @@ impl DataStore { self.transaction_retry_wrapper( "switch_port_configuration_bgp_peer_allow_import_add", - ) - .transaction(&conn, |conn| { + ).transaction(&conn, |conn| { let parent_configuration = configuration.clone(); let new_settings = prefix.clone(); let err = err.clone(); @@ -1858,6 +1857,20 @@ impl DataStore { prefix: new_settings.prefix.into(), }; + let found_config = allow_import::table + .filter(allow_import::port_settings_id.eq(allow_import_config.port_settings_id)) + .filter(allow_import::interface_name.eq(allow_import_config.interface_name.clone())) + .filter(allow_import::addr.eq(allow_import_config.addr)) + .filter(allow_import::prefix.eq(allow_import_config.prefix)) + .select(SwitchPortBgpPeerConfigAllowImport::as_select()) + .get_result_async(&conn) + .await + .optional()?; + + if let Some(config) = found_config { + return Ok(config) + } + let config = diesel::insert_into(allow_import::table) .values(allow_import_config) .returning(SwitchPortBgpPeerConfigAllowImport::as_returning()) @@ -2048,6 +2061,20 @@ impl DataStore { prefix: new_settings.prefix.into(), }; + let found_config = allow_export::table + .filter(allow_export::port_settings_id.eq(allow_export_config.port_settings_id)) + .filter(allow_export::interface_name.eq(allow_export_config.interface_name.clone())) + .filter(allow_export::addr.eq(allow_export_config.addr)) + .filter(allow_export::prefix.eq(allow_export_config.prefix)) + .select(SwitchPortBgpPeerConfigAllowExport::as_select()) + .get_result_async(&conn) + .await + .optional()?; + + if let Some(config) = found_config { + return Ok(config) + } + let config = diesel::insert_into(allow_export::table) .values(allow_export_config) .returning(SwitchPortBgpPeerConfigAllowExport::as_returning()) @@ -2238,6 +2265,20 @@ impl DataStore { community: new_settings.community.into(), }; + let found_config = communities::table + .filter(communities::port_settings_id.eq(community_config.port_settings_id)) + .filter(communities::interface_name.eq(community_config.interface_name.clone())) + .filter(communities::addr.eq(community_config.addr)) + .filter(communities::community.eq(community_config.community)) + .select(SwitchPortBgpPeerConfigCommunity::as_select()) + .get_result_async(&conn) + .await + .optional()?; + + if let Some(config) = found_config { + return Ok(config) + } + let config = diesel::insert_into(communities::table) .values(community_config) .returning(SwitchPortBgpPeerConfigCommunity::as_returning()) diff --git a/nexus/src/app/bgp.rs b/nexus/src/app/bgp.rs index 31a0faa663..1b027b94f5 100644 --- a/nexus/src/app/bgp.rs +++ b/nexus/src/app/bgp.rs @@ -47,7 +47,7 @@ impl super::Nexus { pub async fn bgp_config_delete( &self, opctx: &OpContext, - sel: ¶ms::BgpConfigSelector, + sel: &NameOrId, ) -> DeleteResult { opctx.authorize(authz::Action::Modify, &authz::FLEET).await?; let result = self.db_datastore.bgp_config_delete(opctx, sel).await?; diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 3c73d23cff..c5ebe7bac4 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -4940,17 +4940,17 @@ async fn networking_bgp_imported_routes_ipv4( /// Delete BGP configuration #[endpoint { method = DELETE, - path = "/v1/system/networking/bgp", + path = "/v1/system/networking/bgp/{bgp_config}", tags = ["system/networking"], }] async fn networking_bgp_config_delete( rqctx: RequestContext, - sel: Query, + sel: Path, ) -> Result { let apictx = rqctx.context(); let handler = async { let nexus = &apictx.context.nexus; - let sel = sel.into_inner(); + let sel = sel.into_inner().bgp_config; let opctx = crate::context::op_context_for_external_api(&rqctx).await?; nexus.bgp_config_delete(&opctx, &sel).await?; Ok(HttpResponseUpdatedNoContent {}) @@ -4974,14 +4974,14 @@ async fn networking_bgp_config_delete( async fn networking_bgp_announce_set_update( rqctx: RequestContext, config: TypedBody, -) -> Result, HttpError> { +) -> Result, HttpError> { let apictx = rqctx.context(); let handler = async { let nexus = &apictx.context.nexus; let config = config.into_inner(); let opctx = crate::context::op_context_for_external_api(&rqctx).await?; let result = nexus.bgp_update_announce_set(&opctx, &config).await?; - Ok(HttpResponseCreated::(result.0.into())) + Ok(HttpResponseOk::(result.0.into())) }; apictx .context diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index 30a92ad9e9..3d2d0d683c 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -23,12 +23,15 @@ use nexus_types::external_api::shared; use nexus_types::external_api::shared::IpRange; use nexus_types::external_api::shared::Ipv4Range; use nexus_types::external_api::views::SledProvisionPolicy; +use omicron_common::api::external; use omicron_common::api::external::AddressLotKind; use omicron_common::api::external::AllowedSourceIps; use omicron_common::api::external::ByteCount; use omicron_common::api::external::IdentityMetadataCreateParams; use omicron_common::api::external::IdentityMetadataUpdateParams; use omicron_common::api::external::InstanceCpuCount; +use omicron_common::api::external::LinkFec; +use omicron_common::api::external::LinkSpeed; use omicron_common::api::external::Name; use omicron_common::api::external::NameOrId; use omicron_common::api::external::RouteDestination; @@ -548,6 +551,137 @@ pub static DEMO_SWITCH_PORT_GEOMETRY_CREATE: Lazy< geometry: params::SwitchPortGeometry::Qsfp28x1, }); +pub const DEMO_SWITCH_PORT_LINK_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/link"; + +pub static DEMO_SWITCH_PORT_LINK_CREATE: Lazy = + Lazy::new(|| params::NamedLinkConfigCreate { + name: "my-link".parse().unwrap(), + mtu: 1500, + lldp_config: None, + fec: LinkFec::None, + speed: LinkSpeed::Speed100G, + autoneg: true, + }); + +pub const DEMO_SWITCH_PORT_LINK_INFO_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/link/my-link"; + +pub const DEMO_SWITCH_PORT_ADDRESS_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/address"; + +pub const DEMO_SWITCH_PORT_ADDRESS_ADD_REMOVE: Lazy = + Lazy::new(|| params::AddressAddRemove { + interface: "qsfp0".parse().unwrap(), + address_lot: NameOrId::Name("parkinglot".parse().unwrap()), + address: "203.0.113.11/24".parse().unwrap(), + vlan_id: None, + }); + +pub const DEMO_SWITCH_PORT_ADDRESS_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/address/add"; + +pub const DEMO_SWITCH_PORT_ADDRESS_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/address/remove"; + +pub const DEMO_SWITCH_PORT_ROUTE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/route"; + +pub const DEMO_SWITCH_PORT_ROUTE_ADD_REMOVE: Lazy = + Lazy::new(|| params::RouteAddRemove { + interface: "qsfp0".parse().unwrap(), + dst: "0.0.0.0/0".parse().unwrap(), + gw: "203.0.113.1".parse().unwrap(), + vid: None, + local_pref: None, + }); + +pub const DEMO_SWITCH_PORT_ROUTE_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/route/add"; + +pub const DEMO_SWITCH_PORT_ROUTE_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/route/remove"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ADD: Lazy = + Lazy::new(|| external::BgpPeer { + bgp_config: NameOrId::Name("as47".parse().unwrap()), + interface_name: "qsfp0".into(), + addr: "203.0.113.12/24".parse().unwrap(), + hold_time: Default::default(), + idle_hold_time: Default::default(), + delay_open: Default::default(), + connect_retry: Default::default(), + keepalive: Default::default(), + remote_asn: None, + min_ttl: None, + md5_auth_key: None, + multi_exit_discriminator: None, + local_pref: None, + enforce_first_as: false, + allow_import_list_active: false, + allow_export_list_active: false, + vlan_id: None, + }); + +pub const DEMO_SWITCH_PORT_BGP_PEER_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/add"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_REMOVE: Lazy = + Lazy::new(|| external::BgpPeerRemove { + bgp_config: NameOrId::Name("as47".parse().unwrap()), + interface_name: "qsfp0".into(), + addr: "203.0.113.12".parse().unwrap(), + }); + +pub const DEMO_SWITCH_PORT_BGP_PEER_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/remove"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX: Lazy< + params::AllowedPrefixAddRemove, +> = Lazy::new(|| params::AllowedPrefixAddRemove { + peer_address: "203.0.113.12".parse().unwrap(), + interface: "qsfp0".parse().unwrap(), + prefix: "10.0.0.0/24".parse().unwrap(), +}); + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-import?peer_address=203.0.113.12"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-import/add"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-import/remove"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-export?peer_address=203.0.113.12"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-export/add"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/allow-export/remove"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/community?peer_address=203.0.113.12"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_REMOVE: Lazy< + params::BgpCommunityAddRemove, +> = Lazy::new(|| params::BgpCommunityAddRemove { + peer_address: "203.0.113.12".parse().unwrap(), + interface: "qsfp0".parse().unwrap(), + community: 100, +}); + +pub const DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/community/add"; + +pub const DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_REMOVE_URL: &'static str = + "/v1/system/networking/switch-port-configuration/portofino/bgp-peer/community/remove"; + pub const DEMO_ADDRESS_LOTS_URL: &'static str = "/v1/system/networking/address-lot"; pub const DEMO_ADDRESS_LOT_URL: &'static str = @@ -574,22 +708,27 @@ pub static DEMO_ADDRESS_LOT_BLOCK_CREATE: Lazy< last_address: "203.0.113.20".parse().unwrap(), }); -pub const DEMO_BGP_CONFIG_CREATE_URL: &'static str = - "/v1/system/networking/bgp?name_or_id=as47"; +pub const DEMO_BGP_CONFIG_URL: &'static str = "/v1/system/networking/bgp"; + pub static DEMO_BGP_CONFIG: Lazy = Lazy::new(|| params::BgpConfigCreate { identity: IdentityMetadataCreateParams { name: "as47".parse().unwrap(), description: "BGP config for AS47".into(), }, - bgp_announce_set_id: NameOrId::Name("instances".parse().unwrap()), + bgp_announce_set_id: NameOrId::Name("a-bag-of-addrs".parse().unwrap()), asn: 47, vrf: None, checker: None, shaper: None, }); + +pub const DEMO_BGP_CONFIG_INFO_URL: &'static str = + "/v1/system/networking/bgp/as47"; + pub const DEMO_BGP_ANNOUNCE_SET_URL: &'static str = "/v1/system/networking/bgp-announce-set"; + pub static DEMO_BGP_ANNOUNCE: Lazy = Lazy::new(|| params::BgpAnnounceSetCreate { identity: IdentityMetadataCreateParams { @@ -601,10 +740,13 @@ pub static DEMO_BGP_ANNOUNCE: Lazy = network: "10.0.0.0/16".parse().unwrap(), }], }); + pub const DEMO_BGP_ANNOUNCE_SET_DELETE_URL: &'static str = "/v1/system/networking/bgp-announce-set/a-bag-of-addrs"; + pub const DEMO_BGP_ANNOUNCEMENT_URL: &'static str = "/v1/system/networking/bgp-announce-set/a-bag-of-addrs/announcement"; + pub const DEMO_BGP_STATUS_URL: &'static str = "/v1/system/networking/bgp-status"; pub const DEMO_BGP_EXPORTED_URL: &'static str = @@ -2340,7 +2482,240 @@ pub static VERIFY_ENDPOINTS: Lazy> = Lazy::new(|| { }, VerifyEndpoint { - url: &DEMO_BGP_CONFIG_CREATE_URL, + url: &DEMO_SWITCH_PORT_LINK_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_LINK_CREATE).unwrap(), + ), + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_LINK_INFO_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ADDRESS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ADDRESS_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_ADDRESS_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ADDRESS_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_ADDRESS_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ROUTE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ROUTE_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_ROUTE_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_ROUTE_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_ROUTE_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ADD + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_REMOVE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_REMOVE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_BGP_CONFIG_URL, visibility: Visibility::Public, unprivileged_access: UnprivilegedAccess::None, allowed_methods: vec![ @@ -2348,6 +2723,14 @@ pub static VERIFY_ENDPOINTS: Lazy> = Lazy::new(|| { serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), ), AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_BGP_CONFIG_INFO_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ AllowedMethod::Delete ], }, diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index f285b4569d..c8f8262b0b 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -96,6 +96,16 @@ async fn test_unauthorized(cptestctx: &ControlPlaneTestContext) { .unwrap(), id_routes, ), + SetupReq::Put { url, body, id_routes } => ( + url, + NexusRequest::object_put(client, url, Some(body)) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .map_err(|e| panic!("Failed to PUT to URL: {url}, {e}")) + .unwrap(), + id_routes, + ), }; setup_results.insert(url, result.clone()); @@ -169,6 +179,11 @@ enum SetupReq { body: serde_json::Value, id_routes: Vec<&'static str>, }, + Put { + url: &'static str, + body: serde_json::Value, + id_routes: Vec<&'static str>, + }, } pub static HTTP_SERVER: Lazy = @@ -348,6 +363,72 @@ static SETUP_REQUESTS: Lazy> = Lazy::new(|| { .unwrap(), id_routes: vec![], }, + // Create a switch port link + SetupReq::Post { + url: &DEMO_SWITCH_PORT_LINK_URL, + body: serde_json::to_value(&*DEMO_SWITCH_PORT_LINK_CREATE).unwrap(), + id_routes: vec![], + }, + // Create a switch port address + SetupReq::Post { + url: &DEMO_SWITCH_PORT_ADDRESS_ADD_URL, + body: serde_json::to_value(&*DEMO_SWITCH_PORT_ADDRESS_ADD_REMOVE) + .unwrap(), + id_routes: vec![], + }, + // Create a switch port route + SetupReq::Post { + url: &DEMO_SWITCH_PORT_ROUTE_ADD_URL, + body: serde_json::to_value(&*DEMO_SWITCH_PORT_ROUTE_ADD_REMOVE) + .unwrap(), + id_routes: vec![], + }, + // Create a bgp announce set + SetupReq::Put { + url: &DEMO_BGP_ANNOUNCE_SET_URL, + body: serde_json::to_value(&*DEMO_BGP_ANNOUNCE).unwrap(), + id_routes: vec![], + }, + // Create a bgp config + SetupReq::Post { + url: &DEMO_BGP_CONFIG_URL, + body: serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), + id_routes: vec![], + }, + // Create a switch port bgp peer + SetupReq::Post { + url: &DEMO_SWITCH_PORT_BGP_PEER_ADD_URL, + body: serde_json::to_value(&*DEMO_SWITCH_PORT_BGP_PEER_ADD) + .unwrap(), + id_routes: vec![], + }, + // Allow a prefix to be exported by a peer + SetupReq::Post { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_IMPORT_ADD_URL, + body: serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX, + ) + .unwrap(), + id_routes: vec![], + }, + // Allow a prefix to be imported by a peer + SetupReq::Post { + url: &DEMO_SWITCH_PORT_BGP_PEER_ALLOW_EXPORT_ADD_URL, + body: serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_ALLOWED_PREFIX, + ) + .unwrap(), + id_routes: vec![], + }, + // Add a community to a peer + SetupReq::Post { + url: &DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_URL, + body: serde_json::to_value( + &*DEMO_SWITCH_PORT_BGP_PEER_COMMUNITY_ADD_REMOVE, + ) + .unwrap(), + id_routes: vec![], + }, ] }); diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index 41e7b96751..b08057f6e2 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -185,7 +185,7 @@ networking_bgp_announce_set_list GET /v1/system/networking/bgp-anno networking_bgp_announce_set_update PUT /v1/system/networking/bgp-announce-set networking_bgp_announcement_list GET /v1/system/networking/bgp-announce-set/{name_or_id}/announcement networking_bgp_config_create POST /v1/system/networking/bgp -networking_bgp_config_delete DELETE /v1/system/networking/bgp +networking_bgp_config_delete DELETE /v1/system/networking/bgp/{bgp_config} networking_bgp_config_list GET /v1/system/networking/bgp networking_bgp_exported GET /v1/system/networking/bgp-exported networking_bgp_imported_routes_ipv4 GET /v1/system/networking/bgp-routes-ipv4 diff --git a/nexus/tests/output/uncovered-authz-endpoints.txt b/nexus/tests/output/uncovered-authz-endpoints.txt index 7e030f47ae..c5091c5a3b 100644 --- a/nexus/tests/output/uncovered-authz-endpoints.txt +++ b/nexus/tests/output/uncovered-authz-endpoints.txt @@ -1,18 +1,9 @@ API endpoints with no coverage in authz tests: probe_delete (delete "/experimental/v1/probes/{probe}") -networking_switch_port_configuration_link_delete (delete "/v1/system/networking/switch-port-configuration/{configuration}/link/{link}") probe_list (get "/experimental/v1/probes") probe_view (get "/experimental/v1/probes/{probe}") ping (get "/v1/ping") networking_switch_port_status (get "/v1/system/hardware/switch-port/{port}/status") -networking_switch_port_configuration_address_list (get "/v1/system/networking/switch-port-configuration/{configuration}/address") -networking_switch_port_configuration_bgp_peer_list (get "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer") -networking_switch_port_configuration_bgp_peer_allow_export_list (get "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-export") -networking_switch_port_configuration_bgp_peer_allow_import_list (get "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-import") -networking_switch_port_configuration_bgp_peer_community_list (get "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/community") -networking_switch_port_configuration_link_list (get "/v1/system/networking/switch-port-configuration/{configuration}/link") -networking_switch_port_configuration_link_view (get "/v1/system/networking/switch-port-configuration/{configuration}/link/{link}") -networking_switch_port_configuration_route_list (get "/v1/system/networking/switch-port-configuration/{configuration}/route") device_auth_request (post "/device/auth") device_auth_confirm (post "/device/confirm") device_access_token (post "/device/token") @@ -20,16 +11,3 @@ probe_create (post "/experimental/v1/probes") login_saml (post "/login/{silo_name}/saml/{provider_name}") login_local (post "/v1/login/{silo_name}/local") logout (post "/v1/logout") -networking_switch_port_configuration_address_add (post "/v1/system/networking/switch-port-configuration/{configuration}/address/add") -networking_switch_port_configuration_address_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/address/remove") -networking_switch_port_configuration_bgp_peer_add (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/add") -networking_switch_port_configuration_bgp_peer_allow_export_add (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-export/add") -networking_switch_port_configuration_bgp_peer_allow_export_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-export/remove") -networking_switch_port_configuration_bgp_peer_allow_import_add (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-import/add") -networking_switch_port_configuration_bgp_peer_allow_import_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/allow-import/remove") -networking_switch_port_configuration_bgp_peer_community_add (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/community/add") -networking_switch_port_configuration_bgp_peer_community_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/community/remove") -networking_switch_port_configuration_bgp_peer_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/bgp-peer/remove") -networking_switch_port_configuration_link_create (post "/v1/system/networking/switch-port-configuration/{configuration}/link") -networking_switch_port_configuration_route_add (post "/v1/system/networking/switch-port-configuration/{configuration}/route/add") -networking_switch_port_configuration_route_remove (post "/v1/system/networking/switch-port-configuration/{configuration}/route/remove") diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index e569f465b4..301d52340d 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -1671,7 +1671,7 @@ pub struct BgpCommunityAddRemove { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] pub struct BgpConfigSelector { /// A name or id to use when selecting BGP config. - pub name_or_id: NameOrId, + pub bgp_config: NameOrId, } /// List BGP configs with an optional name or id. diff --git a/openapi/nexus.json b/openapi/nexus.json index e08adf3f1f..5613afe93a 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -6591,7 +6591,9 @@ "$ref": "#/components/responses/Error" } } - }, + } + }, + "/v1/system/networking/bgp/{bgp_config}": { "delete": { "tags": [ "system/networking" @@ -6600,8 +6602,8 @@ "operationId": "networking_bgp_config_delete", "parameters": [ { - "in": "query", - "name": "name_or_id", + "in": "path", + "name": "bgp_config", "description": "A name or id to use when selecting BGP config.", "required": true, "schema": { @@ -6710,8 +6712,8 @@ "required": true }, "responses": { - "201": { - "description": "successful creation", + "200": { + "description": "successful operation", "content": { "application/json": { "schema": {