JCLOUDS-1278: Allow to configure virtual machine NICs

This commit is contained in:
Ignasi Barrera 2017-04-26 15:42:36 +02:00
parent ec67fdea32
commit df30057386
22 changed files with 705 additions and 239 deletions

View File

@ -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");

View File

@ -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<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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Virtual
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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Virtual
// 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 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Virtual
return builder.build();
}
private NetworkInterfaceCard createNetworkInterfaceCard(String subnetId, String name, String locationName,
String azureGroup, TemplateOptions options) {
final PublicIPAddressApi ipApi = api.getPublicIPAddressApi(azureGroup);
PublicIPAddressProperties properties = PublicIPAddressProperties.builder().publicIPAllocationMethod("Static")
.idleTimeoutInMinutes(4).build();
String publicIpAddressName = "public-address-" + name;
PublicIPAddress ip = ipApi.createOrUpdate(publicIpAddressName, locationName, ImmutableMap.of("jclouds", name),
properties);
checkState(publicIpAvailable.create(azureGroup).apply(publicIpAddressName),
"Public IP was not provisioned in the configured timeout");
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()));
String securityGroup = getOnlyElement(options.getGroups(), null);
if (securityGroup != null) {
networkInterfaceCardProperties.networkSecurityGroup(IdReference.create(securityGroup));
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);
}
});
}
String networkInterfaceCardName = "jc-nic-" + name;
return api.getNetworkInterfaceCardApi(azureGroup).createOrUpdate(networkInterfaceCardName, locationName,
networkInterfaceCardProperties.build(), ImmutableMap.of("jclouds", name));
private NetworkInterfaceCard createNetworkInterfaceCard(IpOptions ipConfig, String nodeName, String location,
String securityGroup) {
String resourceGroup = extractResourceGroup(ipConfig.subnet());
String subnetName = extractName(ipConfig.subnet());
IpConfigurationProperties.Builder ipProperties = IpConfigurationProperties.builder()
.subnet(IdReference.create(ipConfig.subnet()))
.privateIPAllocationMethod(ipConfig.address().isPresent() ? "Static" : "Dynamic")
.privateIPAddress(ipConfig.address().orNull());
configurePublicIP(ipConfig, ipProperties, resourceGroup, location, nodeName);
String ipName = namingConvention.uniqueNameForGroup(subnetName);
final String nicName = namingConvention.uniqueNameForGroup(subnetName);
IpConfiguration config = IpConfiguration.builder().name(ipName).properties(ipProperties.build()).build();
NetworkInterfaceCardProperties.Builder nicProperties = NetworkInterfaceCardProperties.builder().ipConfigurations(
ImmutableList.of(config));
if (securityGroup != null) {
nicProperties.networkSecurityGroup(IdReference.create(securityGroup));
}
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) {

View File

@ -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<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();
@ -172,8 +174,13 @@ public class AzureComputeSecurityGroupExtension implements SecurityGroupExtensio
builder.name(name);
builder.location(location);
return securityGroupConverter.apply(api.getNetworkSecurityGroupApi(resourceGroup.name()).createOrUpdate(name,
location.getId(), null, NetworkSecurityGroupProperties.builder().build()));
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(sg);
}
@Override

View File

@ -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<VirtualMachine, No
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 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
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());
}

View File

@ -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<ResourceGroupAndNam
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
@ -87,6 +91,9 @@ public class CreateSecurityGroupIfNeeded extends CacheLoader<ResourceGroupAndNam
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();
}

View File

@ -32,28 +32,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
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 @@ 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.
* <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 @@ 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,27 +157,13 @@ 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> ipOptions) {
AzureTemplateOptions options = new AzureTemplateOptions();
return options.ipOptions(ipOptions);
}
}
}

View File

@ -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();
}
}

View File

@ -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,10 +103,10 @@ 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<IdReference> publicIps = getPublicIps(nic);
@ -113,10 +118,13 @@ public class CleanupResources {
String publicIpResourceGroup = publicIp.resourceGroup();
String publicIpName = publicIp.name();
logger.debug(">> deleting public ip nic %s...", 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;
}

View File

@ -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;
@ -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";
protected synchronized void createDefaultNetworkIfNeeded(String group, String location, AzureTemplateOptions options) {
if (options.getIpOptions().isEmpty()) {
String name = namingConvention.create().sharedNameForGroup(group);
// Subnets belong to a virtual network so that needs to be created first
VirtualNetworkApi vnApi = api.getVirtualNetworkApi(options.getResourceGroup());
VirtualNetwork vn = vnApi.get(virtualNetworkName);
Subnet subnet = Subnet.builder().name(name)
.properties(SubnetProperties.builder().addressPrefix(defaultSubnetAddressPrefix).build()).build();
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)))
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> 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());
}
}
}
}
}

View File

@ -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<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)
@ -53,27 +46,25 @@ 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<IpConfiguration> ipConfigurations);
abstract List<IpConfiguration> ipConfigurations();
public abstract Builder networkSecurityGroup(IdReference networkSecurityGroup);
abstract List<IpConfiguration> ipConfigurations();
abstract NetworkInterfaceCardProperties autoBuild();
public NetworkInterfaceCardProperties build() {

View File

@ -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<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 @@ public abstract class NetworkProfile {
@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();
}
}

View File

@ -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);
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();
}
}
}

View File

@ -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<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 @@ 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<IpConfiguration> ipConfigurations);
abstract List<IpConfiguration> ipConfigurations();
abstract SubnetProperties autoBuild();
public SubnetProperties build() {
@ -86,23 +90,47 @@ public abstract class Subnet {
}
}
@Nullable
public abstract String name();
@Nullable public abstract String name();
@Nullable public abstract String id();
@Nullable public abstract String etag();
@Nullable public abstract SubnetProperties properties();
@Nullable
public abstract String id();
@Nullable public String virtualNetwork() {
return extractVirtualNetwork(id());
}
@Nullable
public abstract String etag();
@Nullable
public abstract SubnetProperties properties();
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();
}
}

View File

@ -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<String, String> tags,
@Nullable @PayloadParam("tags") Map<String, String> tags,
@PayloadParam("properties") PublicIPAddressProperties properties);
@Named("publicipaddress:get")

View File

@ -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();
}
}

View File

@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "IdReferenceTest")
public class IdReferenceTest {
@Test

View File

@ -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
}
}
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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<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,

View File

@ -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<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,