Skip to content

Commit

Permalink
refactor address lot APIs to resemble ip pools
Browse files Browse the repository at this point in the history
  • Loading branch information
internet-diglett committed Jul 8, 2024
1 parent 65b25dc commit 7bccb1b
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 122 deletions.
82 changes: 78 additions & 4 deletions nexus/db-queries/src/db/datastore/address_lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ use crate::db::pagination::paginated;
use crate::transaction_retry::OptionalError;
use async_bb8_diesel::{AsyncRunQueryDsl, Connection};
use chrono::Utc;
use diesel::pg::sql_types;
use diesel::IntoSql;
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,
Expand Down Expand Up @@ -205,7 +202,7 @@ impl DataStore {
&self,
opctx: &OpContext,
address_lot_id: Uuid,
params: params::AddressLotBlockCreate,
params: params::AddressLotBlock,
) -> CreateResult<AddressLotBlock> {
use db::schema::address_lot_block::dsl;

Expand Down Expand Up @@ -264,6 +261,83 @@ impl DataStore {
})
}

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
15 changes: 9 additions & 6 deletions nexus/src/app/address_lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl super::Nexus {
self: &Arc<Self>,
opctx: &OpContext,
address_lot_id: Uuid,
block: params::AddressLotBlockCreate,
block: params::AddressLotBlock,
) -> CreateResult<AddressLotBlock> {
opctx.authorize(authz::Action::CreateChild, &authz::FLEET).await?;
validate_block(&block)?;
Expand All @@ -84,11 +84,14 @@ impl super::Nexus {
pub(crate) async fn address_lot_block_delete(
self: &Arc<Self>,
opctx: &OpContext,
address_lot: &lookup::AddressLot<'_>,
block: params::AddressLotBlockCreate,
address_lot_id: Uuid,
block: params::AddressLotBlock,
) -> DeleteResult {
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
todo!("delete address lot block")
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(
Expand All @@ -105,7 +108,7 @@ impl super::Nexus {
}
}

fn validate_block(block: &params::AddressLotBlockCreate) -> Result<(), Error> {
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)?
Expand Down
86 changes: 66 additions & 20 deletions nexus/src/app/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use nexus_types::deployment::CockroachDbClusterVersion;
use nexus_types::deployment::SledFilter;
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::AddressLotBlock;
use nexus_types::external_api::params::BgpAnnounceSetCreate;
use nexus_types::external_api::params::BgpAnnouncementCreate;
use nexus_types::external_api::params::BgpConfigCreate;
Expand All @@ -47,6 +47,7 @@ use nexus_types::external_api::shared::SiloIdentityMode;
use nexus_types::external_api::shared::SiloRole;
use nexus_types::external_api::shared::UninitializedSled;
use nexus_types::external_api::views;
use nexus_types::identity::Resource;
use nexus_types::internal_api::params::DnsRecord;
use omicron_common::address::{get_64_subnet, Ipv6Subnet, RACK_PREFIX};
use omicron_common::api::external::AddressLotKind;
Expand Down Expand Up @@ -378,26 +379,46 @@ impl super::Nexus {

let first_address = IpAddr::V4(rack_network_config.infra_ip_first);
let last_address = IpAddr::V4(rack_network_config.infra_ip_last);
let ipv4_block = AddressLotBlockCreate { first_address, last_address };

let blocks = vec![ipv4_block];
let ipv4_block = AddressLotBlock { first_address, last_address };

let address_lot_params = AddressLotCreate { identity, kind };

match self
let address_lot_id = match self
.db_datastore
.address_lot_create(opctx, &address_lot_params)
.await
{
Ok(_) => Ok(()),
Ok(v) => Ok(v.id()),
Err(e) => match e {
Error::ObjectAlreadyExists { type_name: _, object_name: _ } => {
Ok(())
let address_lot_lookup = self.address_lot_lookup(
&opctx,
NameOrId::Name(address_lot_name.clone()),
)?;

let (.., authz_address_lot) = address_lot_lookup
.lookup_for(authz::Action::CreateChild)
.await?;
Ok(authz_address_lot.id())
}
_ => Err(e),
},
}?;

match self
.db_datastore
.address_lot_block_create(opctx, address_lot_id, ipv4_block.clone())
.await
{
Ok(_) => Ok(()),
Err(e) => match e {
Error::ObjectAlreadyExists { .. } => Ok(()),
_ => Err(Error::internal_error(&format!(
"unable to create block for address lot {address_lot_id}: {e}",
))),
},
}?;

let mut bgp_configs = HashMap::new();

for bgp_config in &rack_network_config.bgp {
Expand All @@ -412,42 +433,67 @@ impl super::Nexus {
let address_lot_name: Name =
format!("as{}-lot", bgp_config.asn).parse().unwrap();

match self
let address_lot_id = match self
.db_datastore
.address_lot_create(
&opctx,
&AddressLotCreate {
identity: IdentityMetadataCreateParams {
name: address_lot_name,
name: address_lot_name.clone(),
description: format!(
"Address lot for announce set in as {}",
bgp_config.asn
),
},
kind: AddressLotKind::Infra,
// TODO: Levon - Move to new creation logic
// blocks: bgp_config
// .originate
// .iter()
// .map(|o| AddressLotBlockCreate {
// first_address: o.first_addr().into(),
// last_address: o.last_addr().into(),
// })
// .collect(),
},
)
.await
{
Ok(_) => Ok(()),
Ok(v) => Ok(v.id()),
Err(e) => match e {
Error::ObjectAlreadyExists { .. } => Ok(()),
Error::ObjectAlreadyExists { .. } => {
let address_lot_lookup = self.address_lot_lookup(
&opctx,
NameOrId::Name(address_lot_name),
)?;

let (.., authz_address_lot) = address_lot_lookup
.lookup_for(authz::Action::CreateChild)
.await?;
Ok(authz_address_lot.id())
}
_ => Err(Error::internal_error(&format!(
"unable to create address lot for BGP as {}: {e}",
bgp_config.asn
))),
},
}?;

for net in &bgp_config.originate {
match self
.db_datastore
.address_lot_block_create(
&opctx,
address_lot_id,
AddressLotBlock {
first_address: net.first_addr().into(),
last_address: net.last_addr().into(),
},
)
.await
{
Ok(_) => Ok(()),
Err(e) => match e {
Error::ObjectAlreadyExists { .. } => Ok(()),
_ => Err(Error::internal_error(&format!(
"unable to create address lot block for BGP as {}: {e}",
bgp_config.asn
))),
},
}?;
}

match self
.db_datastore
.bgp_create_announce_set(
Expand Down
36 changes: 19 additions & 17 deletions nexus/src/external_api/http_entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ pub(crate) fn external_api() -> NexusApiDescription {
api.register(networking_address_lot_create)?;
api.register(networking_address_lot_delete)?;

// TODO: Levon - Operator-Accessible Address Lot Block API
api.register(networking_address_lot_block_list)?;
api.register(networking_address_lot_block_add)?;
api.register(networking_address_lot_block_remove)?;
Expand Down Expand Up @@ -3500,13 +3499,13 @@ async fn networking_address_lot_list(
/// Add block to address lot
#[endpoint {
method = POST,
path = "/v1/system/networking/address-lot/{address_lot}/blocks",
path = "/v1/system/networking/address-lot/{address_lot}/blocks/add",
tags = ["system/networking"],
}]
async fn networking_address_lot_block_add(
rqctx: RequestContext<ApiContext>,
path_params: Path<params::AddressLotPath>,
block: TypedBody<params::AddressLotBlockCreate>,
block: TypedBody<params::AddressLotBlock>,
) -> Result<HttpResponseCreated<AddressLotBlock>, HttpError> {
let apictx = rqctx.context();
let handler = async {
Expand Down Expand Up @@ -3536,25 +3535,17 @@ async fn networking_address_lot_block_add(
.await
}

#[derive(Serialize, Deserialize, JsonSchema)]
pub struct AddressLotBlockPath {
/// The address lot the block belongs to
address_lot: NameOrId,

/// The block to delete from the address lot
block: NameOrId,
}

/// Remove block from address lot
#[endpoint {
method = DELETE,
path = "/v1/system/networking/address-lot/{address_lot}/blocks/{block}",
method = POST,
path = "/v1/system/networking/address-lot/{address_lot}/blocks/remove",
tags = ["system/networking"],
}]
async fn networking_address_lot_block_remove(
rqctx: RequestContext<ApiContext>,
path_params: Path<AddressLotBlockPath>,
) -> Result<HttpResponseOk<ResultsPage<AddressLotBlock>>, HttpError> {
path_params: Path<params::AddressLotPath>,
block: TypedBody<params::AddressLotBlock>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let apictx = rqctx.context();
let handler = async {
let nexus = &apictx.context.nexus;
Expand All @@ -3563,7 +3554,18 @@ async fn networking_address_lot_block_remove(
let address_lot_lookup =
nexus.address_lot_lookup(&opctx, path.address_lot)?;

todo!("implement address lot block remove logic")
let (.., authz_address_lot) =
address_lot_lookup.lookup_for(authz::Action::CreateChild).await?;

nexus
.address_lot_block_delete(
&opctx,
authz_address_lot.id(),
block.into_inner(),
)
.await?;

Ok(HttpResponseUpdatedNoContent())
};
apictx
.context
Expand Down
Loading

0 comments on commit 7bccb1b

Please sign in to comment.