Skip to content
Permalink
Browse files
JCLOUDS-1278: Allow to configure virtual machine NICs
  • Loading branch information
nacx committed Apr 28, 2017
1 parent 004fbcf commit dbadb279f14848f21879f7eb6c7136e1a5f11192
Showing 22 changed files with 705 additions and 239 deletions.
@@ -99,15 +99,15 @@ public static Properties defaultProperties() {
// 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");
properties.put(API_VERSION_PREFIX + PublicIPAddressApi.class.getSimpleName(), "2015-06-15");
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");
@@ -27,9 +27,14 @@
import static org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
import static org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.getMarketplacePlanFromImageMetadata;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import static org.jclouds.azurecompute.arm.util.VMImages.isCustom;
import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -44,6 +49,7 @@
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
import org.jclouds.azurecompute.arm.compute.functions.CustomImageToVMImage;
import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
import org.jclouds.azurecompute.arm.compute.options.IpOptions;
import org.jclouds.azurecompute.arm.compute.strategy.CleanupResources;
import org.jclouds.azurecompute.arm.domain.AvailabilitySet;
import org.jclouds.azurecompute.arm.domain.CreationData;
@@ -62,6 +68,7 @@
import org.jclouds.azurecompute.arm.domain.OSProfile;
import org.jclouds.azurecompute.arm.domain.Offer;
import org.jclouds.azurecompute.arm.domain.Plan;
import org.jclouds.azurecompute.arm.domain.Provisionable;
import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
import org.jclouds.azurecompute.arm.domain.ResourceGroup;
@@ -75,13 +82,15 @@
import org.jclouds.azurecompute.arm.domain.Version;
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface.NetworkInterfaceProperties;
import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
import org.jclouds.azurecompute.arm.features.OSImageApi;
import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.location.Region;
import org.jclouds.logging.Logger;
@@ -105,6 +114,7 @@
public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VirtualMachine, VMHardware, VMImage, Location> {

public static final String GROUP_KEY = "jclouds_group";
public static final String AUTOGENERATED_IP_KEY = "jclouds-autogenerated";

@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
@@ -116,40 +126,40 @@ 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 NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoN

// 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 @@ private OSProfile createOsProfile(String computerName, Template template) {
return builder.build();
}

private NetworkInterfaceCard createNetworkInterfaceCard(String subnetId, String name, String locationName,
String azureGroup, TemplateOptions options) {
final PublicIPAddressApi ipApi = api.getPublicIPAddressApi(azureGroup);
private List<NetworkInterfaceCard> createNetworkInterfaceCards(final String nodeName, final String location,
AzureTemplateOptions options) {
// Prefer a sorted list of NICs with the ones with public IPs first, to
// make sure the primary NIC is the public one
final String securityGroup = getOnlyElement(options.getGroups(), null);
return Lists.transform(publicIpsFirst(options.getIpOptions()), new Function<IpOptions, NetworkInterfaceCard>() {
@Override
public NetworkInterfaceCard apply(IpOptions input) {
return createNetworkInterfaceCard(input, nodeName, location, securityGroup);
}
});
}

private NetworkInterfaceCard createNetworkInterfaceCard(IpOptions ipConfig, String nodeName, String location,
String securityGroup) {
String resourceGroup = extractResourceGroup(ipConfig.subnet());
String subnetName = extractName(ipConfig.subnet());

PublicIPAddressProperties properties = PublicIPAddressProperties.builder().publicIPAllocationMethod("Static")
.idleTimeoutInMinutes(4).build();
IpConfigurationProperties.Builder ipProperties = IpConfigurationProperties.builder()
.subnet(IdReference.create(ipConfig.subnet()))
.privateIPAllocationMethod(ipConfig.address().isPresent() ? "Static" : "Dynamic")
.privateIPAddress(ipConfig.address().orNull());

String publicIpAddressName = "public-address-" + name;
PublicIPAddress ip = ipApi.createOrUpdate(publicIpAddressName, locationName, ImmutableMap.of("jclouds", name),
properties);
configurePublicIP(ipConfig, ipProperties, resourceGroup, location, nodeName);

checkState(publicIpAvailable.create(azureGroup).apply(publicIpAddressName),
"Public IP was not provisioned in the configured timeout");
String ipName = namingConvention.uniqueNameForGroup(subnetName);
final String nicName = namingConvention.uniqueNameForGroup(subnetName);

IpConfiguration config = IpConfiguration.builder().name(ipName).properties(ipProperties.build()).build();

NetworkInterfaceCardProperties.Builder nicProperties = NetworkInterfaceCardProperties.builder().ipConfigurations(
ImmutableList.of(config));

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));
nicProperties.networkSecurityGroup(IdReference.create(securityGroup));
}

String networkInterfaceCardName = "jc-nic-" + name;
return api.getNetworkInterfaceCardApi(azureGroup).createOrUpdate(networkInterfaceCardName, locationName,
networkInterfaceCardProperties.build(), ImmutableMap.of("jclouds", name));
logger.debug(">> creating nic %s(%s) with security groups (%s)", nicName, config,
securityGroup != null ? securityGroup : "");

final NetworkInterfaceCardApi nicApi = api.getNetworkInterfaceCardApi(resourceGroup);
NetworkInterfaceCard nic = nicApi.createOrUpdate(nicName, location, nicProperties.build(),
ImmutableMap.of("jclouds", nodeName));

resourceAvailable.apply(new Supplier<Provisionable>() {
@Override
public Provisionable get() {
NetworkInterfaceCard updated = nicApi.get(nicName);
return updated == null ? null : updated.properties();
}
});

return nic;
}

private void configurePublicIP(IpOptions ipConfig, IpConfigurationProperties.Builder ipProperties,
String resourceGroup, String location, String nodeName) {
if (ipConfig.publicIpId() != null) {
logger.debug(">> configuring public ip: %s", extractName(ipConfig.publicIpId()));
PublicIPAddress publicIp = api.getPublicIPAddressApi(extractResourceGroup(ipConfig.publicIpId())).get(
extractName(ipConfig.publicIpId()));
ipProperties.publicIPAddress(IdReference.create(publicIp.id()));
} else if (ipConfig.allocateNewPublicIp()) {
PublicIPAddress publicIp = createPublicIp(resourceGroup, location, nodeName);
ipProperties.publicIPAddress(IdReference.create(publicIp.id()));
}
}

/**
* Create the network profile and configure the first NIC as primary.
*/
private NetworkProfile createNetworkProfile(List<NetworkInterfaceCard> nics) {
List<NetworkInterface> nicAttachments = new ArrayList<NetworkInterface>(nics.size());
for (int i = 0; i < nics.size(); i++) {
nicAttachments.add(NetworkInterface.create(nics.get(i).id(), NetworkInterfaceProperties.create(i == 0)));
}
return NetworkProfile.create(nicAttachments);
}

private static List<IpOptions> publicIpsFirst(List<IpOptions> ipOptions) {
List<IpOptions> sorted = new ArrayList<IpOptions>(ipOptions);
Collections.sort(sorted, new Comparator<IpOptions>() {
@Override
public int compare(IpOptions o1, IpOptions o2) {
return o1.allocateNewPublicIp() == o2.allocateNewPublicIp() ? 0 : o1.allocateNewPublicIp() ? -1 : 1;
}
});
return sorted;
}

private PublicIPAddress createPublicIp(String resourceGroup, String location, String nodeName) {
String name = namingConvention.uniqueNameForGroup(nodeName);

PublicIPAddressProperties properties = PublicIPAddressProperties.builder()
.publicIPAllocationMethod("Static")
.idleTimeoutInMinutes(4)
.build();

logger.debug(">> allocating new public ip address: %s", name);

PublicIPAddress ip = api.getPublicIPAddressApi(resourceGroup).createOrUpdate(name, location,
ImmutableMap.of("jclouds", nodeName, AUTOGENERATED_IP_KEY, "true"), properties);

checkState(publicIpAvailable.create(resourceGroup).apply(name),
"Public IP was not provisioned in the configured timeout");

return ip;
}

private StorageProfile createStorageProfile(Image image, List<DataDisk> dataDisks) {
@@ -23,6 +23,8 @@
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;

import java.net.URI;
import java.util.ArrayList;
@@ -36,8 +38,8 @@
import org.jclouds.azurecompute.arm.AzureComputeApi;
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.SecurityGroupAvailablePredicateFactory;
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
import org.jclouds.azurecompute.arm.domain.IdReference;
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroupProperties;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityRule;
@@ -135,12 +137,12 @@ public Set<SecurityGroup> listSecurityGroupsForNode(String nodeId) {
if (vm == null) {
throw new IllegalArgumentException("Node " + nodeId + " was not found");
}
List<IdReference> networkInterfacesIdReferences = vm.properties().networkProfile().networkInterfaces();
List<NetworkInterface> networkInterfaces = vm.properties().networkProfile().networkInterfaces();
List<NetworkSecurityGroup> networkGroups = new ArrayList<NetworkSecurityGroup>();

for (IdReference networkInterfaceCardIdReference : networkInterfacesIdReferences) {
String nicName = networkInterfaceCardIdReference.name();
String nicResourceGroup = networkInterfaceCardIdReference.resourceGroup();
for (NetworkInterface networkInterfaceCardIdReference : networkInterfaces) {
String nicName = extractName(networkInterfaceCardIdReference.id());
String nicResourceGroup = extractResourceGroup(networkInterfaceCardIdReference.id());
NetworkInterfaceCard card = api.getNetworkInterfaceCardApi(nicResourceGroup).get(nicName);
if (card != null && card.properties().networkSecurityGroup() != null) {
String secGroupName = card.properties().networkSecurityGroup().name();
@@ -171,9 +173,14 @@ public SecurityGroup createSecurityGroup(String name, Location location) {
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

0 comments on commit dbadb27

Please sign in to comment.