Skip to content

Commit

Permalink
mgmt, support subnet addressprefixes (Azure#42524)
Browse files Browse the repository at this point in the history
* support withAddressPrefixes

* fix

* verify listAvailableIps will search current subnet

* address comments

* address comments

* nit

* comment

* fix test

* fix
  • Loading branch information
XiaofeiCao authored Oct 24, 2024
1 parent cad4cc9 commit 0d7ef9d
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features Added

- Supported `addressPrefixes` for `Subnet`.

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -62,6 +64,14 @@ public String addressPrefix() {
return this.innerModel().addressPrefix();
}

@Override
public List<String> addressPrefixes() {
if (CoreUtils.isNullOrEmpty(this.innerModel().addressPrefixes())) {
return Collections.emptyList();
}
return Collections.unmodifiableList(this.innerModel().addressPrefixes());
}

@Override
public String name() {
return this.innerModel().name();
Expand Down Expand Up @@ -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<String> addressPrefixes) {
Objects.requireNonNull(addressPrefixes);
this.innerModel().withAddressPrefixes(new ArrayList<>(addressPrefixes));
this.innerModel().withAddressPrefix(null);
return this;
}

Expand Down Expand Up @@ -265,27 +284,21 @@ public Collection<NicIpConfiguration> listNetworkInterfaceIPConfigurations() {

@Override
public Set<String> listAvailablePrivateIPAddresses() {
Set<String> 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<String> 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<String> availableIps = listAvailablePrivateIPAddresses(cidr);
if (!CoreUtils.isNullOrEmpty(availableIps)) {
result = availableIps;
break;
}
}
} else {
result = listAvailablePrivateIPAddresses(this.addressPrefix());
}

ipAddresses.addAll(result.availableIpAddresses());
return ipAddresses;
return result;
}

@Override
Expand Down Expand Up @@ -344,4 +357,29 @@ public SubnetImpl withExistingNatGateway(String resourceId) {
}
return this;
}

private Set<String> listAvailablePrivateIPAddresses(String cidr) {
Set<String> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,37 @@ public interface Subnet extends HasInnerModel<SubnetInner>, ChildResource<Networ
*/
Collection<NicIpConfiguration> listNetworkInterfaceIPConfigurations();

/** @return available private IP addresses within this network */
/**
* List available private IP addresses within this subnet.
* <pre>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.</pre>
*
* @return available private IP addresses within this network
*/
Set<String> 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.
* <p>Use {@link Subnet#addressPrefixes} if this subnet has multiple prefixes.</p>
*
* @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.
* <p>Use {@link Subnet#addressPrefix} if this subnet is created/updated using that property.</p>
*
* @return address space prefixes, in CIDR notation, assigned to this subnet
* @see Subnet#addressPrefix
*/
List<String> addressPrefixes();

/**
* @return the network security group associated with this subnet, if any
* <p>Note that this method will result in a call to Azure each time it is invoked.
Expand Down Expand Up @@ -84,6 +106,14 @@ interface WithAddressPrefix<ParentT> {
* @return the next stage of the definition
*/
WithAttach<ParentT> 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<ParentT> withAddressPrefixes(Collection<String> addressPrefixes);
}

/**
Expand Down Expand Up @@ -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<String> addressPrefixes);
}

/** The stage of the subnet update allowing to change the network security group to assign to the subnet. */
Expand Down Expand Up @@ -435,6 +473,14 @@ interface WithAddressPrefix<ParentT> {
* @return the next stage of the definition
*/
WithAttach<ParentT> 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<ParentT> withAddressPrefixes(Collection<String> addressPrefixes);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -447,21 +491,52 @@ public void canListSubnetAvailableIpAddresses() {

Subnet subnet = network.subnets().get(subnetName);
Set<String> 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));
}

Expand Down

0 comments on commit 0d7ef9d

Please sign in to comment.