diff --git a/nexus/src/db/datastore.rs b/nexus/src/db/datastore.rs index 02a7b01050..a23ad38242 100644 --- a/nexus/src/db/datastore.rs +++ b/nexus/src/db/datastore.rs @@ -1090,9 +1090,7 @@ impl DataStore { } // Insert and allocate an IP address None => { - let block = interface.subnet.ipv4_block.ok_or_else(|| { - Error::internal_error("assuming subnets all have v4 block") - })?; + let block = interface.subnet.ipv4_block; let allocation_query = AllocateIpQuery { block: ipnetwork::IpNetwork::V4(block.0 .0), interface, diff --git a/nexus/src/db/model.rs b/nexus/src/db/model.rs index 87c5587e1c..8d52a350dc 100644 --- a/nexus/src/db/model.rs +++ b/nexus/src/db/model.rs @@ -1162,8 +1162,8 @@ pub struct VpcSubnet { identity: VpcSubnetIdentity, pub vpc_id: Uuid, - pub ipv4_block: Option, - pub ipv6_block: Option, + pub ipv4_block: Ipv4Net, + pub ipv6_block: Ipv6Net, } impl VpcSubnet { @@ -1176,8 +1176,8 @@ impl VpcSubnet { Self { identity, vpc_id, - ipv4_block: params.ipv4_block.map(Ipv4Net), - ipv6_block: params.ipv6_block.map(Ipv6Net), + ipv4_block: Ipv4Net(params.ipv4_block), + ipv6_block: Ipv6Net(params.ipv6_block), } } } @@ -1198,8 +1198,8 @@ impl From for VpcSubnetUpdate { name: params.identity.name.map(Name), description: params.identity.description, time_modified: Utc::now(), - ipv4_block: params.ipv4_block.map(Ipv4Net), - ipv6_block: params.ipv6_block.map(Ipv6Net), + ipv4_block: Some(Ipv4Net(params.ipv4_block)), + ipv6_block: Some(Ipv6Net(params.ipv6_block)), } } } diff --git a/nexus/src/db/schema.rs b/nexus/src/db/schema.rs index 504543d4bb..5924aff185 100644 --- a/nexus/src/db/schema.rs +++ b/nexus/src/db/schema.rs @@ -233,8 +233,8 @@ table! { time_modified -> Timestamptz, time_deleted -> Nullable, vpc_id -> Uuid, - ipv4_block -> Nullable, - ipv6_block -> Nullable, + ipv4_block -> Inet, + ipv6_block -> Inet, } } diff --git a/nexus/src/db/subnet_allocation.rs b/nexus/src/db/subnet_allocation.rs index 7ebfbc5d4e..07ed0fc85a 100644 --- a/nexus/src/db/subnet_allocation.rs +++ b/nexus/src/db/subnet_allocation.rs @@ -231,7 +231,7 @@ mod test { use diesel::pg::Pg; use diesel::prelude::*; use omicron_common::api::external::{ - IdentityMetadataCreateParams, Ipv4Net, MacAddr, + IdentityMetadataCreateParams, Ipv4Net, Ipv6Net, MacAddr, }; use std::convert::TryInto; @@ -249,7 +249,8 @@ mod test { let subnet_id = uuid::Uuid::parse_str("223cb7f7-0d3a-4a4e-a5e1-ad38ecb785d3") .unwrap(); - let block: ipnetwork::Ipv4Network = "192.168.1.0/24".parse().unwrap(); + let ipv4_block = "192.168.1.0/24".parse().unwrap(); + let ipv6_block = "fd12:3456::/64".parse().unwrap(); let subnet = VpcSubnet::new( subnet_id, vpc_id, @@ -258,8 +259,8 @@ mod test { name: "test-subnet".to_string().try_into().unwrap(), description: "subnet description".to_string(), }, - ipv4_block: Some(Ipv4Net(block.clone()).into()), - ipv6_block: None, + ipv4_block: Ipv4Net(ipv4_block), + ipv6_block: Ipv6Net(ipv6_block), }, ); let mac = @@ -281,7 +282,7 @@ mod test { ); let select = AllocateIpQuery { interface, - block: block.into(), + block: ipv4_block.into(), now: DateTime::::from_utc( NaiveDateTime::from_timestamp(0, 0), Utc, diff --git a/nexus/src/external_api/params.rs b/nexus/src/external_api/params.rs index bc3d6796a5..66ad112b8a 100644 --- a/nexus/src/external_api/params.rs +++ b/nexus/src/external_api/params.rs @@ -127,8 +127,8 @@ pub struct VpcUpdate { pub struct VpcSubnetCreate { #[serde(flatten)] pub identity: IdentityMetadataCreateParams, - pub ipv4_block: Option, - pub ipv6_block: Option, + pub ipv4_block: Ipv4Net, + pub ipv6_block: Ipv6Net, } /** @@ -139,8 +139,8 @@ pub struct VpcSubnetCreate { pub struct VpcSubnetUpdate { #[serde(flatten)] pub identity: IdentityMetadataUpdateParams, - pub ipv4_block: Option, - pub ipv6_block: Option, + pub ipv4_block: Ipv4Net, + pub ipv6_block: Ipv6Net, } /* diff --git a/nexus/src/external_api/views.rs b/nexus/src/external_api/views.rs index 0d0fb84c85..6313cad5a5 100644 --- a/nexus/src/external_api/views.rs +++ b/nexus/src/external_api/views.rs @@ -119,10 +119,10 @@ pub struct VpcSubnet { // how to do the validation of user-specified CIDR blocks, or how to create a block if one is // not given. /** The IPv4 subnet CIDR block. */ - pub ipv4_block: Option, + pub ipv4_block: Ipv4Net, /** The IPv6 subnet CIDR block. */ - pub ipv6_block: Option, + pub ipv6_block: Ipv6Net, } impl Into for model::VpcSubnet { @@ -130,8 +130,8 @@ impl Into for model::VpcSubnet { VpcSubnet { identity: self.identity(), vpc_id: self.vpc_id, - ipv4_block: self.ipv4_block.map(|ip| ip.into()), - ipv6_block: self.ipv6_block.map(|ip| ip.into()), + ipv4_block: self.ipv4_block.into(), + ipv6_block: self.ipv6_block.into(), } } } diff --git a/nexus/src/nexus.rs b/nexus/src/nexus.rs index f5f847d6c1..d59dea0a4e 100644 --- a/nexus/src/nexus.rs +++ b/nexus/src/nexus.rs @@ -1592,14 +1592,14 @@ impl Nexus { params.identity.name ), }, - ipv4_block: Some(Ipv4Net( + ipv4_block: Ipv4Net( // TODO: This value should be replaced with the correct ipv4 range for a default subnet "10.1.9.32/16".parse::().unwrap(), - )), - ipv6_block: Some(Ipv6Net( + ), + ipv6_block: Ipv6Net( // TODO: This value should be replaced w/ the first `/64` ipv6 from the address block "2001:db8::0/64".parse::().unwrap(), - )), + ), }, ); self.db_datastore.vpc_create_subnet(subnet).await?; diff --git a/nexus/tests/integration_tests/subnet_allocation.rs b/nexus/tests/integration_tests/subnet_allocation.rs index 1cc5133019..2f255e93ca 100644 --- a/nexus/tests/integration_tests/subnet_allocation.rs +++ b/nexus/tests/integration_tests/subnet_allocation.rs @@ -11,7 +11,7 @@ use http::method::Method; use http::StatusCode; use omicron_common::api::external::{ ByteCount, IdentityMetadataCreateParams, IdentityMetadataUpdateParams, - Instance, InstanceCpuCount, Ipv4Net, NetworkInterface, + Instance, InstanceCpuCount, Ipv4Net, Ipv6Net, NetworkInterface, }; use omicron_nexus::external_api::params; use std::net::IpAddr; @@ -88,14 +88,15 @@ async fn test_subnet_allocation(cptestctx: &ControlPlaneTestContext) { "/organizations/{}/projects/{}/vpcs/default/subnets/default", organization_name, project_name ); - let subnet = "192.168.42.0/29".parse().unwrap(); + let ipv4_block = "192.168.42.0/29".parse().unwrap(); + let ipv6_block = "fd12:3456::/64".parse().unwrap(); let subnet_update = params::VpcSubnetUpdate { identity: IdentityMetadataUpdateParams { name: Some("default".parse().unwrap()), description: None, }, - ipv4_block: Some(Ipv4Net(subnet)), - ipv6_block: None, + ipv4_block: Ipv4Net(ipv4_block), + ipv6_block: Ipv6Net(ipv6_block), }; client .make_request( diff --git a/nexus/tests/integration_tests/vpc_subnets.rs b/nexus/tests/integration_tests/vpc_subnets.rs index 8e45e34997..217dd797c7 100644 --- a/nexus/tests/integration_tests/vpc_subnets.rs +++ b/nexus/tests/integration_tests/vpc_subnets.rs @@ -72,10 +72,8 @@ async fn test_vpc_subnets(cptestctx: &ControlPlaneTestContext) { assert_eq!(error.message, "not found: vpc-subnet with name \"subnet1\""); /* Create a VPC Subnet. */ - let ipv4_block = - Some(Ipv4Net("10.1.9.32/16".parse::().unwrap())); - let ipv6_block = - Some(Ipv6Net("2001:db8::0/96".parse::().unwrap())); + let ipv4_block = Ipv4Net("10.1.9.32/16".parse::().unwrap()); + let ipv6_block = Ipv6Net("2001:db8::0/96".parse::().unwrap()); let new_subnet = params::VpcSubnetCreate { identity: IdentityMetadataCreateParams { name: subnet_name.parse().unwrap(), @@ -146,16 +144,16 @@ async fn test_vpc_subnets(cptestctx: &ControlPlaneTestContext) { name: subnet2_name.parse().unwrap(), description: "it's also below the net".to_string(), }, - ipv4_block: None, - ipv6_block: None, + ipv4_block, + ipv6_block, }; let subnet2: VpcSubnet = objects_post(&client, &subnets_url, new_subnet.clone()).await; assert_eq!(subnet2.identity.name, subnet2_name); assert_eq!(subnet2.identity.description, "it's also below the net"); assert_eq!(subnet2.vpc_id, vpc.identity.id); - assert_eq!(subnet2.ipv4_block, None); - assert_eq!(subnet2.ipv6_block, None); + assert_eq!(subnet2.ipv4_block, ipv4_block); + assert_eq!(subnet2.ipv6_block, ipv6_block); // subnets list should now have two in it let subnets = @@ -165,13 +163,17 @@ async fn test_vpc_subnets(cptestctx: &ControlPlaneTestContext) { subnets_eq(&subnets[1], &subnet2); // update first subnet + let new_ipv4_block = + Ipv4Net("10.1.9.33/16".parse::().unwrap()); + let new_ipv6_block = + Ipv6Net("2001:db9::0/96".parse::().unwrap()); let update_params = params::VpcSubnetUpdate { identity: IdentityMetadataUpdateParams { name: Some("new-name".parse().unwrap()), description: Some("another description".to_string()), }, - ipv4_block: None, - ipv6_block: None, + ipv4_block: new_ipv4_block, + ipv6_block: new_ipv6_block, }; client .make_request( @@ -245,8 +247,8 @@ async fn test_vpc_subnets(cptestctx: &ControlPlaneTestContext) { "it's also below the net" ); assert_eq!(subnet_same_name.vpc_id, vpc2.identity.id); - assert_eq!(subnet_same_name.ipv4_block, None); - assert_eq!(subnet_same_name.ipv6_block, None); + assert_eq!(subnet_same_name.ipv4_block, ipv4_block); + assert_eq!(subnet_same_name.ipv6_block, ipv6_block); } fn subnets_eq(sn1: &VpcSubnet, sn2: &VpcSubnet) {