Skip to content
Open
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
11 changes: 6 additions & 5 deletions end-to-end-tests/src/bin/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use end_to_end_tests::helpers::{
use omicron_test_utils::dev::poll::{CondCheckError, wait_for_condition};
use oxide_client::types::{
ByteCount, DeviceAccessTokenRequest, DeviceAuthRequest, DeviceAuthVerify,
DiskCreate, DiskSource, IpPoolCreate, IpPoolLinkSilo, IpPoolType,
IpVersion, NameOrId, SiloQuotasUpdate,
DiskCreate, DiskSource, IpPoolCreate, IpPoolLinkSilo,
IpPoolReservationType, IpPoolType, IpVersion, NameOrId, SiloQuotasUpdate,
};
use oxide_client::{
ClientConsoleAuthExt, ClientDisksExt, ClientProjectsExt,
Expand Down Expand Up @@ -48,17 +48,18 @@ async fn run_test() -> Result<()> {
eprintln!("creating IP{} IP pool... {:?} - {:?}", ip_version, first, last);
let pool_name = "default";
client
.ip_pool_create()
.system_ip_pool_create()
.body(IpPoolCreate {
name: pool_name.parse().unwrap(),
description: "Default IP pool".to_string(),
ip_version,
pool_type: IpPoolType::Unicast,
reservation_type: IpPoolReservationType::ExternalSilos,
})
.send()
.await?;
client
.ip_pool_silo_link()
.system_ip_pool_silo_link()
.pool(pool_name)
.body(IpPoolLinkSilo {
silo: NameOrId::Name(params.silo_name().parse().unwrap()),
Expand All @@ -67,7 +68,7 @@ async fn run_test() -> Result<()> {
.send()
.await?;
client
.ip_pool_range_add()
.system_ip_pool_range_add()
.pool(pool_name)
.body(try_create_ip_range(first, last)?)
.send()
Expand Down
81 changes: 41 additions & 40 deletions end-to-end-tests/src/bin/commtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use oxide_client::{
ClientSystemHardwareExt, ClientSystemIpPoolsExt, ClientSystemStatusExt,
ClientVpcsExt,
types::{
IpPoolCreate, IpPoolLinkSilo, IpPoolType, IpRange, IpVersion, Name,
NameOrId, PingStatus, ProbeCreate, ProbeInfo, ProjectCreate,
UsernamePasswordCredentials,
IpPoolCreate, IpPoolLinkSilo, IpPoolReservationType, IpPoolType,
IpRange, IpVersion, Name, NameOrId, PingStatus, ProbeCreate, ProbeInfo,
ProjectCreate, UsernamePasswordCredentials,
},
};
use std::{
Expand Down Expand Up @@ -280,48 +280,49 @@ async fn rack_prepare(
})?;

let pool_name = "default";
api_retry!(
if let Err(e) = oxide.ip_pool_view().pool("default").send().await {
if let Some(reqwest::StatusCode::NOT_FOUND) = e.status() {
print!("default ip pool does not exist, creating ...");
let ip_version = if args.ip_pool_begin.is_ipv4() {
IpVersion::V4
} else {
IpVersion::V6
};
oxide
.ip_pool_create()
.body(IpPoolCreate {
name: pool_name.parse().unwrap(),
description: "Default IP pool".to_string(),
ip_version,
pool_type: IpPoolType::Unicast,
})
.send()
.await?;
oxide
.ip_pool_silo_link()
.pool(pool_name)
.body(IpPoolLinkSilo {
silo: NameOrId::Name("recovery".parse().unwrap()),
is_default: true,
})
.send()
.await?;
println!("done");
Ok(())
api_retry!(if let Err(e) =
oxide.system_ip_pool_view().pool("default").send().await
{
if let Some(reqwest::StatusCode::NOT_FOUND) = e.status() {
print!("default ip pool does not exist, creating ...");
let ip_version = if args.ip_pool_begin.is_ipv4() {
IpVersion::V4
} else {
Err(e)
}
} else {
println!("default ip pool already exists");
IpVersion::V6
};
oxide
.system_ip_pool_create()
.body(IpPoolCreate {
name: pool_name.parse().unwrap(),
description: "Default IP pool".to_string(),
ip_version,
pool_type: IpPoolType::Unicast,
reservation_type: IpPoolReservationType::ExternalSilos,
})
.send()
.await?;
oxide
.system_ip_pool_silo_link()
.pool(pool_name)
.body(IpPoolLinkSilo {
silo: NameOrId::Name("recovery".parse().unwrap()),
is_default: true,
})
.send()
.await?;
println!("done");
Ok(())
} else {
Err(e)
}
)?;
} else {
println!("default ip pool already exists");
Ok(())
})?;

let pool = api_retry!(
oxide
.ip_pool_range_list()
.system_ip_pool_range_list()
.limit(u32::MAX)
.pool(Name::try_from("default").unwrap())
.send()
Expand All @@ -346,7 +347,7 @@ async fn rack_prepare(
print!("ip range does not exist, creating ... ");
api_retry!(
oxide
.ip_pool_range_add()
.system_ip_pool_range_add()
.pool(Name::try_from("default").unwrap())
.body(range.clone())
.send()
Expand Down
28 changes: 22 additions & 6 deletions nexus/db-model/src/ip_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ impl ::std::fmt::Display for IpPoolReservationType {
}
}

impl From<shared::IpPoolReservationType> for IpPoolReservationType {
fn from(value: shared::IpPoolReservationType) -> Self {
match value {
shared::IpPoolReservationType::ExternalSilos => Self::ExternalSilos,
shared::IpPoolReservationType::OxideInternal => Self::OxideInternal,
}
}
}

impl From<IpPoolReservationType> for shared::IpPoolReservationType {
fn from(value: IpPoolReservationType) -> Self {
match value {
IpPoolReservationType::ExternalSilos => Self::ExternalSilos,
IpPoolReservationType::OxideInternal => Self::OxideInternal,
}
}
}

impl_enum_type!(
IpVersionEnum:

Expand Down Expand Up @@ -226,15 +244,13 @@ impl IpPool {
}
}

impl From<IpPool> for views::IpPool {
impl From<IpPool> for views::SystemIpPool {
fn from(pool: IpPool) -> Self {
let identity = pool.identity();
let pool_type = pool.pool_type;

Self {
identity,
pool_type: pool_type.into(),
identity: pool.identity(),
pool_type: pool.pool_type.into(),
ip_version: pool.ip_version.into(),
reservation_type: pool.reservation_type.into(),
}
}
}
Expand Down
16 changes: 12 additions & 4 deletions nexus/db-queries/src/db/datastore/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4310,9 +4310,13 @@ mod tests {
))
.unwrap();
let (service_authz_ip_pool, service_ip_pool) = datastore
.ip_pools_service_lookup(&opctx, IpVersion::V4)
.fetch_first_oxide_internal_ip_pool(
&opctx,
authz::Action::CreateChild,
Some(IpVersion::V4),
)
.await
.expect("lookup service ip pool");
.expect("Failed authz check on delegated IP Pool");
datastore
.ip_pool_add_range(
&opctx,
Expand Down Expand Up @@ -4449,9 +4453,13 @@ mod tests {
})
.expect("found external IP");
let (service_authz_ip_pool, service_ip_pool) = datastore
.ip_pools_service_lookup(&opctx, IpVersion::V4)
.fetch_first_oxide_internal_ip_pool(
&opctx,
authz::Action::CreateChild,
Some(IpVersion::V4),
)
.await
.expect("lookup service ip pool");
.expect("Failed authz check for delegated IP Pool");
datastore
.ip_pool_add_range(
&opctx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ impl DataStore {
// Looking up the service pool IDs requires an opctx; we'll do this at
// most once inside the loop below, when we first encounter an address
// of the same IP version.
//
// TODO-correctness: We really need to know which delegated IP Pool to
// select an address from. It's not clear how we do that today, but we
// could make the IpPoolReservationType more fine-grained.
//
// In the meantime, select the first one for a specific IP version,
// which is no worse than today.
//
// See: https://github.com/oxidecomputer/omicron/issues/8949.
let mut v4_pool = None;
let mut v6_pool = None;

Expand All @@ -66,10 +75,13 @@ impl DataStore {
let pool = match pool_ref {
Some(p) => p,
None => {
let new = self
.ip_pools_service_lookup(opctx, version.into())
.await?
.1;
let (_, new) = self
.fetch_first_oxide_internal_ip_pool(
opctx,
nexus_auth::authz::Action::CreateChild,
Some(version.into()),
)
.await?;
*pool_ref = Some(new);
pool_ref.as_ref().unwrap()
}
Expand Down Expand Up @@ -615,9 +627,13 @@ mod tests {
datastore: &DataStore,
) {
let (ip_pool, db_pool) = datastore
.ip_pools_service_lookup(&opctx, IpVersion::V4.into())
.fetch_first_oxide_internal_ip_pool(
&opctx,
nexus_auth::authz::Action::CreateChild,
Some(IpVersion::V4.into()),
)
.await
.expect("failed to find service IP pool");
.expect("Failed authz check on delegated IP Pool");
datastore
.ip_pool_add_range(
&opctx,
Expand Down
37 changes: 27 additions & 10 deletions nexus/db-queries/src/db/datastore/external_ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,19 @@ impl DataStore {
external_ip: OmicronZoneExternalIp,
) -> CreateResult<ExternalIp> {
let version = IpVersion::from(external_ip.ip_version());
let (authz_pool, pool) =
self.ip_pools_service_lookup(opctx, version).await?;
opctx.authorize(authz::Action::CreateChild, &authz_pool).await?;

// TODO-correctness: We need to figure out which IP Pool this address
// actually belongs in, if there are more than one of the same address
// version.
//
// See: https://github.com/oxidecomputer/omicron/issues/8949.
let (.., pool) = self
.fetch_first_oxide_internal_ip_pool(
opctx,
authz::Action::CreateChild,
Some(version),
)
.await?;
let data = IncompleteExternalIp::for_omicron_zone(
pool.id(),
external_ip,
Expand Down Expand Up @@ -341,11 +351,14 @@ impl DataStore {
// Note the IP version used here isn't important. It's just for the
// authz check to list children, and not used for the actual database
// query below, which filters on is_service to get external IPs from
// either pool.
let (authz_pool, _pool) =
self.ip_pools_service_lookup(opctx, IpVersion::V4).await?;
opctx.authorize(authz::Action::ListChildren, &authz_pool).await?;

// any pool.
let _ = self
.fetch_first_oxide_internal_ip_pool(
opctx,
authz::Action::ListChildren,
None,
)
.await?;
paginated(dsl::external_ip, dsl::id, pagparams)
.filter(dsl::is_service)
.filter(dsl::time_deleted.is_null())
Expand Down Expand Up @@ -1171,9 +1184,13 @@ mod tests {
))
.unwrap();
let (service_ip_pool, db_pool) = datastore
.ip_pools_service_lookup(opctx, IpVersion::V4)
.fetch_first_oxide_internal_ip_pool(
opctx,
authz::Action::CreateChild,
Some(IpVersion::V4),
)
.await
.expect("lookup service ip pool");
.expect("Failed authz check on delegated IP Pool");
datastore
.ip_pool_add_range(opctx, &service_ip_pool, &db_pool, &ip_range)
.await
Expand Down
Loading
Loading