Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8eb09cf
stronger typed fields inside BgpPeer
jgallagher Mar 18, 2026
fc58fe8
minor cleanup
jgallagher Mar 19, 2026
e371a44
formatting
jgallagher Mar 19, 2026
6522e72
better error handling
jgallagher Mar 19, 2026
fd3432b
use RouterPeerAddress::UNNUMBERED_SENTINEL in db-queries
jgallagher Mar 19, 2026
49c4b17
remove BgpPeer::interface_name
jgallagher Mar 19, 2026
c47490a
cargo fmt
jgallagher Mar 21, 2026
e83acd5
openapi
jgallagher Mar 21, 2026
005e343
fixup imports
jgallagher Mar 21, 2026
c06e25f
more accurate doc comments
jgallagher Mar 21, 2026
da84490
extract common conversion function
jgallagher Mar 21, 2026
df057ef
use nonzero router_lifetime in crud test
jgallagher Mar 21, 2026
257e04e
more context in errors
jgallagher Mar 21, 2026
6456afc
add unit tests for bgp peer conversions
jgallagher Mar 21, 2026
3148ecf
comment cleanup
jgallagher Mar 23, 2026
115e693
add an unnumbered peer to test_bgp_boundary_switches()
jgallagher Mar 23, 2026
54e5afd
rustdoc
jgallagher Mar 23, 2026
56e7ddf
add missing variant after rebase
jgallagher Mar 24, 2026
3cad231
use RouterPeerType in more datastore methods
jgallagher Mar 24, 2026
a272c65
more context in error string
jgallagher Mar 24, 2026
4b78e23
comment wordsmithing
jgallagher Mar 24, 2026
aa7e38a
slightly more thorough tests
jgallagher Mar 24, 2026
b8bfac2
openapi
jgallagher Mar 24, 2026
9892fe9
remove stale doc comments
jgallagher Mar 24, 2026
0920892
cargo fmt
jgallagher Mar 24, 2026
82e71f7
Merge branch 'main' into john/stronger-unnumbered-types-2
jgallagher Mar 25, 2026
84af63d
Merge branch 'main' into john/stronger-unnumbered-types-2
jgallagher Apr 16, 2026
d4711ee
Merge branch 'main' into john/stronger-unnumbered-types-2
jgallagher Apr 17, 2026
bc2c71d
use `deprecated-field` for old BgpPeer interface name
jgallagher Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions nexus/db-model/src/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ use serde::{Deserialize, Serialize};
use sled_agent_types::early_networking::ImportExportPolicy;
use sled_agent_types::early_networking::PortFec;
use sled_agent_types::early_networking::PortSpeed;
use sled_agent_types::early_networking::RouterLifetimeConfig;
use sled_agent_types::early_networking::RouterPeerType;
use sled_agent_types::early_networking::SwitchSlot;
use std::net::IpAddr;
use uuid::Uuid;

impl_enum_type!(
Expand Down Expand Up @@ -784,12 +787,23 @@ impl SwitchPortBgpPeerConfig {
interface_name: Name,
p: &networking_types::BgpPeer,
) -> Self {
// Numbered peers are represented as a non-NULL `addr` and an arbitrary
// `router_lifetime` (we just use the default). Unnumbered are
// represented as a NULL `addr` and their desired `router_lifetime`.
let (addr, router_lifetime) = match p.addr {
RouterPeerType::Numbered { ip } => {
(Some(IpAddr::from(ip)), RouterLifetimeConfig::default())
}
RouterPeerType::Unnumbered { router_lifetime } => {
(None, router_lifetime)
}
};
Self {
id: Uuid::new_v4(),
port_settings_id,
bgp_config_id,
interface_name,
addr: p.addr.map(|a| a.into()),
addr: addr.map(From::from),
hold_time: p.hold_time.into(),
idle_hold_time: p.idle_hold_time.into(),
delay_open: p.delay_open.into(),
Expand All @@ -812,7 +826,7 @@ impl SwitchPortBgpPeerConfig {
_ => true,
},
vlan_id: p.vlan_id.map(|x| x.into()),
router_lifetime: p.router_lifetime.into(),
router_lifetime: router_lifetime.as_u16().into(),
}
}
}
Expand Down
130 changes: 64 additions & 66 deletions nexus/db-queries/src/db/datastore/bgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ use nexus_db_model::{
};
use nexus_types::external_api::networking;
use nexus_types::identity::Resource;
use omicron_common::api::external;
use omicron_common::api::external::http_pagination::PaginatedBy;
use omicron_common::api::external::{
CreateResult, DeleteResult, Error, ListResultVec, LookupResult, NameOrId,
ResourceType,
};
use ref_cast::RefCast;
use sled_agent_types::early_networking::RouterPeerType;
use sled_agent_types::early_networking::SwitchSlot;
use std::net::IpAddr;
use uuid::Uuid;
Expand Down Expand Up @@ -832,26 +834,22 @@ impl DataStore {
}

/// Look up communities for a BGP peer.
///
/// For numbered peers, pass `Some(addr)`. For unnumbered peers, pass `None`
/// (the function will query using the sentinel value 0.0.0.0/32).
pub async fn communities_for_peer(
&self,
opctx: &OpContext,
port_settings_id: Uuid,
interface_name: &str,
addr: Option<IpAddr>,
interface_name: &external::Name,
addr: RouterPeerType,
) -> ListResultVec<SwitchPortBgpPeerConfigCommunity> {
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config_communities::dsl;

// For unnumbered peers (addr is None), use UNSPECIFIED as sentinel
let db_addr: IpNetwork = addr
.unwrap_or_else(|| IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED))
.into();
// For unnumbered peers (addr is None), use sentinel value
let db_addr: IpNetwork =
addr.ip_squashing_unnumbered_to_sentinel().into();

let results = dsl::switch_port_settings_bgp_peer_config_communities
.filter(dsl::port_settings_id.eq(port_settings_id))
.filter(dsl::interface_name.eq(interface_name.to_owned()))
.filter(dsl::interface_name.eq(interface_name.to_string()))
.filter(dsl::addr.eq(db_addr))
.load_async(&*self.pool_connection_authorized(opctx).await?)
.await
Expand All @@ -865,25 +863,22 @@ impl DataStore {
}

/// Look up allowed exports for a BGP peer.
///
/// For numbered peers, pass `Some(addr)`. For unnumbered peers, pass `None`.
pub async fn allow_export_for_peer(
&self,
opctx: &OpContext,
port_settings_id: Uuid,
interface_name: &str,
addr: Option<IpAddr>,
interface_name: &external::Name,
addr: RouterPeerType,
) -> LookupResult<Option<Vec<SwitchPortBgpPeerConfigAllowExport>>> {
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config as db_peer;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config::dsl as peer_dsl;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config_allow_export as db_allow;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config_allow_export::dsl;

// For unnumbered peers (addr is None), use UNSPECIFIED as sentinel
// for the allow_export table (which has non-nullable addr)
let db_addr: IpNetwork = addr
.unwrap_or_else(|| IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED))
.into();
// For unnumbered peers (addr is None), use sentinel value for the
// allow_export table (which has non-nullable addr)
let db_addr: IpNetwork =
addr.ip_squashing_unnumbered_to_sentinel().into();

let conn = self.pool_connection_authorized(opctx).await?;
let err = OptionalError::new();
Expand All @@ -893,24 +888,27 @@ impl DataStore {
async move {
// Query the main peer config table. For unnumbered peers,
// addr is NULL; for numbered peers, addr matches.
let active = if let Some(addr) = addr {
let addr = IpNetwork::from(addr);
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.eq(addr))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
} else {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.is_null())
.filter(db_peer::interface_name.eq(interface_name.to_owned()))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
let active = match addr {
RouterPeerType::Numbered { ip } => {
let addr = IpNetwork::from(IpAddr::from(ip));
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.eq(addr))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
}
RouterPeerType::Unnumbered { .. } => {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.is_null())
.filter(db_peer::interface_name.eq(interface_name.to_string()))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
}
};

let active = active.map_err(|e| {
Expand All @@ -937,7 +935,7 @@ impl DataStore {
)
.filter(
db_allow::interface_name
.eq(interface_name.to_owned()),
.eq(interface_name.to_string()),
)
.filter(db_allow::addr.eq(db_addr))
.load_async(&conn)
Expand All @@ -960,25 +958,22 @@ impl DataStore {
}

/// Look up allowed imports for a BGP peer.
///
/// For numbered peers, pass `Some(addr)`. For unnumbered peers, pass `None`.
pub async fn allow_import_for_peer(
&self,
opctx: &OpContext,
port_settings_id: Uuid,
interface_name: &str,
addr: Option<IpAddr>,
interface_name: &external::Name,
addr: RouterPeerType,
) -> LookupResult<Option<Vec<SwitchPortBgpPeerConfigAllowImport>>> {
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config as db_peer;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config::dsl as peer_dsl;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config_allow_import as db_allow;
use nexus_db_schema::schema::switch_port_settings_bgp_peer_config_allow_import::dsl;

// For unnumbered peers (addr is None), use UNSPECIFIED as sentinel
// for the allow_import table (which has non-nullable addr)
let db_addr: IpNetwork = addr
.unwrap_or_else(|| IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED))
.into();
// For unnumbered peers (addr is None), use sentinel value for the
// allow_import table (which has non-nullable addr)
let db_addr: IpNetwork =
addr.ip_squashing_unnumbered_to_sentinel().into();

let err = OptionalError::new();
let conn = self.pool_connection_authorized(opctx).await?;
Expand All @@ -989,24 +984,27 @@ impl DataStore {
async move {
// Query the main peer config table. For unnumbered peers,
// addr is NULL; for numbered peers, addr matches.
let active = if let Some(addr) = addr {
let addr = IpNetwork::from(addr);
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.eq(addr))
.select(db_peer::allow_import_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
} else {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.is_null())
.filter(db_peer::interface_name.eq(interface_name.to_owned()))
.select(db_peer::allow_import_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
let active = match addr {
RouterPeerType::Numbered { ip } => {
let addr = IpNetwork::from(IpAddr::from(ip));
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.eq(addr))
.select(db_peer::allow_import_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
}
RouterPeerType::Unnumbered { .. } => {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.is_null())
.filter(db_peer::interface_name.eq(interface_name.to_string()))
.select(db_peer::allow_import_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
}
};

let active = active.map_err(|e| {
Expand All @@ -1033,7 +1031,7 @@ impl DataStore {
)
.filter(
db_allow::interface_name
.eq(interface_name.to_owned()),
.eq(interface_name.to_string()),
)
.filter(db_allow::addr.eq(db_addr))
.load_async(&conn)
Expand Down
Loading
Loading