Skip to content

Commit

Permalink
also fix service pool range add, make tests pass, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
david-crespo committed Feb 20, 2024
1 parent ea9611d commit 1305498
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 70 deletions.
14 changes: 12 additions & 2 deletions nexus/src/external_api/http_entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,13 +1737,16 @@ async fn ip_pool_range_list(
async fn ip_pool_range_add(
rqctx: RequestContext<Arc<ServerContext>>,
path_params: Path<params::IpPoolPath>,
// The plan is to change this back to shared::IpRange once IPv6 works
range_params: TypedBody<shared::Ipv4Range>,
) -> Result<HttpResponseCreated<IpPoolRange>, HttpError> {
let apictx = &rqctx.context();
let handler = async {
let opctx = crate::context::op_context_for_external_api(&rqctx).await?;
let nexus = &apictx.nexus;
let path = path_params.into_inner();
// This is a bit of a hack to keep the service functions general with
// respect to IP version while disallowing IPv6 ranges at the API layer
let range = shared::IpRange::V4(range_params.into_inner());
let pool_lookup = nexus.ip_pool_lookup(&opctx, &path.pool)?;
let out = nexus.ip_pool_add_range(&opctx, &pool_lookup, &range).await?;
Expand All @@ -1761,6 +1764,8 @@ async fn ip_pool_range_add(
async fn ip_pool_range_remove(
rqctx: RequestContext<Arc<ServerContext>>,
path_params: Path<params::IpPoolPath>,
// Note that add is restricted to IPv4 while remove allows v4 or v6. This is
// meant as a safeguard in case existing systems have v6 ranges to remove.
range_params: TypedBody<shared::IpRange>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let apictx = &rqctx.context();
Expand Down Expand Up @@ -1827,13 +1832,16 @@ async fn ip_pool_service_range_list(
}]
async fn ip_pool_service_range_add(
rqctx: RequestContext<Arc<ServerContext>>,
range_params: TypedBody<shared::IpRange>,
// The plan is to change this back to shared::IpRange once IPv6 works
range_params: TypedBody<shared::Ipv4Range>,
) -> Result<HttpResponseCreated<IpPoolRange>, HttpError> {
let apictx = &rqctx.context();
let handler = async {
let opctx = crate::context::op_context_for_external_api(&rqctx).await?;
let nexus = &apictx.nexus;
let range = range_params.into_inner();
// This is a bit of a hack to keep the service functions general with
// respect to IP version while disallowing IPv6 ranges at the API layer
let range = shared::IpRange::V4(range_params.into_inner());
let out = nexus.ip_pool_service_add_range(&opctx, &range).await?;
Ok(HttpResponseCreated(out.into()))
};
Expand All @@ -1848,6 +1856,8 @@ async fn ip_pool_service_range_add(
}]
async fn ip_pool_service_range_remove(
rqctx: RequestContext<Arc<ServerContext>>,
// Note that add is restricted to IPv4 while remove allows v4 or v6. This is
// meant as a safeguard in case existing systems have v6 ranges to remove.
range_params: TypedBody<shared::IpRange>,
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
let apictx = &rqctx.context();
Expand Down
165 changes: 98 additions & 67 deletions nexus/tests/integration_tests/ip_pools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ use nexus_types::external_api::params::IpPoolSiloUpdate;
use nexus_types::external_api::params::IpPoolUpdate;
use nexus_types::external_api::shared::IpRange;
use nexus_types::external_api::shared::Ipv4Range;
use nexus_types::external_api::shared::Ipv6Range;
use nexus_types::external_api::shared::SiloIdentityMode;
use nexus_types::external_api::views::IpPool;
use nexus_types::external_api::views::IpPoolRange;
use nexus_types::external_api::views::IpPoolSiloLink;
use nexus_types::external_api::views::Silo;
use nexus_types::external_api::views::SiloIpPool;
use nexus_types::identity::Resource;
use omicron_common::address::Ipv6Range;
use omicron_common::api::external::IdentityMetadataUpdateParams;
use omicron_common::api::external::NameOrId;
use omicron_common::api::external::SimpleIdentity;
Expand Down Expand Up @@ -853,59 +853,60 @@ async fn test_ip_pool_range_overlapping_ranges_fails(
};
test_bad_ip_ranges(client, &ip_pool_add_range_url, &ipv4_range).await;

// TODO: put back IPv6 tests when IPv6 ranges are supported again
// Test data for IPv6 ranges that should fail due to overlap
let ipv6_range = TestRange {
base_range: IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
)
.unwrap(),
),
bad_ranges: vec![
// The exact same range
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
)
.unwrap(),
),
// Overlaps below
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 5),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 15),
)
.unwrap(),
),
// Overlaps above
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 15),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 25),
)
.unwrap(),
),
// Contains the base range
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 0),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 100),
)
.unwrap(),
),
// Contained by the base range
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 12),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 13),
)
.unwrap(),
),
],
};
test_bad_ip_ranges(client, &ip_pool_add_range_url, &ipv6_range).await;
// let ipv6_range = TestRange {
// base_range: IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
// )
// .unwrap(),
// ),
// bad_ranges: vec![
// // The exact same range
// IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
// )
// .unwrap(),
// ),
// // Overlaps below
// IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 5),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 15),
// )
// .unwrap(),
// ),
// // Overlaps above
// IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 15),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 25),
// )
// .unwrap(),
// ),
// // Contains the base range
// IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 0),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 100),
// )
// .unwrap(),
// ),
// // Contained by the base range
// IpRange::V6(
// Ipv6Range::new(
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 12),
// std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 13),
// )
// .unwrap(),
// ),
// ],
// };
// test_bad_ip_ranges(client, &ip_pool_add_range_url, &ipv6_range).await;
}

async fn test_bad_ip_ranges(
Expand Down Expand Up @@ -952,6 +953,36 @@ async fn test_bad_ip_ranges(
}
}

#[nexus_test]
async fn test_ip_pool_range_rejects_v6(cptestctx: &ControlPlaneTestContext) {
let client = &cptestctx.external_client;

create_ip_pool(client, "p0", None).await;

let range = IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
)
.unwrap(),
);

let add_url = "/v1/system/ip-pools/p0/ranges/add";
let error =
object_create_error(client, add_url, &range, StatusCode::BAD_REQUEST)
.await;

let msg = "unable to parse JSON body: first: invalid IPv4 address syntax";
assert!(error.message.starts_with(msg));

// same deal with service pool
let add_url = "/v1/system/ip-pools-service/ranges/add";
let error =
object_create_error(client, add_url, &range, StatusCode::BAD_REQUEST)
.await;
assert!(error.message.starts_with(msg));
}

#[nexus_test]
async fn test_ip_pool_range_pagination(cptestctx: &ControlPlaneTestContext) {
let client = &cptestctx.external_client;
Expand Down Expand Up @@ -984,17 +1015,17 @@ async fn test_ip_pool_range_pagination(cptestctx: &ControlPlaneTestContext) {
// address, which sorts all IPv4 before IPv6, then within protocol versions
// by their first address.
let ranges = [
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 11),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 20),
IpRange::V4(
Ipv4Range::new(
std::net::Ipv4Addr::new(10, 0, 0, 3),
std::net::Ipv4Addr::new(10, 0, 0, 4),
)
.unwrap(),
),
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 0),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
IpRange::V4(
Ipv4Range::new(
std::net::Ipv4Addr::new(10, 0, 0, 5),
std::net::Ipv4Addr::new(10, 0, 0, 6),
)
.unwrap(),
),
Expand Down Expand Up @@ -1262,15 +1293,15 @@ async fn test_ip_pool_service(cptestctx: &ControlPlaneTestContext) {
let ranges = [
IpRange::V4(
Ipv4Range::new(
std::net::Ipv4Addr::new(10, 0, 0, 1),
std::net::Ipv4Addr::new(10, 0, 0, 2),
std::net::Ipv4Addr::new(10, 0, 0, 3),
std::net::Ipv4Addr::new(10, 0, 0, 4),
)
.unwrap(),
),
IpRange::V6(
Ipv6Range::new(
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 0),
std::net::Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 10),
IpRange::V4(
Ipv4Range::new(
std::net::Ipv4Addr::new(10, 0, 0, 1),
std::net::Ipv4Addr::new(10, 0, 0, 2),
)
.unwrap(),
),
Expand Down
2 changes: 1 addition & 1 deletion openapi/nexus.json
Original file line number Diff line number Diff line change
Expand Up @@ -5560,7 +5560,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IpRange"
"$ref": "#/components/schemas/Ipv4Range"
}
}
},
Expand Down

0 comments on commit 1305498

Please sign in to comment.