From df300573863c7716759c1bfef56fa382ba73d705 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Wed, 26 Apr 2017 15:42:36 +0200 Subject: [PATCH] JCLOUDS-1278: Allow to configure virtual machine NICs --- .../arm/AzureComputeProviderMetadata.java | 4 +- .../compute/AzureComputeServiceAdapter.java | 164 +++++++++++++----- .../AzureComputeSecurityGroupExtension.java | 21 ++- .../VirtualMachineToNodeMetadata.java | 18 +- .../loaders/CreateSecurityGroupIfNeeded.java | 9 +- .../compute/options/AzureTemplateOptions.java | 98 ++++++----- .../arm/compute/options/IpOptions.java | 76 ++++++++ .../compute/strategy/CleanupResources.java | 30 ++-- .../CreateResourcesThenCreateNodes.java | 108 ++++++++---- .../NetworkInterfaceCardProperties.java | 37 ++-- .../arm/domain/NetworkProfile.java | 44 ++++- .../arm/domain/PublicIPAddress.java | 55 ++++-- .../azurecompute/arm/domain/Subnet.java | 74 +++++--- .../arm/features/PublicIPAddressApi.java | 3 +- .../CreateResourcesThenCreateNodesTest.java | 114 ++++++++++++ .../arm/domain/IdReferenceTest.java | 1 + .../azurecompute/arm/domain/SubnetTest.java | 47 +++++ .../arm/features/LoadBalancerApiLiveTest.java | 3 +- .../NetworkInterfaceCardApiMockTest.java | 5 +- .../arm/features/SubnetApiMockTest.java | 2 +- .../features/VirtualMachineApiLiveTest.java | 11 +- .../features/VirtualMachineApiMockTest.java | 20 +-- 22 files changed, 705 insertions(+), 239 deletions(-) create mode 100644 providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java create mode 100644 providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java create mode 100644 providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java index 9d3f05d850..c35455cd9e 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java @@ -99,7 +99,7 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata { // 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 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata { 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/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java index d01d6eced0..02e69fa4ae 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java @@ -27,9 +27,14 @@ import static org.jclouds.azurecompute.arm.compute.domain.LocationAndName.fromSl 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.config.AzureComputeServiceContextMod 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.OSDisk; 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.VMSize; 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 @@ import com.google.common.collect.Lists; public class AzureComputeServiceAdapter implements ComputeServiceAdapter { 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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter> regionIds; private final PublicIpAvailablePredicateFactory publicIpAvailable; private final CustomImageToVMImage customImagetoVmImage; + private final GroupNamingConvention namingConvention; + private Predicate> resourceAvailable; @Inject AzureComputeServiceAdapter(final AzureComputeApi api, @Named(IMAGE_PUBLISHERS) String imagePublishers, CleanupResources cleanupResources, @Region Supplier> regionIds, - PublicIpAvailablePredicateFactory publicIpAvailable, - CustomImageToVMImage customImagetoVmImage) { + PublicIpAvailablePredicateFactory publicIpAvailable, CustomImageToVMImage customImagetoVmImage, + GroupNamingConvention.Factory namingConvention, Predicate> 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 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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter metadataAndTags = metadataAndTagsAsCommaDelimitedValue(template.getOptions()); - Plan plan = getMarketplacePlanFromImageMetadata(template.getImage()); + templateOptions.getUserMetadata().put(GROUP_KEY, group); + Map 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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter 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() { + @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(); + + NetworkInterfaceCardProperties.Builder nicProperties = NetworkInterfaceCardProperties.builder().ipConfigurations( + ImmutableList.of(config)); - String securityGroup = getOnlyElement(options.getGroups(), null); 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() { + @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 nics) { + List nicAttachments = new ArrayList(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 publicIpsFirst(List ipOptions) { + List sorted = new ArrayList(ipOptions); + Collections.sort(sorted, new Comparator() { + @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 dataDisks) { diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java index 59608a498f..50cb75a246 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java @@ -23,6 +23,8 @@ import static com.google.common.collect.Iterables.any; 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 javax.inject.Named; 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 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio if (vm == null) { throw new IllegalArgumentException("Node " + nodeId + " was not found"); } - List networkInterfacesIdReferences = vm.properties().networkProfile().networkInterfaces(); + List networkInterfaces = vm.properties().networkProfile().networkInterfaces(); List networkGroups = new ArrayList(); - 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 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio 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/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java index e4b7da8976..af43cbbcac 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java @@ -22,6 +22,7 @@ import static com.google.common.collect.Iterables.find; 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.compute.functions.VirtualMachineToStatus.Sta 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 @@ public class VirtualMachineToNodeMetadata implements Function getPrivateIpAddresses(List idReferences) { + private Iterable getPrivateIpAddresses(List networkInterfaces) { List 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 @@ public class VirtualMachineToNodeMetadata implements Function getPublicIpAddresses(List idReferences) { + private Iterable getPublicIpAddresses(List networkInterfaces) { List 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/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java index bb5dc0993d..98732d2567 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/loaders/CreateSecurityGroupIfNeeded.java +++ b/providers/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.Named; 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 @@ public class CreateSecurityGroupIfNeeded extends CacheLoader 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 = ImmutableList.of(); /** * Sets the availability set where the nodes will be configured. If it does @@ -92,12 +75,35 @@ public class AzureTemplateOptions extends TemplateOptions implements Cloneable { 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. + *

+ * 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. + *

+ * 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) { + 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 getDataDisks() { return dataDisks; } public String getResourceGroup() { return resourceGroup; } + public List getIpOptions() { return ipOptions; } @Override public AzureTemplateOptions clone() { @@ -111,12 +117,11 @@ public class AzureTemplateOptions extends TemplateOptions implements Cloneable { 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 @@ public class AzureTemplateOptions extends TemplateOptions implements Cloneable { 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 @@ public class AzureTemplateOptions extends TemplateOptions implements Cloneable { 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 @@ public class AzureTemplateOptions extends TemplateOptions implements Cloneable { 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) { + AzureTemplateOptions options = new AzureTemplateOptions(); + return options.ipOptions(ipOptions); + } } } diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java new file mode 100644 index 0000000000..73c4c6c052 --- /dev/null +++ b/providers/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 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 address); + public Builder address(String address) { + return address(Optional.fromNullable(address)); + } + + public abstract IpOptions build(); + } +} diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java index 1f1a37dc51..3ca1a5db5f 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CleanupResources.java @@ -21,7 +21,10 @@ import static com.google.common.base.Predicates.notNull; 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.IdReference; 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 class CleanupResources { 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 publicIps = getPublicIps(nic); logger.debug(">> destroying nic %s...", nicName); @@ -112,9 +117,12 @@ public class CleanupResources { 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 @@ public class CleanupResources { for (DataDisk dataDisk : virtualMachine.properties().storageProfile().dataDisks()) { deleteManagedDisk(dataDisk.managedDiskParameters(), deleteJobs); } - + Set 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 deleteJobs) { if (managedDisk != null) { IdReference diskRef = IdReference.create(managedDisk.id()); diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java index c3bdbdd07e..2ddb340aa2 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodes.java +++ b/providers/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.ResourceGroupAndName; 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.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThen 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 @@ public class CreateResourcesThenCreateNodes extends CreateNodesWithGroupEncodedI 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 @@ public class CreateResourcesThenCreateNodes extends CreateNodesWithGroupEncodedI Multimap 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 @@ public class CreateResourcesThenCreateNodes extends CreateNodesWithGroupEncodedI + "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 @@ public class CreateResourcesThenCreateNodes extends CreateNodesWithGroupEncodedI 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 = 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/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java index d1976f1747..84c8ca2c38 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java @@ -26,23 +26,16 @@ import java.util.List; @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 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 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 ipConfigurations, final IdReference networkSecurityGroup) { + public static NetworkInterfaceCardProperties create(final String provisioningState, final String resourceGuid, + final Boolean enableIPForwarding, final List ipConfigurations, + final IdReference networkSecurityGroup) { NetworkInterfaceCardProperties.Builder builder = NetworkInterfaceCardProperties.builder() .provisioningState(provisioningState) .resourceGuid(resourceGuid) @@ -52,28 +45,26 @@ public abstract class NetworkInterfaceCardProperties implements Provisionable { 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 ipConfigurations); - - abstract List ipConfigurations(); - public abstract Builder networkSecurityGroup(IdReference networkSecurityGroup); + abstract List ipConfigurations(); abstract NetworkInterfaceCardProperties autoBuild(); public NetworkInterfaceCardProperties build() { diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java index b26305f5f5..cdb6d5184b 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkProfile.java @@ -19,6 +19,7 @@ package org.jclouds.azurecompute.arm.domain; 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 @@ import java.util.List; @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 networkInterfaces(); + public abstract List networkInterfaces(); @SerializedNames({"networkInterfaces"}) - public static NetworkProfile create(final List networkInterfaces) { + public static NetworkProfile create(final List networkInterfaces) { return builder().networkInterfaces(networkInterfaces).build(); } + NetworkProfile() { + + } + public abstract Builder toBuilder(); public static Builder builder() { @@ -44,14 +78,14 @@ public abstract class NetworkProfile { @AutoValue.Builder public abstract static class Builder { - public abstract Builder networkInterfaces(List networkInterfaces); + public abstract Builder networkInterfaces(List networkInterfaces); - abstract List networkInterfaces(); + abstract List networkInterfaces(); abstract NetworkProfile autoBuild(); public NetworkProfile build() { - networkInterfaces(networkInterfaces() != null ? ImmutableList.copyOf(networkInterfaces()) : ImmutableList.of()); + networkInterfaces(networkInterfaces() != null ? ImmutableList.copyOf(networkInterfaces()) : ImmutableList.of()); return autoBuild(); } } diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java index a08faddf63..8d0cb2b861 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/PublicIPAddress.java +++ b/providers/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 tags(); - + @Nullable public abstract Map 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 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 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 tags); + public abstract Builder properties(PublicIPAddressProperties properties); + + abstract Map tags(); + abstract PublicIPAddress autoBuild(); + + public PublicIPAddress build() { + tags(tags() != null ? ImmutableMap.copyOf(tags()) : null); + return autoBuild(); + } } } diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java index 6830438f94..80460b8f3f 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Subnet.java @@ -19,6 +19,8 @@ package org.jclouds.azurecompute.arm.domain; 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 @@ import org.jclouds.json.SerializedNames; @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 abstract class Subnet { 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 ipConfigurations(); + @Nullable public abstract String provisioningState(); + @Nullable public abstract String addressPrefix(); + @Nullable public abstract List ipConfigurations(); @SerializedNames({"provisioningState", "addressPrefix", "ipConfigurations"}) public static SubnetProperties create(final String provisioningState, final String addressPrefix, final List ipConfigurations) { @@ -61,6 +64,10 @@ public abstract class Subnet { .build(); } + SubnetProperties() { + + } + public abstract Builder toBuilder(); public static Builder builder() { @@ -70,13 +77,10 @@ public abstract class Subnet { @AutoValue.Builder public abstract static class Builder { public abstract Builder provisioningState(String provisioningState); - public abstract Builder addressPrefix(String addressPrefix); - public abstract Builder ipConfigurations(List ipConfigurations); abstract List ipConfigurations(); - abstract SubnetProperties autoBuild(); public SubnetProperties build() { @@ -86,23 +90,47 @@ public abstract class Subnet { } } - @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/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java index 26937535ff..75af4ff988 100644 --- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java +++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/PublicIPAddressApi.java @@ -34,6 +34,7 @@ import org.jclouds.azurecompute.arm.domain.PublicIPAddress; 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 @@ public interface PublicIPAddressApi { @PUT PublicIPAddress createOrUpdate(@PathParam("publicipaddressname") String publicipaddressname, @PayloadParam("location") String location, - @PayloadParam("tags") Map tags, + @Nullable @PayloadParam("tags") Map tags, @PayloadParam("properties") PublicIPAddressProperties properties); @Named("publicipaddress:get") diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourcesThenCreateNodesTest.java new file mode 100644 index 0000000000..f95430c299 --- /dev/null +++ b/providers/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/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java index e5426d7aa0..e2a56e1840 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java +++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/IdReferenceTest.java @@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals; import org.testng.annotations.Test; +@Test(groups = "unit", testName = "IdReferenceTest") public class IdReferenceTest { @Test diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/domain/SubnetTest.java new file mode 100644 index 0000000000..a5ef44c4ef --- /dev/null +++ b/providers/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/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java index c5b836beb3..5cf42397a0 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java +++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/LoadBalancerApiLiveTest.java @@ -23,6 +23,7 @@ import static com.google.common.collect.Iterables.transform; 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 @@ public class LoadBalancerApiLiveTest extends BaseComputeServiceContextLiveTest { 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/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java index e0f0ed4724..dd10046c50 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java +++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java @@ -40,7 +40,7 @@ public class NetworkInterfaceCardApiMockTest extends BaseAzureComputeApiMockTest 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 @@ public class NetworkInterfaceCardApiMockTest extends BaseAzureComputeApiMockTest 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/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java index 0113201f55..3d58591568 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java +++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/SubnetApiMockTest.java @@ -36,7 +36,7 @@ public class SubnetApiMockTest extends BaseAzureComputeApiMockTest { 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/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java index cecdc012fa..f3f6aacb1f 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java +++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java @@ -51,6 +51,8 @@ import org.jclouds.azurecompute.arm.domain.VirtualMachine; 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 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest { 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 networkInterfaces = - new ArrayList(); + + nic, NetworkInterfaceProperties.create(true)); + List networkInterfaces = new ArrayList(); networkInterfaces.add(networkInterface); NetworkProfile networkProfile = NetworkProfile.create(networkInterfaces); VirtualMachineProperties properties = VirtualMachineProperties.create(null, diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java index 27e6f6b97a..95d967e3ac 100644 --- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java +++ b/providers/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 java.util.List; 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.ImmutableList; 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 @@ public class VirtualMachineApiMockTest extends BaseAzureComputeApiMockTest { 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 networkInterfaces = new ArrayList(); + NetworkInterface networkInterface = NetworkInterface.create("/subscriptions/SUBSCRIPTIONID" + + "/resourceGroups/groupname/providers/Microsoft.Network/networkInterfaces/" + "windowsmachine167", null); + List networkInterfaces = new ArrayList(); networkInterfaces.add(networkInterface); NetworkProfile networkProfile = NetworkProfile.create(networkInterfaces); DiagnosticsProfile.BootDiagnostics bootDiagnostics = DiagnosticsProfile.BootDiagnostics.create(true,