Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions nexus/db-model/src/bgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use sled_agent_types::early_networking::ImportExportPolicy;
use sled_agent_types::early_networking::MaxPathConfig;
use sled_agent_types::early_networking::RouterLifetimeConfig;
use sled_agent_types::early_networking::RouterLifetimeConfigError;
use sled_agent_types::early_networking::RouterPeerIpAddr;
use sled_agent_types::early_networking::RouterPeerIpAddrError;
use sled_agent_types::early_networking::RouterPeerType;
use slog_error_chain::InlineErrorChain;
Expand Down Expand Up @@ -166,6 +167,7 @@ pub struct BgpPeerView {
pub enforce_first_as: bool,
pub vlan_id: Option<SqlU16>,
pub router_lifetime: SqlU16,
pub src_addr: Option<IpNetwork>,
}

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -213,6 +215,10 @@ impl TryFrom<BgpPeerView> for BgpPeerConfig {
allowed_export: ImportExportPolicy::NoFiltering,
allowed_import: ImportExportPolicy::NoFiltering,
vlan_id: value.vlan_id.map(|x| x.0),
src_addr: value
.src_addr
.map(|ip| RouterPeerIpAddr::try_from(ip.ip()))
.transpose()?,
})
}
}
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: Version = Version::new(261, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(262, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ pub static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(262, "bgp-peer-src-addr"),
KnownVersion::new(261, "remove-add-zones-with-mupdate-override"),
KnownVersion::new(260, "ereport-trim-serial-trailing-nulls"),
KnownVersion::new(259, "vmm-failure-reason"),
Expand Down
8 changes: 8 additions & 0 deletions nexus/db-model/src/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ pub struct SwitchPortBgpPeerConfig {
pub vlan_id: Option<SqlU16>,
pub id: Uuid,
router_lifetime: SqlU16,
src_addr: Option<IpNetwork>,
}

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -808,6 +809,11 @@ impl SwitchPortBgpPeerConfig {
pub fn raw_ip_in_db_repr(&self) -> Option<IpNetwork> {
self.addr
}

/// Get the local source address for BGP sessions with this peer, if set.
pub fn src_addr(&self) -> Option<std::net::IpAddr> {
self.src_addr.map(|n| n.ip())
}
}

#[derive(
Expand Down Expand Up @@ -968,6 +974,7 @@ impl SwitchPortBgpPeerConfig {
},
vlan_id: p.vlan_id.map(|x| x.into()),
router_lifetime: router_lifetime.as_u16().into(),
src_addr: p.src_addr.map(|ip| ip.into()),
}
}
}
Expand Down Expand Up @@ -1125,6 +1132,7 @@ mod tests {
allowed_import: ImportExportPolicy::NoFiltering,
allowed_export: ImportExportPolicy::NoFiltering,
vlan_id: None,
src_addr: None,
}
}

Expand Down
178 changes: 177 additions & 1 deletion nexus/db-queries/src/db/datastore/bgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ 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,
ResourceType, UpdateResult,
};
use ref_cast::RefCast;
use sled_agent_types::early_networking::RouterPeerType;
Expand Down Expand Up @@ -225,6 +225,181 @@ impl DataStore {
})
}

pub async fn bgp_config_update(
&self,
opctx: &OpContext,
sel: &networking::BgpConfigSelector,
update: &networking::BgpConfigUpdate,
) -> UpdateResult<BgpConfig> {
use nexus_db_schema::schema::bgp_config;
use nexus_db_schema::schema::bgp_config::dsl as bgp_config_dsl;
use nexus_db_schema::schema::{
bgp_announce_set, bgp_announce_set::dsl as announce_set_dsl,
};

let err = OptionalError::new();
let conn = self.pool_connection_authorized(opctx).await?;
self.transaction_retry_wrapper("bgp_config_update")
.transaction(&conn, |conn| {
let err = err.clone();
async move {
let name_or_id = sel.name_or_id.clone();

// Look up the existing config
let existing: BgpConfig = match name_or_id {
NameOrId::Id(id) => bgp_config_dsl::bgp_config
.filter(bgp_config::id.eq(id))
.filter(bgp_config::time_deleted.is_null())
.select(BgpConfig::as_select())
.limit(1)
.first_async::<BgpConfig>(&conn)
.await
.map_err(|e| {
let msg = "failed to lookup bgp config by id";
error!(opctx.log, "{msg}"; "error" => ?e);
match e {
diesel::result::Error::NotFound => err
.bail(Error::not_found_by_id(
ResourceType::BgpConfig,
&id,
)),
_ => err.bail(Error::internal_error(msg)),
}
})?,
NameOrId::Name(name) => bgp_config_dsl::bgp_config
.filter(bgp_config::name.eq(name.to_string()))
.filter(bgp_config::time_deleted.is_null())
.select(BgpConfig::as_select())
.limit(1)
.first_async::<BgpConfig>(&conn)
.await
.map_err(|e| {
let msg = "failed to lookup bgp config by name";
error!(opctx.log, "{msg}"; "error" => ?e);
match e {
diesel::result::Error::NotFound => err
.bail(Error::not_found_by_name(
ResourceType::BgpConfig,
&name,
)),
_ => err.bail(Error::internal_error(msg)),
}
})?,
};

// Resolve bgp_announce_set_id if an update was requested
let new_bgp_announce_set_id = match update
.bgp_announce_set_id
.clone()
{
None => existing.bgp_announce_set_id,
Some(NameOrId::Name(name)) => {
announce_set_dsl::bgp_announce_set
.filter(
bgp_announce_set::time_deleted.is_null(),
)
.filter(
bgp_announce_set::name.eq(name.to_string()),
)
.select(bgp_announce_set::id)
.limit(1)
.first_async::<Uuid>(&conn)
.await
.map_err(|e| {
let msg =
"failed to lookup announce set by name";
error!(opctx.log, "{msg}"; "error" => ?e);
match e {
diesel::result::Error::NotFound => err
.bail(Error::not_found_by_name(
ResourceType::BgpAnnounceSet,
&name,
)),
_ => {
err.bail(Error::internal_error(msg))
}
}
})?
}
Some(NameOrId::Id(id)) => {
announce_set_dsl::bgp_announce_set
.filter(
bgp_announce_set::time_deleted.is_null(),
)
.filter(bgp_announce_set::id.eq(id))
.select(bgp_announce_set::id)
.limit(1)
.first_async::<Uuid>(&conn)
.await
.map_err(|e| {
let msg =
"failed to lookup announce set by id";
error!(opctx.log, "{msg}"; "error" => ?e);
match e {
diesel::result::Error::NotFound => err
.bail(Error::not_found_by_id(
ResourceType::BgpAnnounceSet,
&id,
)),
_ => {
err.bail(Error::internal_error(msg))
}
}
})?
}
};

let new_name = update
.name
.as_ref()
.map(|n| n.to_string())
.unwrap_or_else(|| existing.name().to_string());
let new_description = update
.description
.clone()
.unwrap_or_else(|| existing.description().to_string());
let new_max_paths = update
.max_paths
.map(|m| m.as_u8())
.unwrap_or(*existing.max_paths);

diesel::update(bgp_config_dsl::bgp_config)
.filter(bgp_config_dsl::id.eq(existing.id()))
.set((
bgp_config_dsl::time_modified.eq(Utc::now()),
bgp_config_dsl::name.eq(new_name),
bgp_config_dsl::description.eq(new_description),
bgp_config_dsl::bgp_announce_set_id
.eq(new_bgp_announce_set_id),
bgp_config_dsl::max_paths
.eq(i16::from(new_max_paths)),
))
.returning(BgpConfig::as_returning())
.get_result_async(&conn)
.await
.map_err(|e| {
let msg = "bgp_config_update failed";
error!(opctx.log, "{msg}"; "error" => ?e);
err.bail(public_error_from_diesel(
e,
ErrorHandler::Server,
))
})
}
})
.await
.map_err(|e| {
let msg = "bgp_config_update failed";
if let Some(err) = err.take() {
error!(opctx.log, "{msg}"; "error" => ?err);
err
} else {
error!(opctx.log, "{msg}"; "error" => ?e);
public_error_from_diesel(e, ErrorHandler::Server)
}
})
}

pub async fn bgp_config_delete(
&self,
opctx: &OpContext,
Expand Down Expand Up @@ -1254,6 +1429,7 @@ mod tests {
allowed_import,
allowed_export,
vlan_id: None,
src_addr: None,
}
}

Expand Down
13 changes: 13 additions & 0 deletions nexus/db-queries/src/db/datastore/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use omicron_common::api::external::{
use ref_cast::RefCast;
use serde::{Deserialize, Serialize};
use sled_agent_types::early_networking::ImportExportPolicy;
use sled_agent_types::early_networking::RouterPeerIpAddr;
use sled_agent_types::early_networking::RouterPeerType;
use sled_agent_types::early_networking::SwitchSlot;
use slog_error_chain::InlineErrorChain;
Expand Down Expand Up @@ -138,6 +139,15 @@ impl BgpPeerFromDbBuilder<'_> {
communities,
allowed_import,
allowed_export,
src_addr: p
.src_addr()
.map(RouterPeerIpAddr::try_from)
.transpose()
.map_err(|err| {
E::internal_error(
InlineErrorChain::new(&err).to_string(),
)
})?,
},
})
}
Expand Down Expand Up @@ -2160,6 +2170,7 @@ mod test {
]),
allowed_import: ImportExportPolicy::NoFiltering,
vlan_id: None,
src_addr: None,
},
BgpPeer {
bgp_config: bgp_config.identity.name.clone().into(),
Expand All @@ -2185,6 +2196,7 @@ mod test {
"192.168.3.0/24".parse().unwrap(),
]),
vlan_id: None,
src_addr: None,
},
],
}],
Expand Down Expand Up @@ -2526,6 +2538,7 @@ mod test {
]),
allowed_import: ImportExportPolicy::NoFiltering,
vlan_id: None,
src_addr: None,
};

let peer_phy1 = BgpPeer {
Expand Down
2 changes: 2 additions & 0 deletions nexus/db-schema/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ table! {
vlan_id -> Nullable<Int4>,
id -> Uuid,
router_lifetime -> Int4,
src_addr -> Nullable<Inet>,
}
}

Expand Down Expand Up @@ -340,6 +341,7 @@ table! {
vlan_id -> Nullable<Int4>,
router_lifetime -> Int4,
asn -> Int8,
src_addr -> Nullable<Inet>,
}
}

Expand Down
1 change: 1 addition & 0 deletions nexus/external-api/output/nexus_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ networking_bgp_announcement_list GET /v1/system/networking/bgp-anno
networking_bgp_config_create POST /v1/system/networking/bgp
networking_bgp_config_delete DELETE /v1/system/networking/bgp
networking_bgp_config_list GET /v1/system/networking/bgp
networking_bgp_config_update PUT /v1/system/networking/bgp
networking_bgp_exported GET /v1/system/networking/bgp-exported
networking_bgp_imported GET /v1/system/networking/bgp-imported
networking_bgp_message_history GET /v1/system/networking/bgp-message-history
Expand Down
Loading