JCLOUDS-1278: Allow to configure virtual machine NICs
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
index 9d3f05d..c35455c 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
@@ -99,7 +99,7 @@
// Api versions used in each API
properties.put(API_VERSION_PREFIX + DeploymentApi.class.getSimpleName(), "2016-02-01");
properties.put(API_VERSION_PREFIX + LocationApi.class.getSimpleName(), "2015-11-01");
- properties.put(API_VERSION_PREFIX + NetworkInterfaceCardApi.class.getSimpleName(), "2015-06-15");
+ properties.put(API_VERSION_PREFIX + NetworkInterfaceCardApi.class.getSimpleName(), "2017-03-01");
properties.put(API_VERSION_PREFIX + NetworkSecurityGroupApi.class.getSimpleName(), "2016-03-30");
properties.put(API_VERSION_PREFIX + NetworkSecurityRuleApi.class.getSimpleName(), "2016-03-30");
properties.put(API_VERSION_PREFIX + OSImageApi.class.getSimpleName(), "2015-06-15");
@@ -107,7 +107,7 @@
properties.put(API_VERSION_PREFIX + ResourceGroupApi.class.getSimpleName(), "2015-01-01");
properties.put(API_VERSION_PREFIX + ResourceProviderApi.class.getSimpleName(), "2015-01-01");
properties.put(API_VERSION_PREFIX + StorageAccountApi.class.getSimpleName(), "2015-06-15");
- properties.put(API_VERSION_PREFIX + SubnetApi.class.getSimpleName(), "2015-06-15");
+ properties.put(API_VERSION_PREFIX + SubnetApi.class.getSimpleName(), "2017-03-01");
properties.put(API_VERSION_PREFIX + VirtualNetworkApi.class.getSimpleName(), "2015-06-15");
properties.put(API_VERSION_PREFIX + VMSizeApi.class.getSimpleName(), "2015-06-15");
properties.put(API_VERSION_PREFIX + VirtualMachineApi.class.getSimpleName(), "2016-04-30-preview");
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
index d01d6ec..02e69fa 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
@@ -27,9 +27,14 @@
import static org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
import static org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.getMarketplacePlanFromImageMetadata;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import static org.jclouds.azurecompute.arm.util.VMImages.isCustom;
import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -44,6 +49,7 @@
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
import org.jclouds.azurecompute.arm.compute.functions.CustomImageToVMImage;
import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
+import org.jclouds.azurecompute.arm.compute.options.IpOptions;
import org.jclouds.azurecompute.arm.compute.strategy.CleanupResources;
import org.jclouds.azurecompute.arm.domain.AvailabilitySet;
import org.jclouds.azurecompute.arm.domain.CreationData;
@@ -62,6 +68,7 @@
import org.jclouds.azurecompute.arm.domain.OSProfile;
import org.jclouds.azurecompute.arm.domain.Offer;
import org.jclouds.azurecompute.arm.domain.Plan;
+import org.jclouds.azurecompute.arm.domain.Provisionable;
import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
import org.jclouds.azurecompute.arm.domain.ResourceGroup;
@@ -75,13 +82,15 @@
import org.jclouds.azurecompute.arm.domain.Version;
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface.NetworkInterfaceProperties;
+import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
import org.jclouds.azurecompute.arm.features.OSImageApi;
-import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.location.Region;
import org.jclouds.logging.Logger;
@@ -105,6 +114,7 @@
public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VirtualMachine, VMHardware, VMImage, Location> {
public static final String GROUP_KEY = "jclouds_group";
+ public static final String AUTOGENERATED_IP_KEY = "jclouds-autogenerated";
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
@@ -116,40 +126,40 @@
private final Supplier<Set<String>> regionIds;
private final PublicIpAvailablePredicateFactory publicIpAvailable;
private final CustomImageToVMImage customImagetoVmImage;
+ private final GroupNamingConvention namingConvention;
+ private Predicate<Supplier<Provisionable>> resourceAvailable;
@Inject
AzureComputeServiceAdapter(final AzureComputeApi api, @Named(IMAGE_PUBLISHERS) String imagePublishers,
CleanupResources cleanupResources, @Region Supplier<Set<String>> regionIds,
- PublicIpAvailablePredicateFactory publicIpAvailable,
- CustomImageToVMImage customImagetoVmImage) {
+ PublicIpAvailablePredicateFactory publicIpAvailable, CustomImageToVMImage customImagetoVmImage,
+ GroupNamingConvention.Factory namingConvention, Predicate<Supplier<Provisionable>> resourceAvailable) {
this.api = api;
this.imagePublishers = Splitter.on(',').trimResults().omitEmptyStrings().splitToList(imagePublishers);
this.cleanupResources = cleanupResources;
this.regionIds = regionIds;
this.publicIpAvailable = publicIpAvailable;
this.customImagetoVmImage = customImagetoVmImage;
+ this.namingConvention = namingConvention.create();
+ this.resourceAvailable = resourceAvailable;
}
@Override
public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(final String group, final String name, final Template template) {
- // TODO network ids => create one nic in each network
-
String locationName = template.getLocation().getId();
Image image = template.getImage();
String hardwareId = fromSlashEncoded(template.getHardware().getId()).name();
- // TODO ARM specific options
AzureTemplateOptions templateOptions = template.getOptions().as(AzureTemplateOptions.class);
- String subnetId = templateOptions.getSubnetId();
String resourceGroupName = templateOptions.getResourceGroup();
IdReference availabilitySet = getAvailabilitySetIdReference(templateOptions.getAvailabilitySet());
+ NetworkProfile networkProfile = createNetworkProfile(createNetworkInterfaceCards(name, locationName,
+ templateOptions));
StorageProfile storageProfile = createStorageProfile(image, templateOptions.getDataDisks());
- NetworkInterfaceCard nic = createNetworkInterfaceCard(subnetId, name, locationName, resourceGroupName, template.getOptions());
HardwareProfile hardwareProfile = HardwareProfile.builder().vmSize(hardwareId).build();
OSProfile osProfile = createOsProfile(name, template);
- NetworkProfile networkProfile = NetworkProfile.builder().networkInterfaces(of(IdReference.create(nic.id()))).build();
+
VirtualMachineProperties virtualMachineProperties = VirtualMachineProperties.builder()
- .licenseType(null) // TODO
.availabilitySet(availabilitySet)
.hardwareProfile(hardwareProfile)
.storageProfile(storageProfile)
@@ -159,11 +169,11 @@
// Store group apart from the name to be able to identify nodes with
// custom names in the configured group
- template.getOptions().getUserMetadata().put(GROUP_KEY, group);
- Map<String, String> metadataAndTags = metadataAndTagsAsCommaDelimitedValue(template.getOptions());
- Plan plan = getMarketplacePlanFromImageMetadata(template.getImage());
+ templateOptions.getUserMetadata().put(GROUP_KEY, group);
+ Map<String, String> metadataAndTags = metadataAndTagsAsCommaDelimitedValue(templateOptions);
+ Plan plan = getMarketplacePlanFromImageMetadata(image);
- VirtualMachine virtualMachine = api.getVirtualMachineApi(resourceGroupName).createOrUpdate(name, template.getLocation().getId(),
+ VirtualMachine virtualMachine = api.getVirtualMachineApi(resourceGroupName).createOrUpdate(name, locationName,
virtualMachineProperties, metadataAndTags, plan);
// Safe to pass null credentials here, as jclouds will default populate
@@ -383,39 +393,113 @@
return builder.build();
}
- private NetworkInterfaceCard createNetworkInterfaceCard(String subnetId, String name, String locationName,
- String azureGroup, TemplateOptions options) {
- final PublicIPAddressApi ipApi = api.getPublicIPAddressApi(azureGroup);
+ private List<NetworkInterfaceCard> createNetworkInterfaceCards(final String nodeName, final String location,
+ AzureTemplateOptions options) {
+ // Prefer a sorted list of NICs with the ones with public IPs first, to
+ // make sure the primary NIC is the public one
+ final String securityGroup = getOnlyElement(options.getGroups(), null);
+ return Lists.transform(publicIpsFirst(options.getIpOptions()), new Function<IpOptions, NetworkInterfaceCard>() {
+ @Override
+ public NetworkInterfaceCard apply(IpOptions input) {
+ return createNetworkInterfaceCard(input, nodeName, location, securityGroup);
+ }
+ });
+ }
+
+ private NetworkInterfaceCard createNetworkInterfaceCard(IpOptions ipConfig, String nodeName, String location,
+ String securityGroup) {
+ String resourceGroup = extractResourceGroup(ipConfig.subnet());
+ String subnetName = extractName(ipConfig.subnet());
- PublicIPAddressProperties properties = PublicIPAddressProperties.builder().publicIPAllocationMethod("Static")
- .idleTimeoutInMinutes(4).build();
+ IpConfigurationProperties.Builder ipProperties = IpConfigurationProperties.builder()
+ .subnet(IdReference.create(ipConfig.subnet()))
+ .privateIPAllocationMethod(ipConfig.address().isPresent() ? "Static" : "Dynamic")
+ .privateIPAddress(ipConfig.address().orNull());
- String publicIpAddressName = "public-address-" + name;
- PublicIPAddress ip = ipApi.createOrUpdate(publicIpAddressName, locationName, ImmutableMap.of("jclouds", name),
- properties);
+ configurePublicIP(ipConfig, ipProperties, resourceGroup, location, nodeName);
- checkState(publicIpAvailable.create(azureGroup).apply(publicIpAddressName),
- "Public IP was not provisioned in the configured timeout");
+ String ipName = namingConvention.uniqueNameForGroup(subnetName);
+ final String nicName = namingConvention.uniqueNameForGroup(subnetName);
- final NetworkInterfaceCardProperties.Builder networkInterfaceCardProperties = NetworkInterfaceCardProperties
- .builder()
- .ipConfigurations(
- of(IpConfiguration
- .builder()
- .name("ipConfig-" + name)
- .properties(
- IpConfigurationProperties.builder().privateIPAllocationMethod("Dynamic")
- .publicIPAddress(IdReference.create(ip.id())).subnet(IdReference.create(subnetId))
- .build()).build()));
+ IpConfiguration config = IpConfiguration.builder().name(ipName).properties(ipProperties.build()).build();
- String securityGroup = getOnlyElement(options.getGroups(), null);
+ NetworkInterfaceCardProperties.Builder nicProperties = NetworkInterfaceCardProperties.builder().ipConfigurations(
+ ImmutableList.of(config));
+
if (securityGroup != null) {
- networkInterfaceCardProperties.networkSecurityGroup(IdReference.create(securityGroup));
+ nicProperties.networkSecurityGroup(IdReference.create(securityGroup));
}
- String networkInterfaceCardName = "jc-nic-" + name;
- return api.getNetworkInterfaceCardApi(azureGroup).createOrUpdate(networkInterfaceCardName, locationName,
- networkInterfaceCardProperties.build(), ImmutableMap.of("jclouds", name));
+ logger.debug(">> creating nic %s(%s) with security groups (%s)", nicName, config,
+ securityGroup != null ? securityGroup : "");
+
+ final NetworkInterfaceCardApi nicApi = api.getNetworkInterfaceCardApi(resourceGroup);
+ NetworkInterfaceCard nic = nicApi.createOrUpdate(nicName, location, nicProperties.build(),
+ ImmutableMap.of("jclouds", nodeName));
+
+ resourceAvailable.apply(new Supplier<Provisionable>() {
+ @Override
+ public Provisionable get() {
+ NetworkInterfaceCard updated = nicApi.get(nicName);
+ return updated == null ? null : updated.properties();
+ }
+ });
+
+ return nic;
+ }
+
+ private void configurePublicIP(IpOptions ipConfig, IpConfigurationProperties.Builder ipProperties,
+ String resourceGroup, String location, String nodeName) {
+ if (ipConfig.publicIpId() != null) {
+ logger.debug(">> configuring public ip: %s", extractName(ipConfig.publicIpId()));
+ PublicIPAddress publicIp = api.getPublicIPAddressApi(extractResourceGroup(ipConfig.publicIpId())).get(
+ extractName(ipConfig.publicIpId()));
+ ipProperties.publicIPAddress(IdReference.create(publicIp.id()));
+ } else if (ipConfig.allocateNewPublicIp()) {
+ PublicIPAddress publicIp = createPublicIp(resourceGroup, location, nodeName);
+ ipProperties.publicIPAddress(IdReference.create(publicIp.id()));
+ }
+ }
+
+ /**
+ * Create the network profile and configure the first NIC as primary.
+ */
+ private NetworkProfile createNetworkProfile(List<NetworkInterfaceCard> nics) {
+ List<NetworkInterface> nicAttachments = new ArrayList<NetworkInterface>(nics.size());
+ for (int i = 0; i < nics.size(); i++) {
+ nicAttachments.add(NetworkInterface.create(nics.get(i).id(), NetworkInterfaceProperties.create(i == 0)));
+ }
+ return NetworkProfile.create(nicAttachments);
+ }
+
+ private static List<IpOptions> publicIpsFirst(List<IpOptions> ipOptions) {
+ List<IpOptions> sorted = new ArrayList<IpOptions>(ipOptions);
+ Collections.sort(sorted, new Comparator<IpOptions>() {
+ @Override
+ public int compare(IpOptions o1, IpOptions o2) {
+ return o1.allocateNewPublicIp() == o2.allocateNewPublicIp() ? 0 : o1.allocateNewPublicIp() ? -1 : 1;
+ }
+ });
+ return sorted;
+ }
+
+ private PublicIPAddress createPublicIp(String resourceGroup, String location, String nodeName) {
+ String name = namingConvention.uniqueNameForGroup(nodeName);
+
+ PublicIPAddressProperties properties = PublicIPAddressProperties.builder()
+ .publicIPAllocationMethod("Static")
+ .idleTimeoutInMinutes(4)
+ .build();
+
+ logger.debug(">> allocating new public ip address: %s", name);
+
+ PublicIPAddress ip = api.getPublicIPAddressApi(resourceGroup).createOrUpdate(name, location,
+ ImmutableMap.of("jclouds", nodeName, AUTOGENERATED_IP_KEY, "true"), properties);
+
+ checkState(publicIpAvailable.create(resourceGroup).apply(name),
+ "Public IP was not provisioned in the configured timeout");
+
+ return ip;
}
private StorageProfile createStorageProfile(Image image, List<DataDisk> dataDisks) {
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
index 59608a4..50cb75a 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
@@ -23,6 +23,8 @@
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import java.net.URI;
import java.util.ArrayList;
@@ -36,8 +38,8 @@
import org.jclouds.azurecompute.arm.AzureComputeApi;
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.SecurityGroupAvailablePredicateFactory;
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
-import org.jclouds.azurecompute.arm.domain.IdReference;
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroupProperties;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityRule;
@@ -135,12 +137,12 @@
if (vm == null) {
throw new IllegalArgumentException("Node " + nodeId + " was not found");
}
- List<IdReference> networkInterfacesIdReferences = vm.properties().networkProfile().networkInterfaces();
+ List<NetworkInterface> networkInterfaces = vm.properties().networkProfile().networkInterfaces();
List<NetworkSecurityGroup> networkGroups = new ArrayList<NetworkSecurityGroup>();
- for (IdReference networkInterfaceCardIdReference : networkInterfacesIdReferences) {
- String nicName = networkInterfaceCardIdReference.name();
- String nicResourceGroup = networkInterfaceCardIdReference.resourceGroup();
+ for (NetworkInterface networkInterfaceCardIdReference : networkInterfaces) {
+ String nicName = extractName(networkInterfaceCardIdReference.id());
+ String nicResourceGroup = extractResourceGroup(networkInterfaceCardIdReference.id());
NetworkInterfaceCard card = api.getNetworkInterfaceCardApi(nicResourceGroup).get(nicName);
if (card != null && card.properties().networkSecurityGroup() != null) {
String secGroupName = card.properties().networkSecurityGroup().name();
@@ -171,9 +173,14 @@
SecurityGroupBuilder builder = new SecurityGroupBuilder();
builder.name(name);
builder.location(location);
+
+ NetworkSecurityGroup sg = api.getNetworkSecurityGroupApi(resourceGroup.name()).createOrUpdate(name,
+ location.getId(), null, NetworkSecurityGroupProperties.builder().build());
+
+ checkState(securityGroupAvailable.create(resourceGroup.name()).apply(name),
+ "Security group was not created in the configured timeout");
- return securityGroupConverter.apply(api.getNetworkSecurityGroupApi(resourceGroup.name()).createOrUpdate(name,
- location.getId(), null, NetworkSecurityGroupProperties.builder().build()));
+ return securityGroupConverter.apply(sg);
}
@Override
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
index e4b7da8..af43cbb 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
@@ -22,6 +22,7 @@
import static org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter.GROUP_KEY;
import static org.jclouds.azurecompute.arm.compute.domain.LocationAndName.fromLocationAndName;
import static org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
import static org.jclouds.location.predicates.LocationPredicates.idEquals;
@@ -40,6 +41,7 @@
import org.jclouds.azurecompute.arm.domain.IdReference;
import org.jclouds.azurecompute.arm.domain.IpConfiguration;
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.StorageProfile;
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
@@ -136,9 +138,9 @@
return builder.build();
}
- private Iterable<String> getPrivateIpAddresses(List<IdReference> idReferences) {
+ private Iterable<String> getPrivateIpAddresses(List<NetworkInterface> networkInterfaces) {
List<String> privateIpAddresses = Lists.newArrayList();
- for (IdReference networkInterfaceCardIdReference : idReferences) {
+ for (NetworkInterface networkInterfaceCardIdReference : networkInterfaces) {
NetworkInterfaceCard networkInterfaceCard = getNetworkInterfaceCard(networkInterfaceCardIdReference);
if (networkInterfaceCard != null && networkInterfaceCard.properties() != null
&& networkInterfaceCard.properties().ipConfigurations() != null) {
@@ -152,21 +154,21 @@
return privateIpAddresses;
}
- private NetworkInterfaceCard getNetworkInterfaceCard(IdReference nic) {
- return api.getNetworkInterfaceCardApi(nic.resourceGroup()).get(nic.name());
+ private NetworkInterfaceCard getNetworkInterfaceCard(NetworkInterface nic) {
+ return api.getNetworkInterfaceCardApi(extractResourceGroup(nic.id())).get(extractName(nic.id()));
}
- private Iterable<String> getPublicIpAddresses(List<IdReference> idReferences) {
+ private Iterable<String> getPublicIpAddresses(List<NetworkInterface> networkInterfaces) {
List<String> publicIpAddresses = Lists.newArrayList();
- for (IdReference networkInterfaceCardIdReference : idReferences) {
+ for (NetworkInterface networkInterfaceCardIdReference : networkInterfaces) {
NetworkInterfaceCard networkInterfaceCard = getNetworkInterfaceCard(networkInterfaceCardIdReference);
if (networkInterfaceCard != null && networkInterfaceCard.properties() != null
&& networkInterfaceCard.properties().ipConfigurations() != null) {
- String resourceGroup = networkInterfaceCardIdReference.resourceGroup();
for (IpConfiguration ipConfiguration : networkInterfaceCard.properties().ipConfigurations()) {
if (ipConfiguration.properties().publicIPAddress() != null) {
IdReference publicIpId = ipConfiguration.properties().publicIPAddress();
- PublicIPAddress publicIp = api.getPublicIPAddressApi(resourceGroup).get(publicIpId.name());
+ PublicIPAddress publicIp = api.getPublicIPAddressApi(publicIpId.resourceGroup()).get(
+ publicIpId.name());
if (publicIp != null && publicIp.properties().ipAddress() != null) {
publicIpAddresses.add(publicIp.properties().ipAddress());
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java
index bb5dc09..98732d2 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java
@@ -16,6 +16,7 @@
*/
package org.jclouds.azurecompute.arm.compute.loaders;
+import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.compute.util.ComputeServiceUtils.getPortRangesFromList;
import java.util.ArrayList;
@@ -28,6 +29,7 @@
import javax.inject.Singleton;
import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.SecurityGroupAvailablePredicateFactory;
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndNameAndIngressRules;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroupProperties;
@@ -48,10 +50,12 @@
protected Logger logger = Logger.NULL;
private final AzureComputeApi api;
+ private final SecurityGroupAvailablePredicateFactory securityGroupAvailable;
@Inject
- CreateSecurityGroupIfNeeded(AzureComputeApi api) {
+ CreateSecurityGroupIfNeeded(AzureComputeApi api, SecurityGroupAvailablePredicateFactory securityRuleAvailable) {
this.api = api;
+ this.securityGroupAvailable = securityRuleAvailable;
}
@Override
@@ -86,6 +90,9 @@
NetworkSecurityGroup securityGroup = api.getNetworkSecurityGroupApi(resourceGroup).createOrUpdate(name, location,
null, NetworkSecurityGroupProperties.builder().securityRules(rules).build());
+
+ checkState(securityGroupAvailable.create(resourceGroup).apply(name),
+ "Security group was not created in the configured timeout");
return securityGroup.id();
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
index d211421..c3140dd 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
@@ -32,28 +32,11 @@
*/
public class AzureTemplateOptions extends TemplateOptions implements Cloneable {
- private String virtualNetworkName;
- private String subnetId;
private AvailabilitySet availabilitySet;
private String availabilitySetName;
private List<DataDisk> dataDisks = ImmutableList.of();
private String resourceGroup;
-
- /**
- * Sets the virtual network name
- */
- public AzureTemplateOptions virtualNetworkName(String virtualNetworkName) {
- this.virtualNetworkName = virtualNetworkName;
- return this;
- }
-
- /**
- * Sets the subnet name
- */
- public AzureTemplateOptions subnetId(String subnetId) {
- this.subnetId = subnetId;
- return this;
- }
+ private List<IpOptions> ipOptions = ImmutableList.of();
/**
* Sets the availability set where the nodes will be configured. If it does
@@ -92,12 +75,35 @@
return dataDisks(ImmutableList.copyOf(checkNotNull(dataDisks, "dataDisks")));
}
- public String getVirtualNetworkName() { return virtualNetworkName; }
- public String getSubnetId() { return subnetId; }
+ /**
+ * Configure the NICs that will be attached to the created nodes.
+ * <p>
+ * Note that the number of NICs that can be attached depends on the size of
+ * the virtual machine, and that the guest operating system needs to be
+ * prepared to set up all the configured interfaces.
+ * <p>
+ * Depending on the image being used, a cloud-init or bootstrap script might
+ * be needed to make the interface setup.
+ */
+ public AzureTemplateOptions ipOptions(Iterable<IpOptions> ipOptions) {
+ for (IpOptions ipOption : checkNotNull(ipOptions, "ipOptions"))
+ checkNotNull(ipOption, "all ipOptions must be non-empty");
+ this.ipOptions = ImmutableList.copyOf(ipOptions);
+ return this;
+ }
+
+ /**
+ * @see {@link AzureTemplateOptions#ipOptions(Iterable)
+ */
+ public AzureTemplateOptions ipOptions(IpOptions... ipOptions) {
+ return ipOptions(ImmutableList.copyOf(checkNotNull(ipOptions, "ipOptions")));
+ }
+
public AvailabilitySet getAvailabilitySet() { return availabilitySet; }
public String getAvailabilitySetName() { return availabilitySetName; }
public List<DataDisk> getDataDisks() { return dataDisks; }
public String getResourceGroup() { return resourceGroup; }
+ public List<IpOptions> getIpOptions() { return ipOptions; }
@Override
public AzureTemplateOptions clone() {
@@ -111,12 +117,11 @@
super.copyTo(to);
if (to instanceof AzureTemplateOptions) {
AzureTemplateOptions eTo = AzureTemplateOptions.class.cast(to);
- eTo.virtualNetworkName(virtualNetworkName);
- eTo.subnetId(subnetId);
eTo.availabilitySet(availabilitySet);
eTo.availabilitySet(availabilitySetName);
eTo.dataDisks(dataDisks);
eTo.resourceGroup(resourceGroup);
+ eTo.ipOptions(ipOptions);
}
}
@@ -128,27 +133,22 @@
AzureTemplateOptions that = (AzureTemplateOptions) o;
- return Objects.equal(virtualNetworkName, that.virtualNetworkName) &&
- Objects.equal(subnetId, that.subnetId) &&
+ return Objects.equal(availabilitySetName, that.availabilitySetName) &&
+ Objects.equal(resourceGroup, that.resourceGroup) &&
Objects.equal(availabilitySet, that.availabilitySet) &&
- Objects.equal(availabilitySetName, that.availabilitySetName) &&
Objects.equal(dataDisks, that.dataDisks) &&
- Objects.equal(resourceGroup, that.resourceGroup);
+ Objects.equal(ipOptions, that.ipOptions);
}
@Override
public int hashCode() {
- return Objects.hashCode(virtualNetworkName, subnetId, availabilitySet, availabilitySetName, dataDisks,
- resourceGroup);
+ return Objects.hashCode(availabilitySet, availabilitySetName, dataDisks,
+ resourceGroup, ipOptions);
}
@Override
public Objects.ToStringHelper string() {
Objects.ToStringHelper toString = super.string();
- if (virtualNetworkName != null)
- toString.add("virtualNetworkName", virtualNetworkName);
- if (subnetId != null)
- toString.add("subnetId", subnetId);
if (availabilitySet != null)
toString.add("availabilitySet", availabilitySet);
if (availabilitySetName != null)
@@ -157,26 +157,12 @@
toString.add("dataDisks", dataDisks);
if (resourceGroup != null)
toString.add("resourceGroup", resourceGroup);
+ if (!ipOptions.isEmpty())
+ toString.add("ipOptions", ipOptions);
return toString;
}
public static class Builder {
-
- /**
- * @see AzureTemplateOptions#virtualNetworkName(String)
- */
- public static AzureTemplateOptions virtualNetworkName(String virtualNetworkName) {
- AzureTemplateOptions options = new AzureTemplateOptions();
- return options.virtualNetworkName(virtualNetworkName);
- }
-
- /**
- * @see AzureTemplateOptions#subnetId(String)
- */
- public static AzureTemplateOptions subnetId(String subnetId) {
- AzureTemplateOptions options = new AzureTemplateOptions();
- return options.subnetId(subnetId);
- }
/**
* @see AzureTemplateOptions#availabilitySet(AvailabilitySet)
@@ -217,5 +203,21 @@
AzureTemplateOptions options = new AzureTemplateOptions();
return options.resourceGroup(resourceGroup);
}
+
+ /**
+ * @see AzureTemplateOptions#ipOptions(IpOptions...)
+ */
+ public static AzureTemplateOptions ipOptions(IpOptions... ipOptions) {
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ return options.ipOptions(ipOptions);
+ }
+
+ /**
+ * @see AzureTemplateOptions#ipOptions(Iterable)
+ */
+ public static AzureTemplateOptions ipOptions(Iterable<IpOptions> ipOptions) {
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ return options.ipOptions(ipOptions);
+ }
}
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java
new file mode 100644
index 0000000..73c4c6c
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.compute.options;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+
+/**
+ * Configures the ip addresses to be configured for the created nodes.
+ */
+@AutoValue
+public abstract class IpOptions {
+
+ /**
+ * The subnet where the NIC will be attached.
+ */
+ public abstract String subnet();
+
+ /**
+ * The IP address to be configured, in case of static allocation, or absent
+ * for dynamic assignment.
+ */
+ public abstract Optional<String> address();
+
+ /**
+ * Flag to indicate if a public ip address should be allocated and bound to
+ * this NIC.
+ */
+ public abstract boolean allocateNewPublicIp();
+
+ /**
+ * ID of the public IP to associate with the NIC.
+ */
+ @Nullable
+ public abstract String publicIpId();
+
+ IpOptions() {
+
+ }
+
+ public abstract Builder toBuilder();
+
+ public static Builder builder() {
+ return new AutoValue_IpOptions.Builder().address((String) null).allocateNewPublicIp(false);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder subnet(String subnet);
+ public abstract Builder allocateNewPublicIp(boolean allocatePublicIp);
+ public abstract Builder publicIpId(String publicIpId);
+
+ abstract Builder address(Optional<String> address);
+ public Builder address(String address) {
+ return address(Optional.fromNullable(address));
+ }
+
+ public abstract IpOptions build();
+ }
+}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java
index 1f1a37d..3ca1a5d 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java
@@ -21,7 +21,10 @@
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.filterValues;
+import static org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter.AUTOGENERATED_IP_KEY;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import java.net.URI;
import java.util.HashMap;
@@ -42,8 +45,10 @@
import org.jclouds.azurecompute.arm.domain.IpConfiguration;
import org.jclouds.azurecompute.arm.domain.ManagedDiskParameters;
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
import org.jclouds.azurecompute.arm.domain.OSDisk;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
import org.jclouds.compute.functions.GroupNamingConvention;
@@ -98,11 +103,11 @@
public boolean cleanupVirtualMachineNICs(VirtualMachine virtualMachine) {
boolean deleted = true;
- for (IdReference nicRef : virtualMachine.properties().networkProfile().networkInterfaces()) {
- String nicResourceGroup = nicRef.resourceGroup();
- String nicName = nicRef.name();
- NetworkInterfaceCard nic = api.getNetworkInterfaceCardApi(nicRef.resourceGroup()).get(nicName);
-
+ for (NetworkInterface nicRef : virtualMachine.properties().networkProfile().networkInterfaces()) {
+ String nicResourceGroup = extractResourceGroup(nicRef.id());
+ String nicName = extractName(nicRef.id());
+ NetworkInterfaceCard nic = api.getNetworkInterfaceCardApi(nicResourceGroup).get(nicName);
+
Iterable<IdReference> publicIps = getPublicIps(nic);
logger.debug(">> destroying nic %s...", nicName);
@@ -112,9 +117,12 @@
for (IdReference publicIp : publicIps) {
String publicIpResourceGroup = publicIp.resourceGroup();
String publicIpName = publicIp.name();
-
- logger.debug(">> deleting public ip nic %s...", publicIpName);
- deleted &= api.getPublicIPAddressApi(publicIpResourceGroup).delete(publicIpName);
+
+ PublicIPAddress ip = api.getPublicIPAddressApi(publicIpResourceGroup).get(publicIpName);
+ if (ip.tags() != null && Boolean.parseBoolean(ip.tags().get(AUTOGENERATED_IP_KEY))) {
+ logger.debug(">> deleting public ip %s...", publicIpName);
+ deleted &= api.getPublicIPAddressApi(publicIpResourceGroup).delete(publicIpName);
+ }
}
}
return deleted;
@@ -129,15 +137,15 @@
for (DataDisk dataDisk : virtualMachine.properties().storageProfile().dataDisks()) {
deleteManagedDisk(dataDisk.managedDiskParameters(), deleteJobs);
}
-
+
Set<String> nonDeletedDisks = filterValues(deleteJobs, not(resourceDeleted)).keySet();
if (!nonDeletedDisks.isEmpty()) {
logger.warn(">> could not delete disks: %s", Joiner.on(',').join(nonDeletedDisks));
}
-
+
return nonDeletedDisks.isEmpty();
}
-
+
private void deleteManagedDisk(@Nullable ManagedDiskParameters managedDisk, Map<String, URI> deleteJobs) {
if (managedDisk != null) {
IdReference diskRef = IdReference.create(managedDisk.id());
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java
index c3bdbdd..2ddb340 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java
@@ -17,10 +17,13 @@
package org.jclouds.azurecompute.arm.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_SUBNET_ADDRESS_PREFIX;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_VNET_ADDRESS_SPACE_PREFIX;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
+import static org.jclouds.azurecompute.arm.domain.Subnet.extractVirtualNetwork;
import java.util.Arrays;
import java.util.Map;
@@ -37,13 +40,15 @@
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndNameAndIngressRules;
import org.jclouds.azurecompute.arm.compute.functions.TemplateToAvailabilitySet;
import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
+import org.jclouds.azurecompute.arm.compute.options.IpOptions;
import org.jclouds.azurecompute.arm.domain.AvailabilitySet;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.ResourceGroup;
import org.jclouds.azurecompute.arm.domain.Subnet;
-import org.jclouds.azurecompute.arm.domain.VirtualNetwork;
-import org.jclouds.azurecompute.arm.features.SubnetApi;
-import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
+import org.jclouds.azurecompute.arm.domain.Subnet.SubnetProperties;
+import org.jclouds.azurecompute.arm.domain.VirtualNetwork.AddressSpace;
+import org.jclouds.azurecompute.arm.domain.VirtualNetwork.VirtualNetworkProperties;
import org.jclouds.compute.config.CustomizationResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
@@ -56,8 +61,9 @@
import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;
-import com.google.common.base.Optional;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
@@ -89,8 +95,7 @@
TemplateToAvailabilitySet templateToAvailabilitySet) {
super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
- this.api = checkNotNull(api, "api cannot be null");
- checkNotNull(userExecutor, "userExecutor cannot be null");
+ this.api = api;
this.securityGroupMap = securityGroupMap;
this.defaultVnetAddressPrefix = defaultVnetAddressPrefix;
this.defaultSubnetAddressPrefix = defaultSubnetAddressPrefix;
@@ -103,7 +108,7 @@
Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
AzureTemplateOptions options = template.getOptions().as(AzureTemplateOptions.class);
-
+
// If there is a script to be run on the node and public key
// authentication has been configured, warn users if the private key
// is not present
@@ -112,42 +117,38 @@
+ "Authentication will delegate to the ssh-agent");
}
- // This sill create the resource group if it does not exist
String location = template.getLocation().getId();
createResourceGroupIfNeeded(group, location, options);
- getOrCreateVirtualNetworkWithSubnet(location, options);
+
+ normalizeNetworkOptions(options);
+ createDefaultNetworkIfNeeded(group, location, options);
+
configureSecurityGroupForOptions(group, template.getLocation(), options);
configureAvailabilitySetForTemplate(template);
return super.execute(group, count, template, goodNodes, badNodes, customizationResponses);
}
- protected synchronized void getOrCreateVirtualNetworkWithSubnet(final String location, AzureTemplateOptions options) {
- String virtualNetworkName = Optional.fromNullable(options.getVirtualNetworkName()).or(
- options.getResourceGroup() + "virtualnetwork");
- String subnetName = options.getResourceGroup() + "subnet";
-
- // Subnets belong to a virtual network so that needs to be created first
- VirtualNetworkApi vnApi = api.getVirtualNetworkApi(options.getResourceGroup());
- VirtualNetwork vn = vnApi.get(virtualNetworkName);
-
- if (vn == null) {
- Subnet subnet = Subnet.create(subnetName, null, null,
- Subnet.SubnetProperties.builder().addressPrefix(defaultSubnetAddressPrefix).build());
-
- VirtualNetwork.VirtualNetworkProperties virtualNetworkProperties = VirtualNetwork.VirtualNetworkProperties
- .builder().addressSpace(VirtualNetwork.AddressSpace.create(Arrays.asList(defaultVnetAddressPrefix)))
+ protected synchronized void createDefaultNetworkIfNeeded(String group, String location, AzureTemplateOptions options) {
+ if (options.getIpOptions().isEmpty()) {
+ String name = namingConvention.create().sharedNameForGroup(group);
+
+ Subnet subnet = Subnet.builder().name(name)
+ .properties(SubnetProperties.builder().addressPrefix(defaultSubnetAddressPrefix).build()).build();
+
+ VirtualNetworkProperties properties = VirtualNetworkProperties.builder()
+ .addressSpace(AddressSpace.create(Arrays.asList(defaultVnetAddressPrefix)))
.subnets(Arrays.asList(subnet)).build();
-
- vn = vnApi.createOrUpdate(virtualNetworkName, location, virtualNetworkProperties);
+
+ logger.debug(">> network options have not been configured. Creating network %s(%s) and subnet %s(%s)", name,
+ defaultVnetAddressPrefix, name, defaultSubnetAddressPrefix);
+
+ api.getVirtualNetworkApi(options.getResourceGroup()).createOrUpdate(name, location, properties);
+ Subnet createdSubnet = api.getSubnetApi(options.getResourceGroup(), name).get(name);
+
+ options.ipOptions(IpOptions.builder().subnet(createdSubnet.id()).allocateNewPublicIp(true).build());
}
-
- SubnetApi subnetApi = api.getSubnetApi(options.getResourceGroup(), virtualNetworkName);
- Subnet subnet = subnetApi.get(subnetName);
-
- options.virtualNetworkName(virtualNetworkName);
- options.subnetId(subnet.id());
}
private static boolean hasRunScriptWithKeyAuthAndNoPrivateKey(Template template) {
@@ -196,4 +197,45 @@
ImmutableMap.of("description", "jclouds default resource group"));
}
}
+
+ @VisibleForTesting
+ void normalizeNetworkOptions(AzureTemplateOptions options) {
+ if (!options.getNetworks().isEmpty() && !options.getIpOptions().isEmpty()) {
+ throw new IllegalArgumentException("The options.networks and options.ipOptions are exclusive");
+ }
+
+ if (!options.getNetworks().isEmpty() && options.getIpOptions().isEmpty()) {
+ // The portable interface allows to configure network IDs (subnet IDs),
+ // but we don't know the type of the IP configurations to be applied
+ // when attaching nodes to those networks. We'll assume private IPs
+ // with Dynamic allocation and no public ip address associated.
+ ImmutableList.Builder<IpOptions> ipOptions = ImmutableList.builder();
+ for (String subnetId : options.getNetworks()) {
+ ipOptions.add(IpOptions.builder().subnet(subnetId).build());
+ }
+ options.ipOptions(ipOptions.build());
+ }
+
+ if (!options.getIpOptions().isEmpty()) {
+ // Eagerly validate that all configured subnets exist.
+ for (IpOptions ipConfig : options.getIpOptions()) {
+ if (ipConfig.allocateNewPublicIp() && ipConfig.publicIpId() != null) {
+ throw new IllegalArgumentException("The allocateNewPublicIps and publicIpId are exclusive");
+ }
+
+ String resourceGroup = extractResourceGroup(ipConfig.subnet());
+ String networkName = extractVirtualNetwork(ipConfig.subnet());
+ String subnetName = extractName(ipConfig.subnet());
+
+ Subnet subnet = api.getSubnetApi(resourceGroup, networkName).get(subnetName);
+ checkState(subnet != null, "Configured subnet %s does not exist", ipConfig.subnet());
+
+ if (ipConfig.publicIpId() != null) {
+ PublicIPAddress publicIp = api.getPublicIPAddressApi(extractResourceGroup(ipConfig.publicIpId())).get(
+ extractName(ipConfig.publicIpId()));
+ checkState(publicIp != null, "Configured public ip %s does not exist", ipConfig.publicIpId());
+ }
+ }
+ }
+ }
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
index d1976f1..84c8ca2 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
@@ -26,23 +26,16 @@
@AutoValue
public abstract class NetworkInterfaceCardProperties implements Provisionable {
- @Nullable
- public abstract String provisioningState();
-
- @Nullable
- public abstract String resourceGuid();
-
- @Nullable
- public abstract Boolean enableIPForwarding();
-
- @Nullable
- public abstract List<IpConfiguration> ipConfigurations();
-
- @Nullable
- public abstract IdReference networkSecurityGroup();
+ @Nullable public abstract String provisioningState();
+ @Nullable public abstract String resourceGuid();
+ @Nullable public abstract Boolean enableIPForwarding();
+ @Nullable public abstract List<IpConfiguration> ipConfigurations();
+ @Nullable public abstract IdReference networkSecurityGroup();
@SerializedNames({"provisioningState", "resourceGuid", "enableIPForwarding", "ipConfigurations", "networkSecurityGroup"})
- public static NetworkInterfaceCardProperties create(final String provisioningState, final String resourceGuid, final Boolean enableIPForwarding, final List<IpConfiguration> ipConfigurations, final IdReference networkSecurityGroup) {
+ public static NetworkInterfaceCardProperties create(final String provisioningState, final String resourceGuid,
+ final Boolean enableIPForwarding, final List<IpConfiguration> ipConfigurations,
+ final IdReference networkSecurityGroup) {
NetworkInterfaceCardProperties.Builder builder = NetworkInterfaceCardProperties.builder()
.provisioningState(provisioningState)
.resourceGuid(resourceGuid)
@@ -52,28 +45,26 @@
return builder.build();
}
-
+
+ NetworkInterfaceCardProperties() {
+
+ }
+
public abstract Builder toBuilder();
public static Builder builder() {
-
return new AutoValue_NetworkInterfaceCardProperties.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder provisioningState(String provisioningState);
-
public abstract Builder resourceGuid(String resourceGuid);
-
public abstract Builder enableIPForwarding(Boolean enableIPForwarding);
-
public abstract Builder ipConfigurations(List<IpConfiguration> ipConfigurations);
-
- abstract List<IpConfiguration> ipConfigurations();
-
public abstract Builder networkSecurityGroup(IdReference networkSecurityGroup);
+ abstract List<IpConfiguration> ipConfigurations();
abstract NetworkInterfaceCardProperties autoBuild();
public NetworkInterfaceCardProperties build() {
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java
index b26305f..cdb6d51 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java
@@ -19,6 +19,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import java.util.List;
@@ -26,16 +27,49 @@
@AutoValue
public abstract class NetworkProfile {
+ @AutoValue
+ public abstract static class NetworkInterface {
+ public abstract String id();
+ @Nullable public abstract NetworkInterfaceProperties properties();
+
+ @AutoValue
+ public abstract static class NetworkInterfaceProperties {
+ public abstract boolean primary();
+
+ NetworkInterfaceProperties() {
+
+ }
+
+ @SerializedNames({"primary"})
+ public static NetworkInterfaceProperties create(boolean primary) {
+ return new AutoValue_NetworkProfile_NetworkInterface_NetworkInterfaceProperties(primary);
+ }
+ }
+
+ NetworkInterface() {
+
+ }
+
+ @SerializedNames({"id", "properties"})
+ public static NetworkInterface create(String id, NetworkInterfaceProperties properties) {
+ return new AutoValue_NetworkProfile_NetworkInterface(id, properties);
+ }
+ }
+
/**
* List of network interfaces
*/
- public abstract List<IdReference> networkInterfaces();
+ public abstract List<NetworkInterface> networkInterfaces();
@SerializedNames({"networkInterfaces"})
- public static NetworkProfile create(final List<IdReference> networkInterfaces) {
+ public static NetworkProfile create(final List<NetworkInterface> networkInterfaces) {
return builder().networkInterfaces(networkInterfaces).build();
}
+ NetworkProfile() {
+
+ }
+
public abstract Builder toBuilder();
public static Builder builder() {
@@ -44,14 +78,14 @@
@AutoValue.Builder
public abstract static class Builder {
- public abstract Builder networkInterfaces(List<IdReference> networkInterfaces);
+ public abstract Builder networkInterfaces(List<NetworkInterface> networkInterfaces);
- abstract List<IdReference> networkInterfaces();
+ abstract List<NetworkInterface> networkInterfaces();
abstract NetworkProfile autoBuild();
public NetworkProfile build() {
- networkInterfaces(networkInterfaces() != null ? ImmutableList.copyOf(networkInterfaces()) : ImmutableList.<IdReference>of());
+ networkInterfaces(networkInterfaces() != null ? ImmutableList.copyOf(networkInterfaces()) : ImmutableList.<NetworkInterface>of());
return autoBuild();
}
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java
index a08fadd..8d0cb2b 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java
@@ -17,36 +17,55 @@
package org.jclouds.azurecompute.arm.domain;
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
-import java.util.Map;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
@AutoValue
public abstract class PublicIPAddress {
public abstract String name();
-
public abstract String id();
-
public abstract String etag();
-
public abstract String location();
-
- @Nullable
- public abstract Map<String, String> tags();
-
+ @Nullable public abstract Map<String, String> tags();
public abstract PublicIPAddressProperties properties();
- @SerializedNames({"name", "id", "etag", "location", "tags", "properties"})
- public static PublicIPAddress create(final String name,
- final String id,
- final String etag,
- final String location,
- final Map<String, String> tags,
- final PublicIPAddressProperties properties) {
- return new AutoValue_PublicIPAddress(name, id, etag, location, tags == null ? null : ImmutableMap.copyOf(tags), properties);
+ @SerializedNames({ "name", "id", "etag", "location", "tags", "properties" })
+ public static PublicIPAddress create(String name, String id, String etag, String location, Map<String, String> tags,
+ PublicIPAddressProperties properties) {
+ return builder().name(name).id(id).etag(etag).location(location).tags(tags).properties(properties).build();
+ }
+
+ PublicIPAddress() {
+
+ }
+
+ public abstract Builder toBuilder();
+
+ public static Builder builder() {
+ return new AutoValue_PublicIPAddress.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder name(String name);
+ public abstract Builder id(String id);
+ public abstract Builder etag(String etag);
+ public abstract Builder location(String location);
+ public abstract Builder tags(Map<String, String> tags);
+ public abstract Builder properties(PublicIPAddressProperties properties);
+
+ abstract Map<String, String> tags();
+ abstract PublicIPAddress autoBuild();
+
+ public PublicIPAddress build() {
+ tags(tags() != null ? ImmutableMap.copyOf(tags()) : null);
+ return autoBuild();
+ }
}
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java
index 6830438..80460b8 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java
@@ -19,6 +19,8 @@
import static com.google.common.collect.ImmutableList.copyOf;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import com.google.common.collect.ImmutableList;
@@ -29,6 +31,8 @@
@AutoValue
public abstract class Subnet {
+ private static final Pattern NETWORK_PATTERN = Pattern.compile("^.*/virtualNetworks/([^/]+)(/.*)?$");
+
@AutoValue
public abstract static class IpConfiguration {
@@ -38,19 +42,18 @@
public static IpConfiguration create(final String id) {
return new AutoValue_Subnet_IpConfiguration(id);
}
+
+ IpConfiguration() {
+
+ }
}
@AutoValue
public abstract static class SubnetProperties implements Provisionable {
- @Nullable
- public abstract String provisioningState();
-
- @Nullable
- public abstract String addressPrefix();
-
- @Nullable
- public abstract List<IpConfiguration> ipConfigurations();
+ @Nullable public abstract String provisioningState();
+ @Nullable public abstract String addressPrefix();
+ @Nullable public abstract List<IpConfiguration> ipConfigurations();
@SerializedNames({"provisioningState", "addressPrefix", "ipConfigurations"})
public static SubnetProperties create(final String provisioningState, final String addressPrefix, final List<IpConfiguration> ipConfigurations) {
@@ -61,6 +64,10 @@
.build();
}
+ SubnetProperties() {
+
+ }
+
public abstract Builder toBuilder();
public static Builder builder() {
@@ -70,13 +77,10 @@
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder provisioningState(String provisioningState);
-
public abstract Builder addressPrefix(String addressPrefix);
-
public abstract Builder ipConfigurations(List<IpConfiguration> ipConfigurations);
abstract List<IpConfiguration> ipConfigurations();
-
abstract SubnetProperties autoBuild();
public SubnetProperties build() {
@@ -86,23 +90,47 @@
}
}
- @Nullable
- public abstract String name();
-
- @Nullable
- public abstract String id();
-
- @Nullable
- public abstract String etag();
-
- @Nullable
- public abstract SubnetProperties properties();
+ @Nullable public abstract String name();
+ @Nullable public abstract String id();
+ @Nullable public abstract String etag();
+ @Nullable public abstract SubnetProperties properties();
+
+ @Nullable public String virtualNetwork() {
+ return extractVirtualNetwork(id());
+ }
+
+ public static String extractVirtualNetwork(String id) {
+ if (id == null)
+ return null;
+ Matcher m = NETWORK_PATTERN.matcher(id);
+ m.matches();
+ return m.group(1);
+ }
@SerializedNames({"name", "id", "etag", "properties"})
public static Subnet create(final String name,
final String id,
final String etag,
final SubnetProperties properties) {
- return new AutoValue_Subnet(name, id, etag, properties);
+ return builder().name(name).id(id).etag(etag).properties(properties).build();
+ }
+
+ Subnet() {
+
+ }
+
+ public abstract Builder toBuilder();
+
+ public static Builder builder() {
+ return new AutoValue_Subnet.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder name(String name);
+ public abstract Builder id(String id);
+ public abstract Builder etag(String etag);
+ public abstract Builder properties(SubnetProperties properties);
+ public abstract Subnet build();
}
}
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java
index 2693753..75af4ff 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java
@@ -34,6 +34,7 @@
import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
import org.jclouds.azurecompute.arm.filters.ApiVersionFilter;
import org.jclouds.azurecompute.arm.functions.FalseOn204;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.oauth.v2.filters.OAuthFilter;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.MapBinder;
@@ -60,7 +61,7 @@
@PUT
PublicIPAddress createOrUpdate(@PathParam("publicipaddressname") String publicipaddressname,
@PayloadParam("location") String location,
- @PayloadParam("tags") Map<String, String> tags,
+ @Nullable @PayloadParam("tags") Map<String, String> tags,
@PayloadParam("properties") PublicIPAddressProperties properties);
@Named("publicipaddress:get")
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java
new file mode 100644
index 0000000..f95430c
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.compute.strategy;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
+import org.jclouds.azurecompute.arm.compute.options.IpOptions;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
+import org.jclouds.azurecompute.arm.domain.Subnet;
+import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
+import org.jclouds.azurecompute.arm.features.SubnetApi;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+@Test(groups = "unit", testName = "CreateResourcesThenCreateNodesTest")
+public class CreateResourcesThenCreateNodesTest {
+
+ @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "The options.networks and options.ipOptions are exclusive")
+ public void testNormalizeNetworkOptionsWithConflictingConfig() {
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ options.ipOptions(IpOptions.builder().subnet(netResource("/virtualNetworks/vn/subnets/foo")).build());
+ options.networks(netResource("/virtualNetworks/vn/subnets/bar"));
+ strategy(null).normalizeNetworkOptions(options);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "The allocateNewPublicIps and publicIpId are exclusive")
+ public void testNormalizeNetworkOptionsExclusivePublicIps() {
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ options.ipOptions(IpOptions.builder().subnet(netResource("/virtualNetworks/vn/subnets/foo"))
+ .allocateNewPublicIp(true).publicIpId(netResource("/publicIPAddresses/pub")).build());
+ strategy(null).normalizeNetworkOptions(options);
+ }
+
+ public void testPortableNetworkOptions() {
+ AzureComputeApi api = createMock(AzureComputeApi.class);
+ SubnetApi subnetApi = createMock(SubnetApi.class);
+
+ expect(api.getSubnetApi(anyObject(String.class), anyObject(String.class))).andReturn(subnetApi).times(2);
+ expect(subnetApi.get(anyObject(String.class))).andReturn(Subnet.builder().build()).times(2);
+ replay(api, subnetApi);
+
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ options.networks(netResource("/virtualNetworks/vn/subnets/foo"), netResource("/virtualNetworks/vn/subnets/bar"));
+ strategy(api).normalizeNetworkOptions(options);
+
+ assertEquals(options.getIpOptions(), ImmutableList.of(
+ IpOptions.builder().subnet(netResource("/virtualNetworks/vn/subnets/foo")).build(), IpOptions.builder()
+ .subnet(netResource("/virtualNetworks/vn/subnets/bar")).build()));
+
+ // Verify that the code has validated that the subnets exist
+ verify(api, subnetApi);
+ }
+
+ public void testProviderSpecificNetworkOptions() {
+ AzureComputeApi api = createMock(AzureComputeApi.class);
+ SubnetApi subnetApi = createMock(SubnetApi.class);
+ PublicIPAddressApi publicIpApi = createMock(PublicIPAddressApi.class);
+
+ expect(api.getSubnetApi(anyObject(String.class), anyObject(String.class))).andReturn(subnetApi).times(2);
+ expect(api.getPublicIPAddressApi(anyObject(String.class))).andReturn(publicIpApi);
+ expect(subnetApi.get(anyObject(String.class))).andReturn(Subnet.builder().build()).times(2);
+ expect(publicIpApi.get(anyObject(String.class))).andReturn(mockAddress());
+ replay(api, subnetApi, publicIpApi);
+
+ IpOptions publicOpts = IpOptions.builder().subnet(netResource("/virtualNetworks/vn/subnets/foo"))
+ .publicIpId(netResource("/publicIPAddresses/pub")).address("10.0.0.2").build();
+ IpOptions privateOpts = IpOptions.builder().subnet(netResource("/virtualNetworks/vn/subnets/bar")).build();
+
+ AzureTemplateOptions options = new AzureTemplateOptions();
+ options.ipOptions(publicOpts, privateOpts);
+ strategy(api).normalizeNetworkOptions(options);
+
+ assertEquals(options.getIpOptions(), ImmutableList.of(publicOpts, privateOpts));
+
+ // Verify that the code has validated that the subnets exist
+ verify(api, subnetApi, publicIpApi);
+ }
+
+ private static CreateResourcesThenCreateNodes strategy(AzureComputeApi api) {
+ return new CreateResourcesThenCreateNodes(null, null, null, null, null, api, null, null, null, null);
+ }
+
+ private static String netResource(String resource) {
+ return "/subscriptions/subs/resourceGroups/rg/providers/Microsoft.Network" + resource;
+ }
+
+ private static PublicIPAddress mockAddress() {
+ return PublicIPAddress.builder().name("name").id("id").etag("etag").location("location")
+ .properties(PublicIPAddressProperties.builder().publicIPAllocationMethod("Dynamic").build()).build();
+ }
+}
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java
index e5426d7..e2a56e1 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java
@@ -22,6 +22,7 @@
import org.testng.annotations.Test;
+@Test(groups = "unit", testName = "IdReferenceTest")
public class IdReferenceTest {
@Test
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java
new file mode 100644
index 0000000..a5ef44c
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.domain;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "SubnetTest")
+public class SubnetTest {
+
+ @Test
+ public void testExtractVirtualNetwork() {
+
+ assertEquals(Subnet.builder().build().virtualNetwork(), null);
+ assertEquals(
+ Subnet.builder()
+ .id("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vn/subnets/subnet")
+ .build().virtualNetwork(), "vn");
+ assertInvalidId("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks");
+ assertInvalidId("virtualNetworks/vn");
+ }
+
+ private static void assertInvalidId(String id) {
+ try {
+ Subnet.builder().id(id).build().virtualNetwork();
+ fail("The given ID " + id + "should not match a valid virtual network");
+ } catch (IllegalStateException ex) {
+ // Expected
+ }
+ }
+}
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java
index c5b836b..5cf4239 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java
@@ -23,6 +23,7 @@
import static com.google.common.collect.Lists.newArrayList;
import static org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions.Builder.availabilitySet;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
import static org.jclouds.azurecompute.arm.domain.InboundNatRuleProperties.Protocol.Tcp;
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
import static org.testng.Assert.assertEquals;
@@ -360,7 +361,7 @@
VirtualMachine vm = api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).get(
resourceGroupAndName.name());
- String nicName = vm.properties().networkProfile().networkInterfaces().get(0).name();
+ String nicName = extractName(vm.properties().networkProfile().networkInterfaces().get(0).id());
nicNames.add(nicName);
}
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
index e0f0ed4..dd10046 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
@@ -40,7 +40,7 @@
private final String subscriptionid = "SUBSCRIPTIONID";
private final String resourcegroup = "myresourcegroup";
- private final String apiVersion = "api-version=2015-06-15";
+ private final String apiVersion = "api-version=2017-03-01";
private final String location = "northeurope";
private final String nicName = "myNic";
@@ -65,7 +65,8 @@
assertNull(nicApi.get(nicName));
- assertSent(server, "GET", "/subscriptions/SUBSCRIPTIONID/resourcegroups/myresourcegroup/providers/Microsoft.Network/networkInterfaces/myNic?api-version=2015-06-15");
+ String path = String.format("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.Network/networkInterfaces/%s?%s", subscriptionid, resourcegroup, nicName, apiVersion);
+ assertSent(server, "GET", path);
}
public void listNetworkInterfaceCards() throws InterruptedException {
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java
index 0113201..3d58591 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java
@@ -36,7 +36,7 @@
private final String resourcegroup = "myresourcegroup";
private final String virtualNetwork = "myvirtualnetwork";
private final String subnetName = "mysubnet";
- private final String apiVersion = "api-version=2015-06-15";
+ private final String apiVersion = "api-version=2017-03-01";
public void createSubnet() throws InterruptedException {
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
index cecdc01..f3f6aac 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
@@ -51,6 +51,8 @@
import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance.PowerState;
import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface.NetworkInterfaceProperties;
import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiLiveTest;
import org.testng.annotations.BeforeClass;
@@ -253,12 +255,11 @@
OSProfile.WindowsConfiguration windowsConfig = OSProfile.WindowsConfiguration.create(false, null, null, true,
null);
OSProfile osProfile = OSProfile.create(vmName, "azureuser", "RFe3&432dg", null, null, windowsConfig);
- IdReference networkInterface =
- IdReference.create("/subscriptions/" + subscriptionid +
+ NetworkInterface networkInterface =
+ NetworkInterface.create("/subscriptions/" + subscriptionid +
"/resourceGroups/" + resourceGroupName + "/providers/Microsoft.Network/networkInterfaces/"
- + nic);
- List<IdReference> networkInterfaces =
- new ArrayList<IdReference>();
+ + nic, NetworkInterfaceProperties.create(true));
+ List<NetworkInterface> networkInterfaces = new ArrayList<NetworkInterface>();
networkInterfaces.add(networkInterface);
NetworkProfile networkProfile = NetworkProfile.create(networkInterfaces);
VirtualMachineProperties properties = VirtualMachineProperties.create(null,
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
index 27e6f6b..95d967e 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
@@ -16,6 +16,12 @@
*/
package org.jclouds.azurecompute.arm.features;
+import static com.google.common.collect.Iterables.isEmpty;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -26,9 +32,9 @@
import org.jclouds.azurecompute.arm.domain.DataDisk;
import org.jclouds.azurecompute.arm.domain.DiagnosticsProfile;
import org.jclouds.azurecompute.arm.domain.HardwareProfile;
-import org.jclouds.azurecompute.arm.domain.IdReference;
import org.jclouds.azurecompute.arm.domain.ImageReference;
import org.jclouds.azurecompute.arm.domain.NetworkProfile;
+import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.OSDisk;
import org.jclouds.azurecompute.arm.domain.OSProfile;
import org.jclouds.azurecompute.arm.domain.Plan;
@@ -45,12 +51,6 @@
import com.google.common.collect.ImmutableMap;
import com.squareup.okhttp.mockwebserver.MockResponse;
-import static com.google.common.collect.Iterables.isEmpty;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
@Test(groups = "unit", testName = "VirtualMachineApiMockTest", singleThreaded = true)
public class VirtualMachineApiMockTest extends BaseAzureComputeApiMockTest {
@@ -257,9 +257,9 @@
OSProfile.WindowsConfiguration windowsConfig = OSProfile.WindowsConfiguration.create(false, null, null, true,
null);
OSProfile osProfile = OSProfile.create("windowsmachine", "azureuser", null, null, null, windowsConfig);
- IdReference networkInterface = IdReference.create("/subscriptions/SUBSCRIPTIONID"
- + "/resourceGroups/groupname/providers/Microsoft.Network/networkInterfaces/" + "windowsmachine167");
- List<IdReference> networkInterfaces = new ArrayList<IdReference>();
+ NetworkInterface networkInterface = NetworkInterface.create("/subscriptions/SUBSCRIPTIONID"
+ + "/resourceGroups/groupname/providers/Microsoft.Network/networkInterfaces/" + "windowsmachine167", null);
+ List<NetworkInterface> networkInterfaces = new ArrayList<NetworkInterface>();
networkInterfaces.add(networkInterface);
NetworkProfile networkProfile = NetworkProfile.create(networkInterfaces);
DiagnosticsProfile.BootDiagnostics bootDiagnostics = DiagnosticsProfile.BootDiagnostics.create(true,