mirror of https://github.com/apache/jclouds.git
JCLOUDS-664 Azurecompute-arm ImageExtension
This commit is contained in:
parent
4f62f40036
commit
3c790ae3ae
|
@ -113,6 +113,11 @@
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds.provider</groupId>
|
||||||
|
<artifactId>azureblob</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
|
@ -135,6 +140,11 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds</groupId>
|
||||||
|
<artifactId>jclouds-blobstore</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.jclouds.azurecompute.arm.compute;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
import static org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension.CUSTOM_IMAGE_PREFIX;
|
|
||||||
import static org.jclouds.util.Predicates2.retry;
|
import static org.jclouds.util.Predicates2.retry;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -45,6 +44,7 @@ import org.jclouds.azurecompute.arm.domain.DeploymentBody;
|
||||||
import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
|
import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
|
||||||
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
|
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
|
||||||
import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
|
import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
|
||||||
|
import org.jclouds.azurecompute.arm.domain.StorageService;
|
||||||
import org.jclouds.azurecompute.arm.domain.VMImage;
|
import org.jclouds.azurecompute.arm.domain.VMImage;
|
||||||
import org.jclouds.azurecompute.arm.domain.VMHardware;
|
import org.jclouds.azurecompute.arm.domain.VMHardware;
|
||||||
import org.jclouds.azurecompute.arm.domain.Location;
|
import org.jclouds.azurecompute.arm.domain.Location;
|
||||||
|
@ -56,6 +56,7 @@ import org.jclouds.azurecompute.arm.domain.VirtualMachine;
|
||||||
import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
|
import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
|
||||||
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
||||||
import org.jclouds.azurecompute.arm.features.OSImageApi;
|
import org.jclouds.azurecompute.arm.features.OSImageApi;
|
||||||
|
import org.jclouds.azurecompute.arm.util.BlobHelper;
|
||||||
import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
|
import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
|
||||||
import org.jclouds.compute.ComputeServiceAdapter;
|
import org.jclouds.compute.ComputeServiceAdapter;
|
||||||
import org.jclouds.compute.domain.Template;
|
import org.jclouds.compute.domain.Template;
|
||||||
|
@ -97,7 +98,6 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
|
||||||
|
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.azureComputeConstants = azureComputeConstants;
|
this.azureComputeConstants = azureComputeConstants;
|
||||||
|
|
||||||
this.azureGroup = azureComputeConstants.azureResourceGroup();
|
this.azureGroup = azureComputeConstants.azureResourceGroup();
|
||||||
|
|
||||||
logger.debug("AzureComputeServiceAdapter set azuregroup to: " + azureGroup);
|
logger.debug("AzureComputeServiceAdapter set azuregroup to: " + azureGroup);
|
||||||
|
@ -219,7 +219,7 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
|
||||||
for (SKU sku : skuList) {
|
for (SKU sku : skuList) {
|
||||||
Iterable<Version> versionList = osImageApi.listVersions(publisherName, offer.name(), sku.name());
|
Iterable<Version> versionList = osImageApi.listVersions(publisherName, offer.name(), sku.name());
|
||||||
for (Version version : versionList) {
|
for (Version version : versionList) {
|
||||||
VMImage vmImage = VMImage.create(publisherName, offer.name(), sku.name(), version.name(), location, false);
|
VMImage vmImage = VMImage.create(publisherName, offer.name(), sku.name(), version.name(), location);
|
||||||
osImagesRef.add(vmImage);
|
osImagesRef.add(vmImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,6 +247,17 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
|
||||||
osImages.addAll(listImagesByLocation(location.name()));
|
osImages.addAll(listImagesByLocation(location.name()));
|
||||||
}
|
}
|
||||||
checkAndSetImageAvailability(osImages, Sets.newHashSet(locationIds));
|
checkAndSetImageAvailability(osImages, Sets.newHashSet(locationIds));
|
||||||
|
|
||||||
|
// list custom images
|
||||||
|
List<StorageService> storages = api.getStorageAccountApi(azureGroup).list();
|
||||||
|
for (StorageService storage : storages) {
|
||||||
|
String name = storage.name();
|
||||||
|
String key = api.getStorageAccountApi(azureGroup).getKeys(name).key1();
|
||||||
|
List<VMImage> images = BlobHelper.getImages("jclouds", azureGroup, storage.name(), key,
|
||||||
|
"custom", storage.location());
|
||||||
|
osImages.addAll(images);
|
||||||
|
}
|
||||||
|
|
||||||
return osImages;
|
return osImages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,24 +275,25 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VMImage getImage(final String id) {
|
public VMImage getImage(final String id) {
|
||||||
String[] fields = VMImageToImage.decodeFieldsFromUniqueId(id);
|
VMImage image = VMImageToImage.decodeFieldsFromUniqueId(id);
|
||||||
if (fields[2].startsWith(CUSTOM_IMAGE_PREFIX)) {
|
if (image.custom()) {
|
||||||
String name = fields[2].substring(CUSTOM_IMAGE_PREFIX.length());
|
String key = api.getStorageAccountApi(azureGroup).getKeys(image.storage()).key1();
|
||||||
String sku = fields[3];
|
if (BlobHelper.customImageExists(image.storage(), key))
|
||||||
String version = "1";
|
return image;
|
||||||
VMImage ref = VMImage.create(CUSTOM_IMAGE_PREFIX + azureGroup, CUSTOM_IMAGE_PREFIX + name, sku, version, fields[0], false);
|
else
|
||||||
return ref;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String location = fields[0];
|
String location = image.location();
|
||||||
String publisher = fields[1];
|
String publisher = image.publisher();
|
||||||
String offer = fields[2];
|
String offer = image.offer();
|
||||||
String sku = fields[3];
|
String sku = image.sku();
|
||||||
|
|
||||||
OSImageApi osImageApi = api.getOSImageApi(location);
|
OSImageApi osImageApi = api.getOSImageApi(location);
|
||||||
List<Version> versions = osImageApi.listVersions(publisher, offer, sku);
|
List<Version> versions = osImageApi.listVersions(publisher, offer, sku);
|
||||||
if (!versions.isEmpty()) {
|
if (!versions.isEmpty()) {
|
||||||
return VMImage.create(publisher, offer, sku, versions.get(0).name(), location, false);
|
return VMImage.create(publisher, offer, sku, versions.get(0).name(), location);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -415,8 +427,18 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
|
||||||
|
|
||||||
List<VMDeployment> vmDeployments = new ArrayList<VMDeployment>();
|
List<VMDeployment> vmDeployments = new ArrayList<VMDeployment>();
|
||||||
for (Deployment d : deployments){
|
for (Deployment d : deployments){
|
||||||
vmDeployments.add(convertDeploymentToVMDeployment(d));
|
// Check that this vm is not generalized and made to custom image
|
||||||
|
try {
|
||||||
|
String storageAccountName = d.name().replaceAll("[^A-Za-z0-9 ]", "") + "stor";
|
||||||
|
String key = api.getStorageAccountApi(azureGroup).getKeys(storageAccountName).key1();
|
||||||
|
if (!BlobHelper.customImageExists(storageAccountName, key))
|
||||||
|
vmDeployments.add(convertDeploymentToVMDeployment(d));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// This might happen if there is no custom images but vm is generalized. No need to list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return vmDeployments;
|
return vmDeployments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,18 +17,24 @@
|
||||||
package org.jclouds.azurecompute.arm.compute.extensions;
|
package org.jclouds.azurecompute.arm.compute.extensions;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
||||||
import com.google.gson.internal.LinkedTreeMap;
|
import com.google.gson.internal.LinkedTreeMap;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.View;
|
||||||
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
||||||
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
|
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
|
||||||
import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
|
import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
|
||||||
import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
|
import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
|
||||||
|
import org.jclouds.azurecompute.arm.domain.StorageServiceKeys;
|
||||||
import org.jclouds.azurecompute.arm.domain.VMImage;
|
import org.jclouds.azurecompute.arm.domain.VMImage;
|
||||||
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
|
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
|
||||||
|
import static java.lang.String.format;
|
||||||
|
import org.jclouds.azurecompute.arm.util.BlobHelper;
|
||||||
import org.jclouds.compute.domain.CloneImageTemplate;
|
import org.jclouds.compute.domain.CloneImageTemplate;
|
||||||
import org.jclouds.compute.domain.Image;
|
import org.jclouds.compute.domain.Image;
|
||||||
import org.jclouds.compute.domain.ImageTemplate;
|
import org.jclouds.compute.domain.ImageTemplate;
|
||||||
|
@ -36,27 +42,25 @@ import org.jclouds.compute.domain.ImageTemplateBuilder;
|
||||||
import org.jclouds.compute.extensions.ImageExtension;
|
import org.jclouds.compute.extensions.ImageExtension;
|
||||||
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
|
import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
|
|
||||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
|
||||||
|
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
|
||||||
|
|
||||||
|
|
||||||
public class AzureComputeImageExtension implements ImageExtension {
|
public class AzureComputeImageExtension implements ImageExtension {
|
||||||
private final AzureComputeApi api;
|
private final AzureComputeApi api;
|
||||||
|
private final ListeningExecutorService userExecutor;
|
||||||
|
private final Supplier<View> blobstore = null;
|
||||||
|
private final String group;
|
||||||
private final Predicate<URI> imageAvailablePredicate;
|
private final Predicate<URI> imageAvailablePredicate;
|
||||||
private final Predicate<String> nodeSuspendedPredicate;
|
private final Predicate<String> nodeSuspendedPredicate;
|
||||||
private final AzureComputeConstants azureComputeConstants;
|
private final AzureComputeConstants azureComputeConstants;
|
||||||
private final ListeningExecutorService userExecutor;
|
|
||||||
private final String group;
|
|
||||||
private final VMImageToImage imageReferenceToImage;
|
private final VMImageToImage imageReferenceToImage;
|
||||||
public static final String CONTAINER_NAME = "vhdsnew";
|
public static final String CONTAINER_NAME = "jclouds";
|
||||||
public static final String CUSTOM_IMAGE_PREFIX = "#";
|
public static final String CUSTOM_IMAGE_PREFIX = "#";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -69,29 +73,28 @@ public class AzureComputeImageExtension implements ImageExtension {
|
||||||
this.userExecutor = userExecutor;
|
this.userExecutor = userExecutor;
|
||||||
this.group = azureComputeConstants.azureResourceGroup();
|
this.group = azureComputeConstants.azureResourceGroup();
|
||||||
this.imageReferenceToImage = imageReferenceToImage;
|
this.imageReferenceToImage = imageReferenceToImage;
|
||||||
this.api = api;
|
|
||||||
this.imageAvailablePredicate = imageAvailablePredicate;
|
this.imageAvailablePredicate = imageAvailablePredicate;
|
||||||
this.nodeSuspendedPredicate = nodeSuspendedPredicate;
|
this.nodeSuspendedPredicate = nodeSuspendedPredicate;
|
||||||
this.azureComputeConstants = azureComputeConstants;
|
this.azureComputeConstants = azureComputeConstants;
|
||||||
|
this.api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImageTemplate buildImageTemplateFromNode(String name, String id) {
|
public ImageTemplate buildImageTemplateFromNode(String name, String id) {
|
||||||
String imageName = name.toLowerCase();
|
String nameLowerCase = name.toLowerCase();
|
||||||
return new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(imageName).build();
|
return new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(nameLowerCase).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Image> createImage(ImageTemplate template) {
|
public ListenableFuture<Image> createImage(ImageTemplate template) {
|
||||||
|
|
||||||
|
|
||||||
final CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
|
final CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
|
||||||
final String id = cloneTemplate.getSourceNodeId();
|
final String id = cloneTemplate.getSourceNodeId();
|
||||||
final String name = cloneTemplate.getName();
|
final String name = cloneTemplate.getName();
|
||||||
final String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "stor";
|
final String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "stor";
|
||||||
|
|
||||||
// VM needs to be stopped before it can be generalized
|
|
||||||
String status = "";
|
|
||||||
api.getVirtualMachineApi(group).stop(id);
|
api.getVirtualMachineApi(group).stop(id);
|
||||||
//Poll until resource is ready to be used
|
|
||||||
if (nodeSuspendedPredicate.apply(id)) {
|
if (nodeSuspendedPredicate.apply(id)) {
|
||||||
return userExecutor.submit(new Callable<Image>() {
|
return userExecutor.submit(new Callable<Image>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -118,26 +121,36 @@ public class AzureComputeImageExtension implements ImageExtension {
|
||||||
disks[1] = datadiskObject.get("name");
|
disks[1] = datadiskObject.get("name");
|
||||||
|
|
||||||
VirtualMachine vm = api.getVirtualMachineApi(group).get(id);
|
VirtualMachine vm = api.getVirtualMachineApi(group).get(id);
|
||||||
String location = vm.location();
|
final VMImage ref = VMImage.create(group, storageAccountName, disks[0], disks[1], name, "custom", vm.location());
|
||||||
final VMImage ref = VMImage.create(CUSTOM_IMAGE_PREFIX + group, CUSTOM_IMAGE_PREFIX + name, disks[0], disks[1], location, false);
|
|
||||||
return imageReferenceToImage.apply(ref);
|
return imageReferenceToImage.apply(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new UncheckedTimeoutException("Image was not created within the time limit: "
|
throw new UncheckedTimeoutException("Image was not created within the time limit: "
|
||||||
+ cloneTemplate.getName());
|
+ cloneTemplate.getName());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final String illegalStateExceptionMessage = format("Node %s was not suspended within %sms.",
|
final String illegalStateExceptionMessage = format("Node %s was not suspended within %sms.",
|
||||||
id, azureComputeConstants.operationTimeout());
|
id, azureComputeConstants.operationTimeout());
|
||||||
throw new IllegalStateException(illegalStateExceptionMessage);
|
throw new IllegalStateException(illegalStateExceptionMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteImage(String id) {
|
public boolean deleteImage(String id) {
|
||||||
|
|
||||||
|
VMImage image = VMImageToImage.decodeFieldsFromUniqueId(id);
|
||||||
|
if (image.custom()) {
|
||||||
|
StorageServiceKeys keys = api.getStorageAccountApi(image.group()).getKeys(image.storage());
|
||||||
|
|
||||||
|
// This removes now all the images in this storage. At least in theory, there should be just one and if there is
|
||||||
|
// more, they should be copies of each other.
|
||||||
|
BlobHelper.deleteContainerIfExists(image.storage(), keys.key1(), "system");
|
||||||
|
return !BlobHelper.customImageExists(image.storage(), keys.key1());
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
|
||||||
|
|
||||||
if (imageReference != null) {
|
if (imageReference != null) {
|
||||||
VMImage vmImage = VMImage.create(imageReference.publisher(), imageReference.offer(), imageReference.sku(),
|
VMImage vmImage = VMImage.create(imageReference.publisher(), imageReference.offer(), imageReference.sku(),
|
||||||
imageReference.version(), locationName, false);
|
imageReference.version(), locationName);
|
||||||
Image image = vmImageToImage.apply(vmImage);
|
Image image = vmImageToImage.apply(vmImage);
|
||||||
builder.imageId(image.getId());
|
builder.imageId(image.getId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jclouds.azurecompute.arm.compute.functions;
|
package org.jclouds.azurecompute.arm.compute.functions;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension.CUSTOM_IMAGE_PREFIX;
|
|
||||||
import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_PASSWORD;
|
import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_PASSWORD;
|
||||||
import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_USERNAME;
|
import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_USERNAME;
|
||||||
|
|
||||||
|
@ -67,8 +66,33 @@ public class VMImageToImage implements Function<VMImage, Image> {
|
||||||
return (imageReference.globallyAvailable() ? "global" : imageReference.location()) + "/" + imageReference.publisher() + "/" + imageReference.offer() + "/" + imageReference.sku();
|
return (imageReference.globallyAvailable() ? "global" : imageReference.location()) + "/" + imageReference.publisher() + "/" + imageReference.offer() + "/" + imageReference.sku();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] decodeFieldsFromUniqueId(final String id) {
|
public static String encodeFieldsToUniqueIdCustom(VMImage imageReference){
|
||||||
return checkNotNull(id, "id").split("/");
|
return (imageReference.globallyAvailable() ? "global" : imageReference.location()) + "/" + imageReference.group() + "/" + imageReference.storage() + "/" + imageReference.vhd1() + "/" + imageReference.offer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VMImage decodeFieldsFromUniqueId(final String id) {
|
||||||
|
String fields[] = checkNotNull(id, "id").split("/");
|
||||||
|
VMImage vmImage;
|
||||||
|
boolean custom = fields.length == 5;
|
||||||
|
if (custom) {
|
||||||
|
/* id fields indexes
|
||||||
|
0: imageReference.location) + "/" +
|
||||||
|
1: imageReference.group + "/" +
|
||||||
|
2: imageReference.storage + "/" +
|
||||||
|
3: imageReference.vhd1 + "/" +
|
||||||
|
4: imageReference.offer
|
||||||
|
*/
|
||||||
|
vmImage = VMImage.create(fields[1], fields[2], fields[3], null, null, fields[4], fields[0]);
|
||||||
|
} else {
|
||||||
|
/* id fields indexes
|
||||||
|
0: imageReference.location) + "/" +
|
||||||
|
1: imageReference.publisher + "/" +
|
||||||
|
2: imageReference.offer + "/" +
|
||||||
|
3: imageReference.sku + "/" +
|
||||||
|
*/
|
||||||
|
vmImage = VMImage.create(fields[1], fields[2], fields[3], null, fields[0]);
|
||||||
|
}
|
||||||
|
return vmImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -80,26 +104,41 @@ public class VMImageToImage implements Function<VMImage, Image> {
|
||||||
public Image apply(final VMImage image) {
|
public Image apply(final VMImage image) {
|
||||||
|
|
||||||
Credentials credentials = new Credentials(AZURE_LOGIN_USERNAME, AZURE_LOGIN_PASSWORD);
|
Credentials credentials = new Credentials(AZURE_LOGIN_USERNAME, AZURE_LOGIN_PASSWORD);
|
||||||
String name = "";
|
if (image.custom()) {
|
||||||
if (image.offer().startsWith(CUSTOM_IMAGE_PREFIX)) {
|
|
||||||
name = image.offer().substring(CUSTOM_IMAGE_PREFIX.length());
|
|
||||||
} else {
|
|
||||||
name = image.offer();
|
|
||||||
}
|
|
||||||
final ImageBuilder builder = new ImageBuilder()
|
|
||||||
.name(name)
|
|
||||||
.description(image.sku())
|
|
||||||
.status(Image.Status.AVAILABLE)
|
|
||||||
.version(image.sku())
|
|
||||||
.id(encodeFieldsToUniqueId(image))
|
|
||||||
.defaultCredentials(LoginCredentials.fromCredentials(credentials))
|
|
||||||
.providerId(image.publisher())
|
|
||||||
.location(image.globallyAvailable() ? null : FluentIterable.from(locations.get())
|
|
||||||
.firstMatch(LocationPredicates.idEquals(image.location()))
|
|
||||||
.get());
|
|
||||||
|
|
||||||
final OperatingSystem.Builder osBuilder = osFamily().apply(image);
|
final ImageBuilder builder = new ImageBuilder()
|
||||||
return builder.operatingSystem(osBuilder.build()).build();
|
.location(FluentIterable.from(locations.get())
|
||||||
|
.firstMatch(LocationPredicates.idEquals(image.location()))
|
||||||
|
.get())
|
||||||
|
.name(image.name())
|
||||||
|
.description("#" + image.group())
|
||||||
|
.status(Image.Status.AVAILABLE)
|
||||||
|
.version(image.storage())
|
||||||
|
.providerId(image.vhd1())
|
||||||
|
.id(encodeFieldsToUniqueIdCustom(image))
|
||||||
|
.defaultCredentials(LoginCredentials.fromCredentials(credentials));
|
||||||
|
|
||||||
|
final OperatingSystem.Builder osBuilder = osFamily().apply(image);
|
||||||
|
Image retimage = builder.operatingSystem(osBuilder.build()).build();
|
||||||
|
return retimage;
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final ImageBuilder builder = new ImageBuilder()
|
||||||
|
.name(image.offer())
|
||||||
|
.description(image.sku())
|
||||||
|
.status(Image.Status.AVAILABLE)
|
||||||
|
.version(image.sku())
|
||||||
|
.id(encodeFieldsToUniqueId(image))
|
||||||
|
.defaultCredentials(LoginCredentials.fromCredentials(credentials))
|
||||||
|
.providerId(image.publisher())
|
||||||
|
.location(image.globallyAvailable() ? null : FluentIterable.from(locations.get())
|
||||||
|
.firstMatch(LocationPredicates.idEquals(image.location()))
|
||||||
|
.get());
|
||||||
|
|
||||||
|
final OperatingSystem.Builder osBuilder = osFamily().apply(image);
|
||||||
|
return builder.operatingSystem(osBuilder.build()).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<VMImage, OperatingSystem.Builder> osFamily() {
|
public static Function<VMImage, OperatingSystem.Builder> osFamily() {
|
||||||
|
@ -124,12 +163,16 @@ public class VMImageToImage implements Function<VMImage, Image> {
|
||||||
family = OsFamily.OEL;
|
family = OsFamily.OEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String sku = image.sku();
|
||||||
|
if (image.custom())
|
||||||
|
sku = image.vhd1();
|
||||||
|
|
||||||
// only 64bit OS images are supported by Azure ARM
|
// only 64bit OS images are supported by Azure ARM
|
||||||
return OperatingSystem.builder().
|
return OperatingSystem.builder().
|
||||||
family(family).
|
family(family).
|
||||||
is64Bit(true).
|
is64Bit(true).
|
||||||
description(image.sku()).
|
description(sku).
|
||||||
version(image.sku());
|
version(sku);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,34 +17,39 @@
|
||||||
package org.jclouds.azurecompute.arm.domain;
|
package org.jclouds.azurecompute.arm.domain;
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.json.SerializedNames;
|
import org.jclouds.json.SerializedNames;
|
||||||
|
|
||||||
@AutoValue
|
@AutoValue
|
||||||
public abstract class VMImage {
|
public abstract class VMImage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The publisher of the image reference.
|
* The publisher of the image reference.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public abstract String publisher();
|
public abstract String publisher();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offer of the image reference.
|
* The offer of the image reference.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public abstract String offer();
|
public abstract String offer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sku of the image reference.
|
* The sku of the image reference.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public abstract String sku();
|
public abstract String sku();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the image reference.
|
* The version of the image reference.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public abstract String version();
|
public abstract String version();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The location from where Image was fetched
|
* The location from where Image was fetched
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public abstract String location();
|
public abstract String location();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,9 +57,50 @@ public abstract class VMImage {
|
||||||
*/
|
*/
|
||||||
public abstract boolean globallyAvailable();
|
public abstract boolean globallyAvailable();
|
||||||
|
|
||||||
@SerializedNames({ "publisher", "offer", "sku", "version", "location", "globallyAvailable"})
|
/**
|
||||||
public static VMImage create(String publisher, String offer, String sku, String version, String location, boolean globallyAvailable) {
|
* The group of the custom image
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract String group();
|
||||||
|
|
||||||
return new AutoValue_VMImage(publisher, offer, sku, version, location, globallyAvailable);
|
/**
|
||||||
|
* The storage of the custom image.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract String storage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vhd1 of the custom image
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract String vhd1();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vhd2 of the custom image.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract String vhd2();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the custom image template.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if custom image
|
||||||
|
*/
|
||||||
|
public abstract boolean custom();
|
||||||
|
|
||||||
|
@SerializedNames({ "publisher", "offer", "sku", "version", "location"})
|
||||||
|
public static VMImage create(String publisher, String offer, String sku, String version, String location) {
|
||||||
|
|
||||||
|
return new AutoValue_VMImage(publisher, offer, sku, version, location, false, null, null, null, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedNames({ "group", "storage", "vhd1", "vhd2", "name", "offer", "location"})
|
||||||
|
public static VMImage create(String group, String storage, String vhd1, String vhd2, String name, String offer, String location) {
|
||||||
|
|
||||||
|
return new AutoValue_VMImage(null, offer, null, null, location, false, group, storage, vhd1, vhd2, name, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.jclouds.ContextBuilder;
|
||||||
|
import org.jclouds.azure.storage.domain.BoundedSet;
|
||||||
|
import org.jclouds.azureblob.AzureBlobClient;
|
||||||
|
import org.jclouds.azureblob.domain.BlobProperties;
|
||||||
|
import org.jclouds.azureblob.domain.ContainerProperties;
|
||||||
|
import org.jclouds.azureblob.domain.ListBlobsResponse;
|
||||||
|
import org.jclouds.azurecompute.arm.domain.VMImage;
|
||||||
|
import org.jclouds.util.Closeables2;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BlobHelper {
|
||||||
|
|
||||||
|
public static void deleteContainerIfExists(String storage, String key, String containerName) {
|
||||||
|
final AzureBlobClient azureBlob = ContextBuilder.newBuilder("azureblob")
|
||||||
|
.credentials(storage, key)
|
||||||
|
.buildApi(AzureBlobClient.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
azureBlob.deleteContainer(containerName);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Closeables2.closeQuietly(azureBlob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean customImageExists(String storage, String key) {
|
||||||
|
final AzureBlobClient azureBlob = ContextBuilder.newBuilder("azureblob")
|
||||||
|
.credentials(storage, key)
|
||||||
|
.buildApi(AzureBlobClient.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return azureBlob.containerExists("system");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Closeables2.closeQuietly(azureBlob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<VMImage> getImages(String containerName, String group,
|
||||||
|
String storageAccountName, String key, String offer, String location) {
|
||||||
|
final AzureBlobClient azureBlob = ContextBuilder.newBuilder("azureblob")
|
||||||
|
.credentials(storageAccountName, key)
|
||||||
|
.buildApi(AzureBlobClient.class);
|
||||||
|
|
||||||
|
|
||||||
|
List<VMImage> list = new ArrayList<VMImage>();
|
||||||
|
try {
|
||||||
|
BoundedSet<ContainerProperties> containerList = azureBlob.listContainers();
|
||||||
|
for (ContainerProperties props : containerList) {
|
||||||
|
if (props.getName().equals("system")) {
|
||||||
|
ListBlobsResponse blobList = azureBlob.listBlobs("system");
|
||||||
|
String osDisk = "";
|
||||||
|
String dataDisk = "";
|
||||||
|
|
||||||
|
for (BlobProperties blob : blobList) {
|
||||||
|
String name = blob.getName();
|
||||||
|
|
||||||
|
if (dataDisk.length() == 0) dataDisk = name.substring(1 + name.lastIndexOf('/'));
|
||||||
|
else if (osDisk.length() == 0) osDisk = name.substring(1 + name.lastIndexOf('/'));
|
||||||
|
}
|
||||||
|
final VMImage ref = VMImage.create(group, storageAccountName, osDisk, dataDisk, "test-create-image", "custom", location);
|
||||||
|
list.add(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Closeables2.closeQuietly(azureBlob);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
|
@ -182,7 +182,7 @@ public class DeploymentTemplateBuilder {
|
||||||
|
|
||||||
String imageName = template.getImage().getName();
|
String imageName = template.getImage().getName();
|
||||||
if (imageName.startsWith(CUSTOM_IMAGE_PREFIX)) {
|
if (imageName.startsWith(CUSTOM_IMAGE_PREFIX)) {
|
||||||
storageAccountName = imageName.substring(CUSTOM_IMAGE_PREFIX.length()); // get group name
|
storageAccountName = template.getImage().getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Strings.isNullOrEmpty(storageAccountName)) {
|
if (Strings.isNullOrEmpty(storageAccountName)) {
|
||||||
|
@ -414,13 +414,12 @@ public class DeploymentTemplateBuilder {
|
||||||
boolean usingMarketplaceImage = true;
|
boolean usingMarketplaceImage = true;
|
||||||
String cusotomImageUri = "";
|
String cusotomImageUri = "";
|
||||||
|
|
||||||
// TODO: make new fields for group information
|
// Handle custom image case if description starts with CUSTOM_IMAGE_PREFIX
|
||||||
String publisher = template.getImage().getProviderId();
|
String vhd1 = template.getImage().getProviderId();
|
||||||
String storageName = template.getImage().getName();
|
String description = template.getImage().getDescription();
|
||||||
String sku = template.getImage().getDescription(); // this is actual VHD
|
if (description.substring(0, CUSTOM_IMAGE_PREFIX.length()).equals(CUSTOM_IMAGE_PREFIX)) {
|
||||||
if (storageName.startsWith(CUSTOM_IMAGE_PREFIX)) {
|
String storageName = template.getImage().getVersion();
|
||||||
storageName = storageName.substring(CUSTOM_IMAGE_PREFIX.length()); // get group name
|
cusotomImageUri = vhd1;
|
||||||
cusotomImageUri = sku;
|
|
||||||
cusotomImageUri = "https://" + storageName + ".blob.core.windows.net/system/Microsoft.Compute/Images/" + AzureComputeImageExtension.CONTAINER_NAME + "/" + cusotomImageUri;
|
cusotomImageUri = "https://" + storageName + ".blob.core.windows.net/system/Microsoft.Compute/Images/" + AzureComputeImageExtension.CONTAINER_NAME + "/" + cusotomImageUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class AzureComputeImageExtensionLiveTest extends BaseImageExtensionLiveTe
|
||||||
properties.setProperty(TIMEOUT_PORT_OPEN, scriptTimeout + "");
|
properties.setProperty(TIMEOUT_PORT_OPEN, scriptTimeout + "");
|
||||||
properties.setProperty(TIMEOUT_NODE_TERMINATED, scriptTimeout + "");
|
properties.setProperty(TIMEOUT_NODE_TERMINATED, scriptTimeout + "");
|
||||||
properties.setProperty(TIMEOUT_NODE_SUSPENDED, scriptTimeout + "");
|
properties.setProperty(TIMEOUT_NODE_SUSPENDED, scriptTimeout + "");
|
||||||
properties.put(RESOURCE_GROUP_NAME, "a5");
|
properties.put(RESOURCE_GROUP_NAME, "jcloudsgroup");
|
||||||
|
|
||||||
properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
|
properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
|
||||||
properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
|
properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
|
||||||
|
@ -86,4 +86,5 @@ public class AzureComputeImageExtensionLiveTest extends BaseImageExtensionLiveTe
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue