Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Network Operator APIs #6016

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
cd6c856
WIP: refactor of operator api networking endpoints
Jun 18, 2024
c5f042b
Merge branch 'main' into refactor-networking-apis
internet-diglett Jul 4, 2024
65b25dc
fix address lot testing
internet-diglett Jul 4, 2024
7bccb1b
refactor address lot APIs to resemble ip pools
internet-diglett Jul 8, 2024
812e991
rename AddressLotBlock params
internet-diglett Jul 17, 2024
85abe0e
Merge branch 'main' into refactor-networking-apis
internet-diglett Jul 17, 2024
047c6fe
WIP: allow partial update of switch port configuration
internet-diglett Jul 23, 2024
8a807d9
fixup! WIP: allow partial update of switch port configuration
internet-diglett Jul 29, 2024
78033b5
plumb through switch port geometry
internet-diglett Jul 31, 2024
d8c2f42
fixup! plumb through switch port geometry
internet-diglett Jul 31, 2024
bca07fe
list links
internet-diglett Aug 1, 2024
30c217c
return structured data for geometry
internet-diglett Aug 1, 2024
7000904
plumb through switch port link configuration
internet-diglett Aug 6, 2024
1c695d0
WIP: plumb switch port link create
internet-diglett Aug 6, 2024
ba50716
create / delete link config
internet-diglett Aug 9, 2024
d5b5553
Merge branch 'main' into refactor-networking-apis-2
internet-diglett Aug 27, 2024
6eb4be4
WIP: interface address management
internet-diglett Aug 27, 2024
69ff1f0
WIP: interface address management
internet-diglett Aug 29, 2024
2e392d2
WIP: more refactor work
internet-diglett Aug 29, 2024
8cad50e
Make addresses list-able without knowing interface name
internet-diglett Aug 29, 2024
7a32cbd
regen openapi
internet-diglett Aug 29, 2024
2377850
fix address remove logic
internet-diglett Aug 30, 2024
8416cac
WIP: route add / remove
internet-diglett Aug 30, 2024
d991823
WIP: route add / remove
internet-diglett Aug 30, 2024
de92b30
WIP: route add / remove / list
internet-diglett Aug 30, 2024
22fe92a
add bgp peer list / add / remove
internet-diglett Aug 30, 2024
193bcaf
don't require entire peer config for peer removal
internet-diglett Aug 30, 2024
1360445
WIP: bgp import/export/community add and list
internet-diglett Aug 30, 2024
a392a80
WIP: finish roughing out bgp import/export/community endpoints
internet-diglett Aug 30, 2024
a1a70fd
WIP: breakout bgp peer import/export/communities
internet-diglett Aug 31, 2024
b1cbebe
WIP unsquash switch port query errors
internet-diglett Sep 10, 2024
822953e
WIP: update tests
internet-diglett Oct 15, 2024
9ef3456
add integration tests for new endpoints
internet-diglett Oct 16, 2024
f65f6f9
change const to static in test fixtures
internet-diglett Oct 16, 2024
2225b77
Round 1 of PR review fixes
internet-diglett Oct 18, 2024
155e5d4
Round 2 of PR review fixes
internet-diglett Oct 19, 2024
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
3 changes: 0 additions & 3 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2041,9 +2041,6 @@ impl std::fmt::Display for Digest {
pub struct AddressLotCreateResponse {
/// The address lot that was created.
pub lot: AddressLot,

/// The address lot blocks that were created.
pub blocks: Vec<AddressLotBlock>,
}

/// Represents an address lot object, containing the id of the lot that can be
Expand Down
209 changes: 142 additions & 67 deletions nexus/db-queries/src/db/datastore/address_lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,21 @@ use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
use diesel_dtrace::DTraceConnection;
use ipnetwork::IpNetwork;
use nexus_types::external_api::params;
use nexus_types::identity::Resource;
use omicron_common::api::external::http_pagination::PaginatedBy;
use omicron_common::api::external::{
CreateResult, DataPageParams, DeleteResult, Error, ListResultVec,
LookupResult, ResourceType,
};
use ref_cast::RefCast;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AddressLotCreateResult {
pub lot: AddressLot,
pub blocks: Vec<AddressLotBlock>,
}

impl DataStore {
pub async fn address_lot_create(
&self,
opctx: &OpContext,
params: &params::AddressLotCreate,
) -> CreateResult<AddressLotCreateResult> {
) -> CreateResult<AddressLot> {
use db::schema::address_lot::dsl as lot_dsl;
use db::schema::address_lot_block::dsl as block_dsl;

let conn = self.pool_connection_authorized(opctx).await?;

Expand Down Expand Up @@ -81,63 +72,7 @@ impl DataStore {
}
};

let desired_blocks: Vec<AddressLotBlock> = params
.blocks
.iter()
.map(|b| {
AddressLotBlock::new(
db_lot.id(),
b.first_address.into(),
b.last_address.into(),
)
})
.collect();

let found_blocks: Vec<AddressLotBlock> =
block_dsl::address_lot_block
.filter(block_dsl::address_lot_id.eq(db_lot.id()))
.filter(
block_dsl::first_address.eq_any(
desired_blocks
.iter()
.map(|b| b.first_address)
.collect::<Vec<_>>(),
),
)
.filter(
block_dsl::last_address.eq_any(
desired_blocks
.iter()
.map(|b| b.last_address)
.collect::<Vec<_>>(),
),
)
.get_results_async(&conn)
.await?;

let mut blocks = vec![];

// If the block is found in the database, use the found block.
// If the block is not found in the database, insert it.
for desired_block in desired_blocks {
let block = match found_blocks.iter().find(|db_b| {
db_b.first_address == desired_block.first_address
&& db_b.last_address == desired_block.last_address
}) {
Some(block) => block.clone(),
None => {
diesel::insert_into(block_dsl::address_lot_block)
.values(desired_block)
.returning(AddressLotBlock::as_returning())
.get_results_async(&conn)
.await?[0]
.clone()
}
};
blocks.push(block);
}

Ok(AddressLotCreateResult { lot: db_lot, blocks })
Ok(db_lot)
})
.await
.map_err(|e| {
Expand Down Expand Up @@ -263,6 +198,146 @@ impl DataStore {
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
}

pub async fn address_lot_block_create(
&self,
opctx: &OpContext,
address_lot_id: Uuid,
params: params::AddressLotBlock,
) -> CreateResult<AddressLotBlock> {
use db::schema::address_lot_block::dsl;

let conn = self.pool_connection_authorized(opctx).await?;

self.transaction_retry_wrapper("address_lot_create")
.transaction(&conn, |conn| async move {
let found_block: Option<AddressLotBlock> =
dsl::address_lot_block
.filter(dsl::address_lot_id.eq(address_lot_id))
.filter(
dsl::first_address
.eq(IpNetwork::from(params.first_address)),
)
.filter(
dsl::last_address
.eq(IpNetwork::from(params.last_address)),
)
.select(AddressLotBlock::as_select())
.limit(1)
.first_async(&conn)
.await
.ok();

let new_block = AddressLotBlock::new(
address_lot_id,
IpNetwork::from(params.first_address),
IpNetwork::from(params.last_address),
);

let db_block = match found_block {
Some(v) => v,
None => {
diesel::insert_into(dsl::address_lot_block)
.values(new_block)
.returning(AddressLotBlock::as_returning())
.get_result_async(&conn)
.await?
}
};

Ok(db_block)
})
.await
.map_err(|e| {
public_error_from_diesel(
e,
ErrorHandler::Conflict(
ResourceType::AddressLotBlock,
&format!(
"block covering range {} - {}",
params.first_address, params.last_address
),
),
)
})
internet-diglett marked this conversation as resolved.
Show resolved Hide resolved
}

pub async fn address_lot_block_delete(
&self,
opctx: &OpContext,
address_lot_id: Uuid,
params: params::AddressLotBlock,
) -> DeleteResult {
use db::schema::address_lot_block::dsl;
use db::schema::address_lot_rsvd_block::dsl as rsvd_block_dsl;

#[derive(Debug)]
enum AddressLotBlockDeleteError {
BlockInUse,
}

let conn = self.pool_connection_authorized(opctx).await?;

let err = OptionalError::new();

self.transaction_retry_wrapper("address_lot_delete")
.transaction(&conn, |conn| {
let err = err.clone();
async move {
let rsvd: Vec<AddressLotReservedBlock> =
rsvd_block_dsl::address_lot_rsvd_block
.filter(
rsvd_block_dsl::address_lot_id
.eq(address_lot_id),
)
.filter(
rsvd_block_dsl::first_address
.eq(IpNetwork::from(params.first_address)),
)
.filter(
rsvd_block_dsl::last_address
.eq(IpNetwork::from(params.last_address)),
)
.select(AddressLotReservedBlock::as_select())
.limit(1)
.load_async(&conn)
.await?;

if !rsvd.is_empty() {
return Err(
err.bail(AddressLotBlockDeleteError::BlockInUse)
);
}

diesel::delete(dsl::address_lot_block)
.filter(dsl::address_lot_id.eq(address_lot_id))
.filter(
dsl::first_address
.eq(IpNetwork::from(params.first_address)),
)
.filter(
dsl::last_address
.eq(IpNetwork::from(params.last_address)),
)
.execute_async(&conn)
.await?;

Ok(())
}
})
.await
.map_err(|e| {
if let Some(err) = err.take() {
match err {
AddressLotBlockDeleteError::BlockInUse => {
Error::invalid_request("block is in use")
}
}
} else {
public_error_from_diesel(e, ErrorHandler::Server)
}
})
}

pub async fn address_lot_id_for_block_id(
&self,
opctx: &OpContext,
Expand Down
1 change: 0 additions & 1 deletion nexus/db-queries/src/db/datastore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ mod volume;
mod vpc;
mod zpool;

pub use address_lot::AddressLotCreateResult;
pub use dns::DataStoreDnsTest;
pub use dns::DnsVersionUpdateBuilder;
pub use instance::InstanceAndActiveVmm;
Expand Down
56 changes: 39 additions & 17 deletions nexus/src/app/address_lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use db::model::{AddressLot, AddressLotBlock};
use nexus_db_queries::authz;
use nexus_db_queries::context::OpContext;
use nexus_db_queries::db;
use nexus_db_queries::db::datastore::AddressLotCreateResult;
use nexus_db_queries::db::lookup;
use nexus_db_queries::db::lookup::LookupPath;
use omicron_common::api::external::http_pagination::PaginatedBy;
Expand Down Expand Up @@ -45,9 +44,8 @@ impl super::Nexus {
self: &Arc<Self>,
opctx: &OpContext,
params: params::AddressLotCreate,
) -> CreateResult<AddressLotCreateResult> {
) -> CreateResult<AddressLot> {
opctx.authorize(authz::Action::CreateChild, &authz::FLEET).await?;
validate_blocks(&params)?;
self.db_datastore.address_lot_create(opctx, &params).await
}

Expand All @@ -70,6 +68,32 @@ impl super::Nexus {
self.db_datastore.address_lot_list(opctx, pagparams).await
}

pub(crate) async fn address_lot_block_create(
self: &Arc<Self>,
opctx: &OpContext,
address_lot_id: Uuid,
block: params::AddressLotBlock,
) -> CreateResult<AddressLotBlock> {
opctx.authorize(authz::Action::CreateChild, &authz::FLEET).await?;
validate_block(&block)?;
self.db_datastore
.address_lot_block_create(opctx, address_lot_id, block)
.await
}

pub(crate) async fn address_lot_block_delete(
self: &Arc<Self>,
opctx: &OpContext,
address_lot_id: Uuid,
block: params::AddressLotBlock,
) -> DeleteResult {
opctx.authorize(authz::Action::Delete, &authz::FLEET).await?;
validate_block(&block)?;
self.db_datastore
.address_lot_block_delete(opctx, address_lot_id, block)
.await
}

pub(crate) async fn address_lot_block_list(
self: &Arc<Self>,
opctx: &OpContext,
Expand All @@ -84,20 +108,18 @@ impl super::Nexus {
}
}

fn validate_blocks(lot: &params::AddressLotCreate) -> Result<(), Error> {
for b in &lot.blocks {
match (&b.first_address, &b.last_address) {
(IpAddr::V4(first), IpAddr::V4(last)) => {
validate_v4_block(first, last)?
}
(IpAddr::V6(first), IpAddr::V6(last)) => {
validate_v6_block(first, last)?
}
_ => {
return Err(Error::invalid_request(
"Block bounds must be in same address family",
));
}
fn validate_block(block: &params::AddressLotBlock) -> Result<(), Error> {
match (&block.first_address, &block.last_address) {
(IpAddr::V4(first), IpAddr::V4(last)) => {
validate_v4_block(first, last)?
}
(IpAddr::V6(first), IpAddr::V6(last)) => {
validate_v6_block(first, last)?
}
_ => {
return Err(Error::invalid_request(
"Block bounds must be in same address family",
));
}
}
Ok(())
Expand Down
Loading
Loading