From 0d7ef9de258e85948e8e5546285542c76b1c3c58 Mon Sep 17 00:00:00 2001 From: Xiaofei Cao <92354331+XiaofeiCao@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:54:45 +0800 Subject: [PATCH] mgmt, support subnet addressprefixes (#42524) * support withAddressPrefixes * fix * verify listAvailableIps will search current subnet * address comments * address comments * nit * comment * fix test * fix --- .../CHANGELOG.md | 2 + .../azure-resourcemanager-network/assets.json | 2 +- .../network/implementation/SubnetImpl.java | 90 ++++++++++++------ .../network/models/Subnet.java | 50 +++++++++- .../NetworkInterfaceOperationsTests.java | 91 +++++++++++++++++-- 5 files changed, 198 insertions(+), 37 deletions(-) diff --git a/sdk/resourcemanager/azure-resourcemanager-network/CHANGELOG.md b/sdk/resourcemanager/azure-resourcemanager-network/CHANGELOG.md index 4463344f78aa2..92603851d445b 100644 --- a/sdk/resourcemanager/azure-resourcemanager-network/CHANGELOG.md +++ b/sdk/resourcemanager/azure-resourcemanager-network/CHANGELOG.md @@ -4,6 +4,8 @@ ### Features Added +- Supported `addressPrefixes` for `Subnet`. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/resourcemanager/azure-resourcemanager-network/assets.json b/sdk/resourcemanager/azure-resourcemanager-network/assets.json index 2e47830b92852..678c39147e811 100644 --- a/sdk/resourcemanager/azure-resourcemanager-network/assets.json +++ b/sdk/resourcemanager/azure-resourcemanager-network/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/resourcemanager/azure-resourcemanager-network", - "Tag": "java/resourcemanager/azure-resourcemanager-network_e07024eb08" + "Tag": "java/resourcemanager/azure-resourcemanager-network_195291e896" } diff --git a/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/implementation/SubnetImpl.java b/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/implementation/SubnetImpl.java index 21875807a4946..703652dd5b694 100644 --- a/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/implementation/SubnetImpl.java +++ b/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/implementation/SubnetImpl.java @@ -2,7 +2,14 @@ // Licensed under the MIT License. package com.azure.resourcemanager.network.implementation; +import com.azure.core.management.Region; import com.azure.core.management.SubResource; +import com.azure.core.util.CoreUtils; +import com.azure.resourcemanager.network.fluent.models.IpAddressAvailabilityResultInner; +import com.azure.resourcemanager.network.fluent.models.IpConfigurationInner; +import com.azure.resourcemanager.network.fluent.models.NetworkSecurityGroupInner; +import com.azure.resourcemanager.network.fluent.models.RouteTableInner; +import com.azure.resourcemanager.network.fluent.models.SubnetInner; import com.azure.resourcemanager.network.models.Delegation; import com.azure.resourcemanager.network.models.Network; import com.azure.resourcemanager.network.models.NetworkInterface; @@ -12,12 +19,6 @@ import com.azure.resourcemanager.network.models.ServiceEndpointPropertiesFormat; import com.azure.resourcemanager.network.models.ServiceEndpointType; import com.azure.resourcemanager.network.models.Subnet; -import com.azure.resourcemanager.network.fluent.models.IpAddressAvailabilityResultInner; -import com.azure.resourcemanager.network.fluent.models.IpConfigurationInner; -import com.azure.resourcemanager.network.fluent.models.NetworkSecurityGroupInner; -import com.azure.resourcemanager.network.fluent.models.RouteTableInner; -import com.azure.resourcemanager.network.fluent.models.SubnetInner; -import com.azure.core.management.Region; import com.azure.resourcemanager.network.models.VirtualNetworkPrivateEndpointNetworkPolicies; import com.azure.resourcemanager.network.models.VirtualNetworkPrivateLinkServiceNetworkPolicies; import com.azure.resourcemanager.resources.fluentcore.arm.ResourceUtils; @@ -31,6 +32,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -62,6 +64,14 @@ public String addressPrefix() { return this.innerModel().addressPrefix(); } + @Override + public List addressPrefixes() { + if (CoreUtils.isNullOrEmpty(this.innerModel().addressPrefixes())) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(this.innerModel().addressPrefixes()); + } + @Override public String name() { return this.innerModel().name(); @@ -157,6 +167,15 @@ public Update withoutRouteTable() { @Override public SubnetImpl withAddressPrefix(String cidr) { this.innerModel().withAddressPrefix(cidr); + this.innerModel().withAddressPrefixes(null); + return this; + } + + @Override + public SubnetImpl withAddressPrefixes(Collection addressPrefixes) { + Objects.requireNonNull(addressPrefixes); + this.innerModel().withAddressPrefixes(new ArrayList<>(addressPrefixes)); + this.innerModel().withAddressPrefix(null); return this; } @@ -265,27 +284,21 @@ public Collection listNetworkInterfaceIPConfigurations() { @Override public Set listAvailablePrivateIPAddresses() { - Set ipAddresses = new TreeSet<>(); - - String cidr = this.addressPrefix(); - if (cidr == null) { - return ipAddresses; // Should never happen, but just in case - } - String takenIPAddress = cidr.split("/")[0]; - - IpAddressAvailabilityResultInner result = - this - .parent() - .manager() - .serviceClient() - .getVirtualNetworks() - .checkIpAddressAvailability(this.parent().resourceGroupName(), this.parent().name(), takenIPAddress); - if (result == null || result.availableIpAddresses() == null) { - return ipAddresses; + Set result = Collections.emptySet(); + if (!CoreUtils.isNullOrEmpty(this.addressPrefixes())) { + for (String cidr : this.addressPrefixes()) { + // According to our test, when doing "checkIpAddressAvailability", backend knows about which subnet the "startIp" + // belongs to, thus we only need to use one of the address prefixes. + Set availableIps = listAvailablePrivateIPAddresses(cidr); + if (!CoreUtils.isNullOrEmpty(availableIps)) { + result = availableIps; + break; + } + } + } else { + result = listAvailablePrivateIPAddresses(this.addressPrefix()); } - - ipAddresses.addAll(result.availableIpAddresses()); - return ipAddresses; + return result; } @Override @@ -344,4 +357,29 @@ public SubnetImpl withExistingNatGateway(String resourceId) { } return this; } + + private Set listAvailablePrivateIPAddresses(String cidr) { + Set ipAddresses = new TreeSet<>(); + if (cidr == null) { + return ipAddresses; + } + String takenIPAddress = cidr.split("/")[0]; + + IpAddressAvailabilityResultInner result = + this + .parent() + .manager() + .serviceClient() + .getVirtualNetworks() + .checkIpAddressAvailability(this.parent().resourceGroupName(), this.parent().name(), takenIPAddress); + if (result == null + // there's a case when user doesn't have the permission to query, result.availableIpAddresses() will be null + || result.availableIpAddresses() == null + ) { + return ipAddresses; + } + + ipAddresses.addAll(result.availableIpAddresses()); + return ipAddresses; + } } diff --git a/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/models/Subnet.java b/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/models/Subnet.java index f45a93ad0fc13..967ca98a98c18 100644 --- a/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/models/Subnet.java +++ b/sdk/resourcemanager/azure-resourcemanager-network/src/main/java/com/azure/resourcemanager/network/models/Subnet.java @@ -25,15 +25,37 @@ public interface Subnet extends HasInnerModel, ChildResource listNetworkInterfaceIPConfigurations(); - /** @return available private IP addresses within this network */ + /** + * List available private IP addresses within this subnet. + *
Starting IPs of the address prefixes are not returned, due to:
+     * 1. They are usually reserved by platform(e.g. for gateway usage).
+     * 2. For backward-compatibility.
+ * + * @return available private IP addresses within this network + */ Set listAvailablePrivateIPAddresses(); /** @return number of network interface IP configurations associated with this subnet */ int networkInterfaceIPConfigurationCount(); - /** @return the address space prefix, in CIDR notation, assigned to this subnet */ + /** + * Gets the address space prefix, in CIDR notation, assigned to this subnet. + *

Use {@link Subnet#addressPrefixes} if this subnet has multiple prefixes.

+ * + * @return the address space prefix(or one of the prefixes, if this subnet has multiple ones), in CIDR notation, assigned to this subnet + * @see Subnet#addressPrefixes + */ String addressPrefix(); + /** + * Gets address space prefixes, in CIDR notation, assigned to this subnet. + *

Use {@link Subnet#addressPrefix} if this subnet is created/updated using that property.

+ * + * @return address space prefixes, in CIDR notation, assigned to this subnet + * @see Subnet#addressPrefix + */ + List addressPrefixes(); + /** * @return the network security group associated with this subnet, if any *

Note that this method will result in a call to Azure each time it is invoked. @@ -84,6 +106,14 @@ interface WithAddressPrefix { * @return the next stage of the definition */ WithAttach withAddressPrefix(String cidr); + + /** + * Specifies the IP address spaces of the subnet, within the address space of the network. + * + * @param addressPrefixes the IP address space prefixes using the CIDR notation + * @return the next stage of the definition + */ + WithAttach withAddressPrefixes(Collection addressPrefixes); } /** @@ -248,6 +278,14 @@ interface WithAddressPrefix { * @return the next stage */ Update withAddressPrefix(String cidr); + + /** + * Specifies the IP address spaces of the subnet, within the address space of the network. + * + * @param addressPrefixes the IP address space prefixes using the CIDR notation + * @return the next stage + */ + Update withAddressPrefixes(Collection addressPrefixes); } /** The stage of the subnet update allowing to change the network security group to assign to the subnet. */ @@ -435,6 +473,14 @@ interface WithAddressPrefix { * @return the next stage of the definition */ WithAttach withAddressPrefix(String cidr); + + /** + * Specifies the IP address spaces of the subnet, within the address space of the network. + * + * @param addressPrefixes the IP address space prefixes using the CIDR notation + * @return the next stage of the definition + */ + WithAttach withAddressPrefixes(Collection addressPrefixes); } /** diff --git a/sdk/resourcemanager/azure-resourcemanager-network/src/test/java/com/azure/resourcemanager/network/NetworkInterfaceOperationsTests.java b/sdk/resourcemanager/azure-resourcemanager-network/src/test/java/com/azure/resourcemanager/network/NetworkInterfaceOperationsTests.java index 41523fe1a7d4d..2511b0f1c1c86 100644 --- a/sdk/resourcemanager/azure-resourcemanager-network/src/test/java/com/azure/resourcemanager/network/NetworkInterfaceOperationsTests.java +++ b/sdk/resourcemanager/azure-resourcemanager-network/src/test/java/com/azure/resourcemanager/network/NetworkInterfaceOperationsTests.java @@ -4,6 +4,7 @@ package com.azure.resourcemanager.network; import com.azure.core.management.Region; +import com.azure.core.util.CoreUtils; import com.azure.resourcemanager.network.fluent.models.NatGatewayInner; import com.azure.resourcemanager.network.models.ApplicationSecurityGroup; import com.azure.resourcemanager.network.models.DeleteOptions; @@ -431,11 +432,54 @@ public void canDeleteNetworkWithServiceCallBack() { Assertions.assertEquals(counter.intValue(), 1); } + @Test + public void canSetSubnetAddressPrefixes() { + String networkName = generateRandomResourceName("vnet", 10); + String subnetName = "subnet1"; + // create withAddressPrefix + Network network = networkManager.networks() + .define(networkName) + .withRegion(Region.US_EAST) + .withNewResourceGroup(rgName) + .withAddressSpace("10.0.0.0/24") + .withSubnet(subnetName, "10.0.0.0/29") + .create(); + + Subnet subnet = network.subnets().get(subnetName); + Assertions.assertNotNull(subnet.addressPrefix()); + Assertions.assertTrue(CoreUtils.isNullOrEmpty(subnet.addressPrefixes())); + + // update withAddressPrefixes + network.update().updateSubnet(subnetName) + .withAddressPrefixes(Arrays.asList("10.0.0.8/29", "10.0.0.16/29")) + .parent() + .apply(); + + network.refresh(); + subnet = network.subnets().get(subnetName); + + Assertions.assertEquals(2, subnet.addressPrefixes().size()); + Assertions.assertTrue(subnet.addressPrefixes().contains("10.0.0.8/29")); + Assertions.assertNull(subnet.addressPrefix()); + + // update withAddressPrefix + network.update().updateSubnet(subnetName) + .withAddressPrefix("10.0.0.0/29") + .parent() + .apply(); + + network.refresh(); + subnet = network.subnets().get(subnetName); + + Assertions.assertEquals("10.0.0.0/29", subnet.addressPrefix()); + Assertions.assertTrue(CoreUtils.isNullOrEmpty(subnet.innerModel().addressPrefixes())); + } + @Test public void canListSubnetAvailableIpAddresses() { String networkName = generateRandomResourceName("vnet", 10); String subnetName = "subnet1"; - String nicName = generateRandomResourceName("nic", 10); + String subnet2Name = "subnet2"; Network network = networkManager.networks() .define(networkName) @@ -447,21 +491,52 @@ public void canListSubnetAvailableIpAddresses() { Subnet subnet = network.subnets().get(subnetName); Set availableIps = subnet.listAvailablePrivateIPAddresses(); - Assertions.assertTrue(availableIps.size() > 0); + Assertions.assertFalse(availableIps.isEmpty()); - String availableIp = availableIps.iterator().next(); + // occupy all available ip addresses + for (String ip : availableIps) { + networkManager.networkInterfaces() + .define(generateRandomResourceName("nic", 10)) + .withRegion(Region.US_EAST) + .withExistingResourceGroup(rgName) + .withExistingPrimaryNetwork(network) + .withSubnet(subnetName) + .withPrimaryPrivateIPAddressStatic(ip) + .create(); + } - // occupy the available ip address - NetworkInterface nic = networkManager.networkInterfaces() - .define(nicName) + availableIps = subnet.listAvailablePrivateIPAddresses(); + Assertions.assertTrue(availableIps.isEmpty()); + + // define a new subnet with address prefixes + network.update().defineSubnet(subnet2Name) + .withAddressPrefixes(Arrays.asList("10.0.0.8/29", "10.0.0.16/29")) + .attach() + .apply(); + + network.refresh(); + + // verify it does not affect previous subnet's available ip addresses + subnet = network.subnets().get(subnetName); + Assertions.assertTrue(subnet.listAvailablePrivateIPAddresses().isEmpty()); + + Subnet subnet2 = network.subnets().get(subnet2Name); + availableIps = subnet2.listAvailablePrivateIPAddresses(); + Assertions.assertFalse(availableIps.isEmpty()); + Assertions.assertEquals(2, subnet2.addressPrefixes().size()); + + // occupy the available ip address of the second subnet + String availableIp = availableIps.iterator().next(); + networkManager.networkInterfaces() + .define(generateRandomResourceName("nic", 10)) .withRegion(Region.US_EAST) .withExistingResourceGroup(rgName) .withExistingPrimaryNetwork(network) - .withSubnet(subnetName) + .withSubnet(subnet2Name) .withPrimaryPrivateIPAddressStatic(availableIp) .create(); - availableIps = subnet.listAvailablePrivateIPAddresses(); + availableIps = subnet2.listAvailablePrivateIPAddresses(); Assertions.assertFalse(availableIps.contains(availableIp)); }