Skip to content

Commit

Permalink
rack-init: save RSS BGP config to db, api: vec of bgp peers per inter…
Browse files Browse the repository at this point in the history
…face
  • Loading branch information
rcgoodfellow committed Nov 28, 2023
1 parent 003ca4d commit 4b7bb41
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 62 deletions.
96 changes: 50 additions & 46 deletions nexus/db-queries/src/db/datastore/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,39 +305,41 @@ impl DataStore {
.await?;

let mut bgp_peer_config = Vec::new();
for (interface_name, p) in &params.bgp_peers {
use db::schema::bgp_config;
let bgp_config_id = match &p.bgp_config {
NameOrId::Id(id) => *id,
NameOrId::Name(name) => {
let name = name.to_string();
bgp_config_dsl::bgp_config
.filter(bgp_config::time_deleted.is_null())
.filter(bgp_config::name.eq(name))
.select(bgp_config::id)
.limit(1)
.first_async::<Uuid>(&conn)
.await
.map_err(|_|
TxnError::CustomError(
SwitchPortSettingsCreateError::BgpConfigNotFound,
)
)?
}
};
for (interface_name, peer_config) in &params.bgp_peers {
for p in &peer_config.peers {
use db::schema::bgp_config;
let bgp_config_id = match &p.bgp_config {
NameOrId::Id(id) => *id,
NameOrId::Name(name) => {
let name = name.to_string();
bgp_config_dsl::bgp_config
.filter(bgp_config::time_deleted.is_null())
.filter(bgp_config::name.eq(name))
.select(bgp_config::id)
.limit(1)
.first_async::<Uuid>(&conn)
.await
.map_err(|_|
TxnError::CustomError(
SwitchPortSettingsCreateError::BgpConfigNotFound,
)
)?
}
};

bgp_peer_config.push(SwitchPortBgpPeerConfig::new(
psid,
bgp_config_id,
interface_name.clone(),
p.addr.into(),
p.hold_time.into(),
p.idle_hold_time.into(),
p.delay_open.into(),
p.connect_retry.into(),
p.keepalive.into(),
));
bgp_peer_config.push(SwitchPortBgpPeerConfig::new(
psid,
bgp_config_id,
interface_name.clone(),
p.addr.into(),
p.hold_time.into(),
p.idle_hold_time.into(),
p.delay_open.into(),
p.connect_retry.into(),
p.keepalive.into(),
));

}
}
result.bgp_peers =
diesel::insert_into(
Expand Down Expand Up @@ -1153,8 +1155,8 @@ mod test {
use crate::db::datastore::{datastore_test, UpdatePrecondition};
use nexus_test_utils::db::test_setup_database;
use nexus_types::external_api::params::{
BgpAnnounceSetCreate, BgpConfigCreate, BgpPeerConfig, SwitchPortConfig,
SwitchPortGeometry, SwitchPortSettingsCreate,
BgpAnnounceSetCreate, BgpConfigCreate, BgpPeer, BgpPeerConfig,
SwitchPortConfig, SwitchPortGeometry, SwitchPortSettingsCreate,
};
use omicron_common::api::external::{
IdentityMetadataCreateParams, Name, NameOrId,
Expand Down Expand Up @@ -1218,19 +1220,21 @@ mod test {
bgp_peers: HashMap::from([(
"phy0".into(),
BgpPeerConfig {
bgp_announce_set: NameOrId::Name(
"test-announce-set".parse().unwrap(),
),
bgp_config: NameOrId::Name(
"test-bgp-config".parse().unwrap(),
),
interface_name: "qsfp0".into(),
addr: "192.168.1.1".parse().unwrap(),
hold_time: 0,
idle_hold_time: 0,
delay_open: 0,
connect_retry: 0,
keepalive: 0,
peers: vec![BgpPeer {
bgp_announce_set: NameOrId::Name(
"test-announce-set".parse().unwrap(),
),
bgp_config: NameOrId::Name(
"test-bgp-config".parse().unwrap(),
),
interface_name: "qsfp0".into(),
addr: "192.168.1.1".parse().unwrap(),
hold_time: 0,
idle_hold_time: 0,
delay_open: 0,
connect_retry: 0,
keepalive: 0,
}],
},
)]),
addresses: HashMap::new(),
Expand Down
138 changes: 134 additions & 4 deletions nexus/src/app/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ use nexus_db_queries::db::lookup::LookupPath;
use nexus_types::external_api::params::Address;
use nexus_types::external_api::params::AddressConfig;
use nexus_types::external_api::params::AddressLotBlockCreate;
use nexus_types::external_api::params::BgpAnnounceSetCreate;
use nexus_types::external_api::params::BgpAnnouncementCreate;
use nexus_types::external_api::params::BgpConfigCreate;
use nexus_types::external_api::params::BgpPeer;
use nexus_types::external_api::params::LinkConfig;
use nexus_types::external_api::params::LldpServiceConfig;
use nexus_types::external_api::params::RouteConfig;
use nexus_types::external_api::params::SwitchPortConfig;
use nexus_types::external_api::params::{
AddressLotCreate, LoopbackAddressCreate, Route, SiloCreate,
AddressLotCreate, BgpPeerConfig, LoopbackAddressCreate, Route, SiloCreate,
SwitchPortSettingsCreate,
};
use nexus_types::external_api::shared::Baseboard;
Expand All @@ -53,8 +57,8 @@ use sled_agent_client::types::EarlyNetworkConfigBody;
use sled_agent_client::types::StartSledAgentRequest;
use sled_agent_client::types::StartSledAgentRequestBody;
use sled_agent_client::types::{
BgpConfig, BgpPeerConfig, EarlyNetworkConfig, PortConfigV1,
RackNetworkConfigV1, RouteConfig as SledRouteConfig,
BgpConfig, BgpPeerConfig as SledBgpPeerConfig, EarlyNetworkConfig,
PortConfigV1, RackNetworkConfigV1, RouteConfig as SledRouteConfig,
};
use std::collections::BTreeMap;
use std::collections::BTreeSet;
Expand Down Expand Up @@ -408,6 +412,108 @@ impl super::Nexus {
Error::internal_error(&format!("unable to retrieve authz_address_lot for infra address_lot: {e}"))
})?;

let mut bgp_configs = HashMap::new();

for bgp_config in &rack_network_config.bgp {
bgp_configs.insert(bgp_config.asn, bgp_config.clone());

let bgp_config_name: Name =
format!("as{}", bgp_config.asn).parse().unwrap();

let announce_set_name: Name =
format!("as{}-announce", bgp_config.asn).parse().unwrap();

let address_lot_name: Name =
format!("as{}-lot", bgp_config.asn).parse().unwrap();

self.db_datastore
.address_lot_create(
&opctx,
&AddressLotCreate {
identity: IdentityMetadataCreateParams {
name: address_lot_name,
description: format!(
"Address lot for announce set in as {}",
bgp_config.asn
),
},
kind: AddressLotKind::Infra,
blocks: bgp_config
.originate
.iter()
.map(|o| AddressLotBlockCreate {
first_address: o.network().into(),
last_address: o.broadcast().into(),
})
.collect(),
},
)
.await
.map_err(|e| {
Error::internal_error(&format!(
"unable to create address lot for BGP as {}: {}",
bgp_config.asn, e
))
})?;

self.db_datastore
.bgp_create_announce_set(
&opctx,
&BgpAnnounceSetCreate {
identity: IdentityMetadataCreateParams {
name: announce_set_name.clone(),
description: format!(
"Announce set for AS {}",
bgp_config.asn
),
},
announcement: bgp_config
.originate
.iter()
.map(|x| BgpAnnouncementCreate {
address_lot_block: NameOrId::Name(
format!("as{}", bgp_config.asn)
.parse()
.unwrap(),
),
network: IpNetwork::from(*x).into(),
})
.collect(),
},
)
.await
.map_err(|e| {
Error::internal_error(&format!(
"unable to create bgp announce set for as {}: {}",
bgp_config.asn, e
))
})?;

self.db_datastore
.bgp_config_set(
&opctx,
&BgpConfigCreate {
identity: IdentityMetadataCreateParams {
name: bgp_config_name,
description: format!(
"BGP config for AS {}",
bgp_config.asn
),
},
asn: bgp_config.asn,
bgp_announce_set_id: announce_set_name.into(),
vrf: None,
},
)
.await
.map_err(|e| {
Error::internal_error(&format!(
"unable to set bgp config for as {}: {}",
bgp_config.asn, e
))
})?;
}

for (idx, uplink_config) in
rack_network_config.ports.iter().enumerate()
{
Expand Down Expand Up @@ -505,6 +611,30 @@ impl super::Nexus {
.routes
.insert("phy0".to_string(), RouteConfig { routes });

let peers: Vec<BgpPeer> = uplink_config
.bgp_peers
.iter()
.map(|r| BgpPeer {
bgp_announce_set: NameOrId::Name(
format!("as{}-announce", r.asn).parse().unwrap(), //TODO unwrap
),
bgp_config: NameOrId::Name(
format!("as{}", r.asn).parse().unwrap(), //TODO unwrap
),
interface_name: "phy0".into(),
addr: r.addr.into(),
hold_time: r.hold_time.unwrap_or(6) as u32,
idle_hold_time: r.idle_hold_time.unwrap_or(3) as u32,
delay_open: r.delay_open.unwrap_or(0) as u32,
connect_retry: r.connect_retry.unwrap_or(3) as u32,
keepalive: r.keepalive.unwrap_or(2) as u32,
})
.collect();

port_settings_params
.bgp_peers
.insert("phy0".to_string(), BgpPeerConfig { peers });

let link = LinkConfig {
mtu: 1500, //TODO as parameter
lldp: LldpServiceConfig {
Expand Down Expand Up @@ -673,7 +803,7 @@ impl super::Nexus {
addresses: info.addresses.iter().map(|a| a.address).collect(),
bgp_peers: peer_info
.iter()
.map(|(p, asn, addr)| BgpPeerConfig {
.map(|(p, asn, addr)| SledBgpPeerConfig {
addr: *addr,
asn: *asn,
port: port.port_name.clone(),
Expand Down
22 changes: 12 additions & 10 deletions nexus/tests/integration_tests/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use nexus_test_utils::http_testing::{AuthnMode, NexusRequest, RequestBuilder};
use nexus_test_utils_macros::nexus_test;
use nexus_types::external_api::params::{
Address, AddressConfig, AddressLotBlockCreate, AddressLotCreate,
BgpAnnounceSetCreate, BgpAnnouncementCreate, BgpConfigCreate,
BgpAnnounceSetCreate, BgpAnnouncementCreate, BgpConfigCreate, BgpPeer,
BgpPeerConfig, LinkConfig, LinkFec, LinkSpeed, LldpServiceConfig, Route,
RouteConfig, SwitchInterfaceConfig, SwitchInterfaceKind,
SwitchPortApplySettings, SwitchPortSettingsCreate,
Expand Down Expand Up @@ -253,15 +253,17 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) {
settings.bgp_peers.insert(
"phy0".into(),
BgpPeerConfig {
bgp_config: NameOrId::Name("as47".parse().unwrap()), //TODO
bgp_announce_set: NameOrId::Name("instances".parse().unwrap()), //TODO
interface_name: "phy0".to_string(),
addr: "1.2.3.4".parse().unwrap(),
hold_time: 6,
idle_hold_time: 6,
delay_open: 0,
connect_retry: 3,
keepalive: 2,
peers: vec![BgpPeer {
bgp_config: NameOrId::Name("as47".parse().unwrap()), //TODO
bgp_announce_set: NameOrId::Name("instances".parse().unwrap()), //TODO
interface_name: "phy0".to_string(),
addr: "1.2.3.4".parse().unwrap(),
hold_time: 6,
idle_hold_time: 6,
delay_open: 0,
connect_retry: 3,
keepalive: 2,
}],
},
);
let _created: SwitchPortSettingsView = NexusRequest::objects_post(
Expand Down
7 changes: 6 additions & 1 deletion nexus/types/src/external_api/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,12 +1511,17 @@ pub struct BgpConfigListSelector {
pub name_or_id: Option<NameOrId>,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct BgpPeerConfig {
pub peers: Vec<BgpPeer>,
}

/// A BGP peer configuration for an interface. Includes the set of announcements
/// that will be advertised to the peer identified by `addr`. The `bgp_config`
/// parameter is a reference to global BGP parameters. The `interface_name`
/// indicates what interface the peer should be contacted on.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct BgpPeerConfig {
pub struct BgpPeer {
/// The set of announcements advertised by the peer.
pub bgp_announce_set: NameOrId,

Expand Down
16 changes: 15 additions & 1 deletion openapi/nexus.json
Original file line number Diff line number Diff line change
Expand Up @@ -7816,7 +7816,7 @@
"switch"
]
},
"BgpPeerConfig": {
"BgpPeer": {
"description": "A BGP peer configuration for an interface. Includes the set of announcements that will be advertised to the peer identified by `addr`. The `bgp_config` parameter is a reference to global BGP parameters. The `interface_name` indicates what interface the peer should be contacted on.",
"type": "object",
"properties": {
Expand Down Expand Up @@ -7888,6 +7888,20 @@
"keepalive"
]
},
"BgpPeerConfig": {
"type": "object",
"properties": {
"peers": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BgpPeer"
}
}
},
"required": [
"peers"
]
},
"BgpPeerState": {
"description": "The current state of a BGP peer.",
"oneOf": [
Expand Down

0 comments on commit 4b7bb41

Please sign in to comment.