org.dasein.cloud.aws.compute.AMI Maven / Gradle / Ivy
/**
* Copyright (C) 2009-2015 Dell, Inc.
* See annotations for authorship information
*
* ====================================================================
* Licensed 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.dasein.cloud.aws.compute;
import org.apache.log4j.Logger;
import org.dasein.cloud.*;
import org.dasein.cloud.aws.AWSCloud;
import org.dasein.cloud.aws.storage.S3Method;
import org.dasein.cloud.compute.*;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.util.APITrace;
import org.dasein.util.CalendarWrapper;
import org.dasein.util.Jiterator;
import org.dasein.util.JiteratorPopulator;
import org.dasein.util.PopulatorThread;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @version 2013.01.1 Fixed a data consistency issue with AWS (issue #21)
*/
public class AMI extends AbstractImageSupport {
static private final Logger logger = Logger.getLogger(AMI.class);
private volatile transient AMICapabilities capabilities;
AMI(AWSCloud provider) {
super(provider);
}
@Override
public void addImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.addImageShare");
try {
setPrivateShare(providerImageId, true, accountNumber);
}
finally {
APITrace.end();
}
}
@Override
public void addPublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.addPublicShare");
try {
setPublicShare(providerImageId, true);
}
finally {
APITrace.end();
}
}
@Override
public ImageCapabilities getCapabilities() {
if( capabilities == null ) {
capabilities = new AMICapabilities(getProvider());
}
return capabilities;
}
@Override
protected MachineImage capture(@Nonnull ImageCreateOptions options, @Nullable AsynchronousTask task) throws CloudException, InternalException {
ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException("No context was set for this request");
}
return captureImage(ctx, options, task);
}
private @Nonnull MachineImage captureImage(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nullable AsynchronousTask task) throws CloudException, InternalException {
APITrace.begin(getProvider(), "captureImage");
try {
if( task != null ) {
task.setStartTime(System.currentTimeMillis());
}
VirtualMachine vm = null;
long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 30L);
while( timeout > System.currentTimeMillis() ) {
try {
//noinspection ConstantConditions
vm = getProvider(). getComputeServices().getVirtualMachineSupport().getVirtualMachine(options.getVirtualMachineId());
if( vm == null || VmState.TERMINATED.equals(vm.getCurrentState()) ) {
break;
}
if( VmState.RUNNING.equals(vm.getCurrentState()) || VmState.STOPPED.equals(vm.getCurrentState()) ) {
break;
}
if( !vm.isPersistent() ) {
if( vm.getPlatform().isWindows() ) {
String bucket = getProvider(). getStorageServices().getOnlineStorageSupport().createBucket("dsnwin" + (System.currentTimeMillis() % 10000), true).getBucketName();
if( bucket == null ) {
throw new CloudException("There is no bucket");
}
return captureWindows(getProvider(). getContext(), options, bucket, task);
}
}
}
catch( Throwable ignore ) {
// ignore
}
try { Thread.sleep(15000L); }
catch( InterruptedException ignore ) { }
}
if( vm == null ) {
throw new CloudException("No such virtual machine: " + options.getVirtualMachineId());
}
String lastMessage = null;
int attempts = 5;
while( attempts > 0 ) {
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.CREATE_IMAGE);
NodeList blocks;
EC2Method method;
Document doc;
/* need to perform the opposite of "reboot" as Amazon's API takes "NoReboot"
Therefore:
If reboot == false, then NoReboot = true
If reboot == true, then NoReboot = false
*/
Boolean reboot = options.getReboot();
if( reboot != null ) {
reboot = !reboot;
parameters.put("NoReboot", reboot.toString());
}
parameters.put("InstanceId", options.getVirtualMachineId());
parameters.put("Name", options.getName());
parameters.put("Description", options.getDescription());
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("imageId");
if( blocks.getLength() > 0 ) {
Node imageIdNode = blocks.item(0);
String id = imageIdNode.getFirstChild().getNodeValue().trim();
MachineImage img = getImage(id);
if( img == null ) {
for( int i=0; i<5; i++ ) {
try { Thread.sleep(5000L * i); }
catch( InterruptedException ignore ) { }
img = getImage(id);
if( img != null ) {
break;
}
}
if( img == null ) {
throw new CloudException("No image exists for " + id + " as created during the capture process");
}
}
if( MachineImageState.DELETED.equals(img.getCurrentState()) ) {
String errorMessage = (String)img.getTag("stateReason");
if( errorMessage != null ) {
if( errorMessage.contains("try again") ) {
lastMessage = errorMessage;
attempts--;
try { Thread.sleep(CalendarWrapper.MINUTE); }
catch( InterruptedException ignore ) { }
continue;
}
throw new CloudException(errorMessage);
}
}
// Add tags
List tags = new ArrayList();
Map meta = options.getMetaData();
meta.put("Name", options.getName());
meta.put("Description", options.getDescription());
for( Map.Entry entry : meta.entrySet() )
tags.add(new Tag(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString()));
if( !tags.isEmpty() ) {
getProvider().createTags(EC2Method.SERVICE_ID, id, tags.toArray(new Tag[tags.size()]));
}
return img;
}
throw new CloudException("No error occurred during imaging, but no machine image was specified");
}
if( lastMessage == null ) {
lastMessage = "Unknown error";
}
throw new CloudException(lastMessage);
}
finally {
APITrace.end();
}
}
private MachineImage captureWindows(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nonnull String bucket, @Nullable AsynchronousTask task) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.captureWindows");
try {
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.BUNDLE_INSTANCE);
StringBuilder uploadPolicy = new StringBuilder();
NodeList blocks;
EC2Method method;
Document doc;
uploadPolicy.append("{");
uploadPolicy.append("\"expiration\":\"");
{
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date date = new Date(System.currentTimeMillis() + (CalendarWrapper.HOUR*12L));
uploadPolicy.append(fmt.format(date));
}
uploadPolicy.append("\",\"conditions\":");
{
uploadPolicy.append("[");
uploadPolicy.append("{\"bucket\":\"");
uploadPolicy.append(bucket);
uploadPolicy.append("\"},");
uploadPolicy.append("{\"acl\": \"ec2-bundle-read\"},");
uploadPolicy.append("[\"starts-with\", \"$key\", \"");
uploadPolicy.append(options.getName());
uploadPolicy.append("\"]");
uploadPolicy.append("]");
}
uploadPolicy.append("}");
String base64Policy;
try {
base64Policy = S3Method.toBase64(uploadPolicy.toString().getBytes("utf-8"));
}
catch( UnsupportedEncodingException e ) {
logger.error(e);
e.printStackTrace();
throw new InternalException(e);
}
parameters.put("InstanceId", options.getVirtualMachineId());
parameters.put("Storage.S3.Bucket", bucket);
parameters.put("Storage.S3.Prefix", options.getName());
parameters.put("Storage.S3.AWSAccessKeyId", ctx.getAccountNumber());
parameters.put("Storage.S3.UploadPolicy", base64Policy);
parameters.put("Storage.S3.UploadPolicySignature", getProvider(). signUploadPolicy(base64Policy));
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("bundleId");
if( blocks.getLength() < 1 ) {
throw new CloudException("Unable to identify the bundle task ID.");
}
String bundleId = blocks.item(0).getFirstChild().getNodeValue();
String manifest = (bucket + "/" + options.getName() + ".manifest.xml");
if( task == null ) {
task = new AsynchronousTask();
task.setStartTime(System.currentTimeMillis());
}
waitForBundle(bundleId, manifest, options.getPlatform(), options.getName(), options.getDescription(), task);
Throwable t = task.getTaskError();
if( t != null ) {
if( t instanceof CloudException ) {
throw (CloudException)t;
}
else if( t instanceof InternalException ) {
throw (InternalException)t;
}
throw new InternalException(t);
}
MachineImage img = task.getResult();
if( img == null ) {
throw new CloudException("No image was created, but no error was given");
}
return img;
}
finally {
APITrace.end();
}
}
@Override
public @Nonnull String copyImage(@Nonnull ImageCopyOptions options) throws CloudException, InternalException {
APITrace.begin(getProvider(), "copyImage");
AWSCloud targetProvider = null;
try {
/* Steps overview:
* 1. Connect to target region using the same account
* 2. Invoke EC2 'copyImage' method in the context of target region
*/
final ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException( "Provider context is necessary for this request" );
}
final String sourceRegionId = ctx.getRegionId();
final String targetRegionId = options.getTargetRegionId();
final ProviderContext targetContext = ctx.copy(targetRegionId);
targetProvider = ( AWSCloud ) targetContext.connect();
if ( targetProvider.testContext() == null ) {
throw new CloudException( "Could not connect with the same account to the copy target region: " +
targetRegionId );
}
// Invoke the EC2 method
Map parameters = targetProvider.getStandardParameters(
targetProvider.getContext(), EC2Method.COPY_IMAGE);
parameters.put( "SourceRegion", sourceRegionId );
parameters.put( "SourceImageId", options.getProviderImageId() );
if (options.getName() != null) {
parameters.put( "Name", options.getName() );
}
if (options.getDescription() != null) {
parameters.put( "Description", options.getDescription() );
}
Document doc;
try {
EC2Method method = new EC2Method(targetProvider, parameters);
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
NodeList blocks = doc.getElementsByTagName( "imageId" );
if( blocks.getLength() > 0 ) {
Node imageIdNode = blocks.item(0);
return imageIdNode.getFirstChild().getNodeValue().trim();
}
throw new CloudException( "No error occurred during imaging, but no machine image was specified" );
}
finally {
if ( targetProvider != null ) {
targetProvider.close();
}
APITrace.end();
}
}
private @Nonnull Iterable executeImageSearch(int pass, boolean forPublic, @Nonnull ImageFilterOptions options) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.executeImageSearch");
try {
final ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException("No context was set for this request");
}
final String regionId = ctx.getRegionId();
if( regionId == null ) {
throw new CloudException("No region was set for this request");
}
Architecture architecture = options.getArchitecture();
if( architecture != null && !architecture.equals(Architecture.I32) && !architecture.equals(Architecture.I64) ) {
if( !options.isMatchesAny() ) {
return Collections.emptyList();
}
}
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.DESCRIBE_IMAGES);
final List list = new ArrayList();
if( forPublic ) {
if( pass == 1 ) {
parameters.put("ExecutableBy.1", "all");
}
else {
parameters.put("ExecutableBy.1", "self");
}
}
else {
if( pass == 1 ) {
parameters.put("ExecutableBy.1", "self");
}
else {
parameters.put("Owner", "self");
}
}
final ImageFilterOptions finalOptions = fillImageFilterParameters(forPublic, options, parameters);
EC2Method method = new EC2Method(getProvider(), parameters);
try {
method.invoke(
new DescribeImagesResponseParser(
getProvider(). getContext().getRegionId(),
(getProvider(). getEC2Provider().isAWS() ? null : getProvider(). getContext().getAccountNumber()),
finalOptions,
list));
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
return list;
}
finally {
APITrace.end();
}
}
@Override
public @Nullable MachineImage getImage(@Nonnull String providerImageId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.getImage");
try {
ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException("No context was set for this request");
}
if( getProvider(). getEC2Provider().isAWS() ) {
Map parameters = getProvider(). getStandardParameters(ctx, EC2Method.DESCRIBE_IMAGES);
NodeList blocks;
EC2Method method;
Document doc;
parameters.put("ImageId", providerImageId);
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && code.startsWith("InvalidAMIID") ) {
return null;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("imagesSet");
for( int i=0; i parameters = getProvider(). getStandardParameters(getContext(), EC2Method.DESCRIBE_IMAGES);
if( getProvider(). getEC2Provider().isAWS() ) {
parameters.put("Owner", getContext().getAccountNumber());
}
EC2Method method = new EC2Method(getProvider(), parameters);
try {
method.invoke();
return true;
}
catch( EC2Exception e ) {
String msg = e.getSummary();
if( msg != null && msg.contains("not able to validate the provided access credentials") ) {
return false;
}
logger.error("AWS Error checking subscription: " + e.getCode() + "/" + e.getSummary());
if( logger.isDebugEnabled() ) {
e.printStackTrace();
}
throw new CloudException(e);
}
}
finally {
APITrace.end();
}
}
public @Nonnull Iterable listImageStatus(final @Nonnull ImageClass cls) throws CloudException, InternalException {
getProvider(). hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
APITrace.begin(getProvider(), "Image.listImageStatus");
try {
try {
TreeSet ids = new TreeSet();
for( ResourceStatus status : executeStatusList(1, cls) ) {
ids.add(status.getProviderResourceId());
iterator.push(status);
}
for( ResourceStatus status : executeStatusList(2, cls) ) {
if( !ids.contains(status.getProviderResourceId()) ) {
iterator.push(status);
}
}
}
finally {
getProvider(). release();
}
}
finally {
APITrace.end();
}
}
});
populator.populate();
return populator.getResult();
}
private @Nonnull Iterable executeStatusList(int pass, @Nonnull ImageClass cls) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.executeStatusList");
try {
ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException("No context was set for this request");
}
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.DESCRIBE_IMAGES);
ArrayList list = new ArrayList();
EC2Method method;
NodeList blocks;
Document doc;
String accountNumber = ctx.getAccountNumber();
if( pass == 1 ) {
parameters.put("ExecutableBy.1", "self");
}
else if( getProvider(). getEC2Provider().isAWS() ) {
parameters.put("Owner", "self");
}
String t = "machine";
switch( cls ) {
case MACHINE: t = "machine"; break;
case KERNEL: t = "kernel"; break;
case RAMDISK: t = "ramdisk"; break;
}
parameters.put("Filter.1.Name", "image-type");
parameters.put("Filter.1.Value", t);
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("imagesSet");
for( int i=0; i parameters) throws CloudException, InternalException {
int filter = 1;
if( forPublic ) {
parameters.put("Filter." + filter + ".Name", "state");
parameters.put("Filter." + (filter++) + ".Value.1", "available");
}
if( options.isMatchesAny() && options.getCriteriaCount() > 1 ) {
if( forPublic ) {
return options;
}
else {
options.withAccountNumber(getContext().getAccountNumber());
return options;
}
}
String owner = options.getAccountNumber();
if( owner != null ) {
parameters.put("Owner", owner);
}
Architecture architecture = options.getArchitecture();
if( architecture != null && (architecture.equals(Architecture.I32) || architecture.equals(Architecture.I64)) ) {
parameters.put("Filter." + filter + ".Name", "architecture");
parameters.put("Filter." + (filter++) + ".Value.1", Architecture.I32.equals(options.getArchitecture()) ? "i386" : "x86_64");
}
Platform platform = options.getPlatform();
if( platform != null && platform.equals(Platform.WINDOWS) ) {
parameters.put("Filter." + filter + ".Name", "platform");
parameters.put("Filter." + (filter++) + ".Value.1", "windows");
}
ImageClass cls= options.getImageClass();
String t = "machine";
if( cls != null ) {
switch( cls ) {
case MACHINE: t = "machine"; break;
case KERNEL: t = "kernel"; break;
case RAMDISK: t = "ramdisk"; break;
}
parameters.put("Filter." + filter + ".Name", "image-type");
parameters.put("Filter." + (filter++) + ".Value.1", t);
}
Map extraParameters = new HashMap();
AWSCloud.addExtraParameters( extraParameters, getProvider(). getTagFilterParams( options.getTags(), filter ) );
parameters.putAll(extraParameters);
String regex = options.getRegex();
options = ImageFilterOptions.getInstance();
if( regex != null ) {
options.matchingRegex(regex);
}
if( platform != null ) {
options.onPlatform(platform);
}
return options;
}
@Override
public @Nonnull Iterable listShares(@Nonnull String forMachineImageId) throws CloudException, InternalException {
return sharesAsList(forMachineImageId);
}
@Override
public @Nonnull String[] mapServiceAction(@Nonnull ServiceAction action) {
if( action.equals(MachineImageSupport.ANY) ) {
return new String[] { EC2Method.EC2_PREFIX + "*" };
}
if( action.equals(MachineImageSupport.DOWNLOAD_IMAGE) ) {
return new String[0];
}
else if( action.equals(MachineImageSupport.GET_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.DESCRIBE_IMAGES };
}
else if( action.equals(MachineImageSupport.IMAGE_VM) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.CREATE_IMAGE, EC2Method.EC2_PREFIX + EC2Method.REGISTER_IMAGE };
}
else if( action.equals(MachineImageSupport.COPY_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.COPY_IMAGE };
}
else if( action.equals(MachineImageSupport.LIST_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.DESCRIBE_IMAGES };
}
else if( action.equals(MachineImageSupport.MAKE_PUBLIC) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.MODIFY_IMAGE_ATTRIBUTE };
}
else if( action.equals(MachineImageSupport.REGISTER_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.REGISTER_IMAGE };
}
else if( action.equals(MachineImageSupport.REMOVE_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.DEREGISTER_IMAGE };
}
else if( action.equals(MachineImageSupport.SHARE_IMAGE) ) {
return new String[] { EC2Method.EC2_PREFIX + EC2Method.MODIFY_IMAGE_ATTRIBUTE };
}
else if( action.equals(MachineImageSupport.UPLOAD_IMAGE) ) {
return new String[0];
}
return new String[0];
}
/*
private void populateImages(@Nonnull ProviderContext ctx, @Nullable String accountNumber, @Nonnull Jiterator iterator, Map extraParameters) throws CloudException, InternalException {
APITrace.begin(getProvider(), "populateImages");
try {
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.DESCRIBE_IMAGES);
EC2Method method;
NodeList blocks;
Document doc;
if( accountNumber == null ) {
accountNumber = ctx.getAccountNumber();
}
if( getProvider(). getEC2Provider().isAWS() ) {
parameters.put("Owner", accountNumber);
}
getProvider(). putExtraParameters( parameters, extraParameters );
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("imagesSet");
for( int i=0; i parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.REGISTER_IMAGE);
NodeList blocks;
EC2Method method;
Document doc;
parameters.put("ImageLocation", options.getBundleLocation());
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("imageId");
if( blocks.getLength() > 0 ) {
Node imageIdNode = blocks.item(0);
String id = imageIdNode.getFirstChild().getNodeValue().trim();
MachineImage img = getMachineImage(id);
if( img == null ) {
throw new CloudException("Expected to find newly registered machine image '" + id + "', but none was found");
}
return img;
}
throw new CloudException("No machine image was registered, but no error was thrown");
}
finally {
APITrace.end();
}
}
@Override
public void remove( @Nonnull String providerImageId, boolean checkState ) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.remove");
try {
if ( checkState ) {
long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 30L);
while ( timeout > System.currentTimeMillis() ) {
try {
MachineImage img = getMachineImage( providerImageId );
if ( img == null || MachineImageState.DELETED.equals( img.getCurrentState() ) ) {
return;
}
if ( MachineImageState.ACTIVE.equals( img.getCurrentState() ) ) {
break;
}
} catch ( Throwable ignore ) {
// ignore
}
try {
Thread.sleep( 15000L );
}
catch ( InterruptedException ignore ) {
}
}
}
Map parameters = getProvider(). getStandardParameters( getProvider(). getContext(), EC2Method.DEREGISTER_IMAGE );
NodeList blocks;
EC2Method method;
Document doc;
parameters.put( "ImageId", providerImageId );
method = new EC2Method( getProvider(), parameters );
try {
doc = method.invoke();
} catch ( EC2Exception e ) {
logger.error( e.getSummary() );
throw new CloudException( e );
}
blocks = doc.getElementsByTagName( "return" );
if ( blocks.getLength() > 0 ) {
Node imageIdNode = blocks.item( 0 );
if ( !imageIdNode.getFirstChild().getNodeValue().trim().equals( "true" ) ) {
throw new CloudException( "Failed to de-register image " + providerImageId );
}
}
}
finally {
APITrace.end();
}
}
@Override
public void removeAllImageShares(@Nonnull String providerImageId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.removeAllImageShares");
try {
List shares = sharesAsList(providerImageId);
setPrivateShare(providerImageId, false, shares.toArray(new String[shares.size()]));
setPublicShare(providerImageId, false);
}
finally {
APITrace.end();
}
}
@Override
public void removeImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.removeImageShare");
try {
setPrivateShare(providerImageId, false, accountNumber);
}
finally {
APITrace.end();
}
}
@Override
public void removePublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.removePublicShare");
try {
setPublicShare(providerImageId, false);
}
finally {
APITrace.end();
}
}
@Override
public @Nonnull Iterable listImages(@Nullable ImageFilterOptions options) throws CloudException, InternalException {
final ImageFilterOptions opts;
if( options == null ) {
opts = ImageFilterOptions.getInstance();
}
else {
opts = options;
}
getProvider(). hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
APITrace.begin(getProvider(), "Image.listImages");
try {
try {
Set ids = new TreeSet();
for( MachineImage img : executeImageSearch(1, false, opts) ) {
ids.add(img.getProviderMachineImageId());
iterator.push(img);
}
for( MachineImage img : executeImageSearch(2, false, opts) ) {
if( !ids.contains(img.getProviderMachineImageId()) ) {
iterator.push(img);
}
}
}
finally {
getProvider(). release();
}
}
finally {
APITrace.end();
}
}
});
populator.populate();
return populator.getResult();
}
@Override
public @Nonnull Iterable searchPublicImages(final @Nonnull ImageFilterOptions options) throws CloudException, InternalException {
getProvider(). hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
APITrace.begin(getProvider(), "searchPublicImages");
try {
try {
for( MachineImage img : executeImageSearch(1, true, options) ) {
iterator.push(img);
}
for( MachineImage img : executeImageSearch(2, true, options) ) {
iterator.push(img);
}
}
finally {
getProvider(). release();
}
}
finally {
APITrace.end();
}
}
});
populator.populate();
return populator.getResult();
}
private void setPrivateShare(@Nonnull String imageId, boolean allowed, @Nonnull String ... accountIds) throws CloudException, InternalException {
if( accountIds == null || accountIds.length < 1 ) {
return;
}
long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 30L);
while( timeout > System.currentTimeMillis() ) {
try {
MachineImage img = getMachineImage(imageId);
if( img == null ) {
throw new CloudException("The machine image " + imageId + " disappeared while waiting to set sharing");
}
if( MachineImageState.ACTIVE.equals(img.getCurrentState()) ) {
break;
}
}
catch( Throwable ignore ) {
// ignore
}
try { Thread.sleep(15000L); }
catch( InterruptedException ignore ) { }
}
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.MODIFY_IMAGE_ATTRIBUTE);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("ImageId", imageId);
for( int i=0; i 0 ) {
if( !blocks.item(0).getFirstChild().getNodeValue().equalsIgnoreCase("true") ) {
throw new CloudException("Share of image failed without explanation.");
}
}
timeout = System.currentTimeMillis() + (CalendarWrapper.SECOND * 30);
while( timeout > System.currentTimeMillis() ) {
try {
MachineImage img = getMachineImage(imageId);
if( img == null ) {
return;
}
boolean present = true;
for( String accountId : accountIds ) {
boolean found = false;
for( String share : listShares(imageId) ) {
if( share.equals(accountId) ) {
found = true;
break;
}
}
if( !found ) {
present = false;
break;
}
}
if( present == allowed ) {
return;
}
}
catch( Throwable ignore ) {
// ignore
}
try { Thread.sleep(2000L); }
catch( InterruptedException ignore ) { }
}
}
private void setPublicShare(@Nonnull String imageId, boolean allowed) throws CloudException, InternalException {
long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 30L);
while( timeout > System.currentTimeMillis() ) {
try {
MachineImage img = getMachineImage(imageId);
if( img == null ) {
throw new CloudException("The machine image " + imageId + " disappeared while waiting to set sharing");
}
if( MachineImageState.ACTIVE.equals(img.getCurrentState()) ) {
break;
}
}
catch( Throwable ignore ) {
// ignore
}
try { Thread.sleep(15000L); }
catch( InterruptedException ignore ) { }
}
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.MODIFY_IMAGE_ATTRIBUTE);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("ImageId", imageId);
if( allowed ) {
parameters.put("LaunchPermission.Add.1.Group", "all");
}
else {
parameters.put("LaunchPermission.Remove.1.Group", "all");
}
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && code.startsWith("InvalidImageID") ) {
return;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("return");
if( blocks.getLength() > 0 ) {
if( !blocks.item(0).getFirstChild().getNodeValue().equalsIgnoreCase("true") ) {
throw new CloudException("Share of image failed without explanation.");
}
}
timeout = System.currentTimeMillis() + (CalendarWrapper.SECOND * 30);
while( timeout > System.currentTimeMillis() ) {
try {
MachineImage img = getMachineImage(imageId);
if( img == null ) {
return;
}
if( allowed == isImageSharedWithPublic(imageId) ) {
return;
}
}
catch( Throwable ignore ) {
// ignore
}
try { Thread.sleep(2000L); }
catch( InterruptedException ignore ) { }
}
}
private @Nonnull List sharesAsList(@Nonnull String forMachineImageId) throws CloudException, InternalException {
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.DESCRIBE_IMAGE_ATTRIBUTE);
ArrayList list = new ArrayList();
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("ImageId", forMachineImageId);
parameters.put("Attribute", "launchPermission");
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && code.startsWith("InvalidImageID") ) {
return list;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("launchPermission");
for( int i=0; i 0 ) {
list.add(userId);
}
}
}
}
}
}
}
return list;
}
@Override
public boolean supportsCustomImages() {
return true;
}
@Override
public boolean supportsImageCapture(@Nonnull MachineImageType type) throws CloudException, InternalException {
return getCapabilities().supportsImageCapture(type);
}
@Override
public boolean supportsImageSharing() throws CloudException, InternalException{
return getCapabilities().supportsImageSharing();
}
@Override
public boolean supportsImageSharingWithPublic() throws CloudException, InternalException{
return getCapabilities().supportsImageSharingWithPublic();
}
@Override
public boolean supportsPublicLibrary(@Nonnull ImageClass cls) throws CloudException, InternalException {
return getCapabilities().supportsPublicLibrary(cls);
}
private static class BundleTask {
public String bundleId;
public double progress;
public String message;
public String state;
}
private BundleTask toBundleTask(Node node) throws CloudException, InternalException {
NodeList attributes = node.getChildNodes();
BundleTask task = new BundleTask();
for( int i=0; i 0 ) {
value = attribute.getFirstChild().getNodeValue().trim();
}
if( value != null && value.equalsIgnoreCase("available") ) {
state = MachineImageState.ACTIVE;
}
else if( value != null && value.equalsIgnoreCase("failed") ) {
state = MachineImageState.DELETED;
}
else {
state = MachineImageState.PENDING;
}
}
}
if( imageId == null ) {
return null;
}
return new ResourceStatus(imageId, state);
}
private @Nullable MachineImage toMachineImage(@Nullable Node node) throws CloudException, InternalException {
if( node == null ) {
return null;
}
ProviderContext ctx = getProvider(). getContext();
if( ctx == null ) {
throw new CloudException("No context was set for this request");
}
String regionId = ctx.getRegionId();
if( regionId == null ) {
throw new CloudException("No region was set for this request");
}
NodeList attributes = node.getChildNodes();
List volumes = new ArrayList();
String location = null;
ImageClass imgClass = ImageClass.MACHINE;
MachineImageState state = null;
String amiId = null;
String reason = null;
String ownerId = null;
boolean isPublic = false;
Architecture arch = Architecture.I64;
Platform platform = null;
String imgName = null;
String description = null;
MachineImageType type = null;
MachineImageFormat storageFormat = null;
Node tagsNode = null;
String virtualizationType = null;
String hypervisor = null;
for( int i=0; i 0 ) {
value = attribute.getFirstChild().getNodeValue().trim();
}
if( value != null && value.equalsIgnoreCase("available") ) {
state = MachineImageState.ACTIVE;
}
else if( value != null && value.equalsIgnoreCase("failed") ) {
state = MachineImageState.DELETED;
}
else {
state = MachineImageState.PENDING;
}
}
else if( name.equalsIgnoreCase("statereason") && attribute.hasChildNodes() ) {
NodeList parts = attribute.getChildNodes();
for( int j=0; j 0 ) {
value = attribute.getFirstChild().getNodeValue();
}
if( value != null ) {
ownerId = value.trim();
}
}
else if( name.equals("isPublic") ) {
String value = null;
if( attribute.getChildNodes().getLength() > 0 ) {
value = attribute.getFirstChild().getNodeValue();
}
isPublic = value != null && value.trim().equalsIgnoreCase("true");
}
else if( name.equals("architecture") ) {
String value = attribute.getFirstChild().getNodeValue().trim();
if( value.equals("i386") ) {
arch = Architecture.I32;
}
}
else if( name.equals("platform") ) {
if( attribute.hasChildNodes() ) {
platform = Platform.guess(attribute.getFirstChild().getNodeValue());
}
}
else if( name.equals("name") ) {
if( attribute.hasChildNodes() ) {
imgName = attribute.getFirstChild().getNodeValue();
}
}
else if( name.equals("description") ) {
if( attribute.hasChildNodes() ) {
description = attribute.getFirstChild().getNodeValue();
}
}
else if( name.equals("rootDeviceType") ) {
if( attribute.hasChildNodes() ) {
String value = attribute.getFirstChild().getNodeValue();
if( value.equalsIgnoreCase("ebs") ) {
type = MachineImageType.VOLUME; // ebs
}
else {
type = MachineImageType.STORAGE; // instance-store
storageFormat = MachineImageFormat.AWS;
}
}
}
else if ( "virtualizationType".equals(name) ) {
virtualizationType = attribute.getFirstChild().getNodeValue();
}
else if ( "hypervisor".equals(name) ) {
hypervisor = attribute.getFirstChild().getNodeValue();
}
else if ( name.equals("tagSet")) {
tagsNode = attribute;
}
else if( name.equalsIgnoreCase("blockDeviceMapping") ) {
if( attribute.hasChildNodes() ) {
NodeList devices = attribute.getChildNodes();
for( int z = 0; z < devices.getLength(); z++ ) {
NodeList param = devices.item(z).getChildNodes();
String deviceName = null;
String snapshotId = null;
Integer volumeSize = null;
String volumeType = null;
Integer iops = null;
if( devices.item(z).getNodeName().equalsIgnoreCase("item") ) {
for( int j = 0; j < param.getLength(); j++ ) {
String nodeName = param.item(j).getNodeName();
if( nodeName.equalsIgnoreCase("deviceName") ) {
deviceName = param.item(j).getFirstChild().getNodeValue().trim();
}
else if( nodeName.equalsIgnoreCase("ebs") ) {
NodeList ebs = param.item(j).getChildNodes();
for( int k = 0; k < ebs.getLength(); k++ ) {
String ebsName = ebs.item(k).getNodeName();
if( ebsName.equalsIgnoreCase("snapshotId") ) {
snapshotId = ebs.item(k).getFirstChild().getNodeValue().trim();
}
else if( ebsName.equalsIgnoreCase("volumeSize") ) {
volumeSize = Integer.valueOf(ebs.item(k).getFirstChild().getNodeValue().trim());
}
else if( ebsName.equalsIgnoreCase("volumeType") ) {
volumeType = ebs.item(k).getFirstChild().getNodeValue().trim();
}
else if( ebsName.equalsIgnoreCase("iops") ) {
iops = Integer.valueOf(ebs.item(k).getFirstChild().getNodeValue().trim());
}
}
}
}
if( deviceName != null || snapshotId != null || volumeSize != null || volumeType != null || iops != null ) {
volumes.add(MachineImageVolume.getInstance(deviceName, snapshotId, volumeSize, volumeType, iops));
}
}
}
}
}
}
if( platform == null ) {
if( location != null ) {
platform = Platform.guess(location);
}
else {
platform = Platform.UNKNOWN;
}
}
if( Platform.UNKNOWN.equals(platform) && description != null ) {
platform = Platform.guess(description);
}
if( location != null ) {
String[] parts = location.split("/");
if( parts != null && parts.length > 1 ) {
location = parts[parts.length - 1];
}
int i = location.indexOf(".manifest.xml");
if( i > -1 ) {
location = location.substring(0, i);
}
if( imgName == null || imgName.isEmpty() ) {
imgName = location;
}
if( description == null || description.isEmpty() ) {
description = createDescription(location, arch, platform);
}
}
else {
if( imgName == null || imgName.isEmpty() ) {
imgName = amiId;
}
if( description == null || description.isEmpty() ) {
description = createDescription(imgName, arch, platform);
}
}
if( !getProvider(). getEC2Provider().isAWS() ) {
ownerId = ctx.getAccountNumber();
}
MachineImage image = MachineImage.getInstance(ownerId, regionId, amiId, imgClass, state, imgName, description, arch, platform);
image.withVolumes(volumes);
if( hypervisor != null ) {
image.getProviderMetadata().put("hypervisor", hypervisor);
}
if( virtualizationType != null ) {
image.getProviderMetadata().put("virtualizationType", virtualizationType);
}
if( isPublic ) {
image.sharedWithPublic();
}
image.setTag("public", String.valueOf(isPublic)); // for compatibility's sake
image.setTag("stateReason", reason);
if( tagsNode != null ) {
getProvider().setTags(tagsNode, image);
}
if( type != null ) {
image.withType(type);
}
if( storageFormat != null ) {
image.withStorageFormat(storageFormat);
}
return image;
}
public static String createDescription(String title, Architecture arch, Platform platform) {
StringBuilder sb = new StringBuilder();
sb.append(title);
if( arch != null || platform != null ) {
sb.append(" (");
if( arch != null ) {
sb.append(arch.toString()).append(" ");
}
if( platform != null ) {
sb.append(platform.toString());
}
sb.append(")");
}
return sb.toString();
}
@Override
public void updateTags(@Nonnull String imageId, @Nonnull Tag... tags) throws CloudException, InternalException {
updateTags(new String[]{imageId}, tags);
}
@Override
public void updateTags(@Nonnull String[] imageIds, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.updateTags");
try {
getProvider(). createTags(EC2Method.SERVICE_ID, imageIds, tags);
}
finally {
APITrace.end();
}
}
@Override
public void removeTags(@Nonnull String imageId, @Nonnull Tag... tags) throws CloudException, InternalException {
removeTags(new String[]{imageId}, tags);
}
@Override
public void removeTags(@Nonnull String[] imageIds, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Image.removeTags");
try {
getProvider(). removeTags(EC2Method.SERVICE_ID, imageIds, tags);
}
finally {
APITrace.end();
}
}
private void waitForBundle(@Nonnull String bundleId, @Nonnull String manifest, @Nonnull Platform platform, @Nonnull String name, @Nonnull String description, AsynchronousTask task) {
APITrace.begin(getProvider(), "Image.waitForBundle");
try {
try {
long failurePoint = -1L;
while( !task.isComplete() ) {
Map parameters = getProvider(). getStandardParameters(getProvider(). getContext(), EC2Method.DESCRIBE_BUNDLE_TASKS);
NodeList blocks;
EC2Method method;
Document doc;
parameters.put("BundleId", bundleId);
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("bundleInstanceTasksSet");
for( int i=0; i (CalendarWrapper.MINUTE * 2) ) {
message = "Bundle failed without further information.";
}
}
if( message != null ) {
task.complete(new CloudException(message));
}
}
else if( bt.state.equals("pending") || bt.state.equals("waiting-for-shutdown") ) {
task.setPercentComplete(0.0);
}
else if( bt.state.equals("bundling") ) {
double p = bt.progress/2;
if( p > 50.00 ) {
p = 50.00;
}
task.setPercentComplete(p);
}
else if( bt.state.equals("storing") ) {
double p = 50.0 + bt.progress/2;
if( p > 100.0 ) {
p = 100.0;
}
task.setPercentComplete(p);
}
else {
task.setPercentComplete(0.0);
}
}
}
}
}
if( !task.isComplete() ) {
try { Thread.sleep(20000L); }
catch( InterruptedException ignore ) { }
}
}
}
catch( Throwable t ) {
logger.error(t);
t.printStackTrace();
task.complete(t);
}
}
finally {
APITrace.end();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy