<commit_before>/**
 * Copyright (C) 2009-2012 enStratus Networks Inc
 *
 * ====================================================================
 * 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 java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.log4j.Logger;
import org.dasein.cloud.AsynchronousTask;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.OperationNotSupportedException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.Requirement;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.Tag;
import org.dasein.cloud.aws.AWSCloud;
import org.dasein.cloud.aws.storage.S3Method;
import org.dasein.cloud.compute.AbstractImageSupport;
import org.dasein.cloud.compute.Architecture;
import org.dasein.cloud.compute.ImageClass;
import org.dasein.cloud.compute.ImageCreateOptions;
import org.dasein.cloud.compute.ImageFilterOptions;
import org.dasein.cloud.compute.MachineImage;
import org.dasein.cloud.compute.MachineImageFormat;
import org.dasein.cloud.compute.MachineImageState;
import org.dasein.cloud.compute.MachineImageSupport;
import org.dasein.cloud.compute.MachineImageType;
import org.dasein.cloud.compute.Platform;
import org.dasein.cloud.compute.VirtualMachine;
import org.dasein.cloud.compute.VmState;
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;

/**
 * @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 AWSCloud provider = null;
	
	AMI(AWSCloud provider) {
		super(provider);
        this.provider = provider;
	}

    @Override
    public void addImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber) throws CloudException, InternalException {
        APITrace.begin(provider, "addImageShare");
        try {
            setPrivateShare(providerImageId, true, accountNumber);
        }
        finally {
            APITrace.end();
        }
    }

    @Override
    public void addPublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
        APITrace.begin(provider, "addPublicShare");
        try {
            setPublicShare(providerImageId, true);
        }
        finally {
            APITrace.end();
        }
    }

    private @Nonnull MachineImage captureImage(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        APITrace.begin(provider, "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 = provider.getComputeServices().getVirtualMachineSupport().getVirtualMachine(options.getVirtualMachineId());
                    if( vm == null || VmState.TERMINATED.equals(vm.getCurrentState()) ) {
                        break;
                    }
                    if( !vm.isPersistent() ) {
                        throw new OperationNotSupportedException("You cannot capture instance-backed virtual machines");
                    }
                    if( VmState.RUNNING.equals(vm.getCurrentState()) || VmState.STOPPED.equals(vm.getCurrentState()) ) {
                        break;
                    }
                }
                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 ) {
                if( vm.getPlatform().isWindows() ) {
                    String bucket = provider.getStorageServices().getBlobStoreSupport().createBucket("dsnwin" + (System.currentTimeMillis() % 10000), true).getBucketName();

                    if( bucket == null ) {
                        throw new CloudException("There is no bucket");
                    }
                    return captureWindows(ctx, options, bucket, task);
                }
                else {
                    Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.CREATE_IMAGE);
                    NodeList blocks;
                    EC2Method method;
                    Document doc;

                    parameters.put("InstanceId", options.getVirtualMachineId());
                    parameters.put("Name", options.getName());
                    parameters.put("Description", options.getDescription());
                    method = new EC2Method(provider, provider.getEc2Url(), 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);
                            }
                        }
                        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();
        }
    }

    @Override
    protected MachineImage capture(@Nonnull ImageCreateOptions options, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        ProviderContext ctx = provider.getContext();

        if( ctx == null ) {
            throw new CloudException("No context was set for this request");
        }
        return captureImage(ctx, options, task);
    }

    private MachineImage captureWindows(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nonnull String bucket, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        APITrace.begin(provider, "captureWindows");
        try {
            Map<String,String> parameters = provider.getStandardParameters(provider.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", provider.signUploadPolicy(base64Policy));
            method = new EC2Method(provider, provider.getEc2Url(), 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<MachineImage>();
                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();
        }
    }

    private @Nonnull Iterable<MachineImage> executeImageSearch(@Nullable String ownerId, @Nullable String keyword, @Nullable Platform platform, @Nullable Architecture architecture, @Nonnull ImageClass cls) throws CloudException, InternalException {
        APITrace.begin(provider, "executeImageSearch");
        try {
            ProviderContext ctx = provider.getContext();

            if( ctx == null ) {
                throw new CloudException("No context was set for this request");
            }
            Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.DESCRIBE_IMAGES);
            ArrayList<MachineImage> list = new ArrayList<MachineImage>();
            NodeList blocks;
            EC2Method method;
            Document doc;

            parameters.put("ExecutableBy.1", ctx.getAccountNumber());
            int filter = 1;
            if( architecture != null ) {
                parameters.put("Filter." + filter + ".Name", "architecture");
                parameters.put("Filter." + (filter++) + ".Value.1", architecture.equals(Architecture.I32) ? "i386" : "x86_64");
            }
            if( platform != null && platform.equals(Platform.WINDOWS) ) {
                parameters.put("Filter." + filter + ".Name", "platform");
                parameters.put("Filter." + (filter++) + ".Value.1", "windows");
            }
            String t = "machine";

            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);
            parameters.put("Filter." + filter + ".Name", "state");
            parameters.put("Filter." + filter + ".Value.1", "available");
            method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                NodeList instances = blocks.item(i).getChildNodes();

                for( int j=0; j<instances.getLength(); j++ ) {
                    Node instance = instances.item(j);

                    if( instance.getNodeName().equals("item") ) {
                        MachineImage image = toMachineImage(instance);

                        if( image != null ) {
                            if( matches(image, keyword, platform, architecture, cls) && ownerId == null || ownerId.equals(image.getProviderOwnerId()) ) {
                                list.add(image);
                            }
                        }
                    }
                }
            }
            return list;
        }
        finally {
            APITrace.end();
        }
    }

    private @Nonnull Iterable<MachineImage> executePublicImageSearch(@Nullable String keyword, @Nullable Platform platform, @Nullable Architecture architecture, @Nonnull ImageClass cls) throws CloudException, InternalException {
        APITrace.begin(provider, "executePublicImageSearch");
        try {
            Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.DESCRIBE_IMAGES);
            ArrayList<MachineImage> list = new ArrayList<MachineImage>();
            NodeList blocks;
            EC2Method method;
            Document doc;

            parameters.put("ExecutableBy.1", "all");
            int filter = 1;
            if( architecture != null ) {
                parameters.put("Filter." + filter + ".Name", "architecture");
                parameters.put("Filter." + (filter++) + ".Value.1", architecture.equals(Architecture.I32) ? "i386" : "x86_64");
            }
            if( platform != null && platform.equals(Platform.WINDOWS) ) {
                parameters.put("Filter." + filter + ".Name", "platform");
                parameters.put("Filter." + (filter++) + ".Value.1", "windows");
            }
            String t = "machine";

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

            parameters.put("Filter." + filter + ".Name", "state");
            parameters.put("Filter." + filter + ".Value.1", "available");
            method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                NodeList instances = blocks.item(i).getChildNodes();

                for( int j=0; j<instances.getLength(); j++ ) {
                    Node instance = instances.item(j);

                    if( instance.getNodeName().equals("item") ) {
                        MachineImage image = toMachineImage(instance);

                        if( image != null ) {
                            if( matches(image, keyword, platform, architecture, cls) ) {
                                list.add(image);
                            }
                        }
                    }
                }
            }
            return list;
        }
        finally {
            APITrace.end();
        }
    }

    @Override
    public @Nullable MachineImage getImage(@Nonnull String providerImageId) throws CloudException, InternalException {
        APITrace.begin(provider, "getImage");
        try {
            ProviderContext ctx = provider.getContext();

            if( ctx == null ) {
                throw new CloudException("No context was set for this request");
            }
            if( provider.getEC2Provider().isAWS() ) {
                Map<String,String> parameters = provider.getStandardParameters(ctx, EC2Method.DESCRIBE_IMAGES);
                NodeList blocks;
                EC2Method method;
                Document doc;

                parameters.put("ImageId", providerImageId);
                method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                    NodeList instances = blocks.item(i).getChildNodes();

                    for( int j=0; j<instances.getLength(); j++ ) {
                        Node instance = instances.item(j);

                        if( instance.getNodeName().equals("item") ) {
                            MachineImage image = toMachineImage(instance);

                            if( image != null && image.getProviderMachineImageId().equals(providerImageId) ) {
                                return image;
                            }
                        }
                    }
                }
                return null;
            }
            else {
                for( MachineImage image : listImages(ImageClass.MACHINE) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                for( MachineImage image : listImages(ImageClass.KERNEL) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                for( MachineImage image : listImages(ImageClass.RAMDISK) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                return null;
            }
        }
        finally {
            APITrace.end();
        }
    }

    @Override
    public @Nullable MachineImage getMachineImage(@Nonnull String imageId) throws InternalException, CloudException {
        return getImage(imageId);
    }
	
	@Override
	public @Nonnull String getProviderTermForImage(@Nonnull Locale locale) {
        return getProviderTermForImage(locale, ImageClass.MACHINE);
    }

    @Override
    public @Nonnull String getProviderTermForImage(@Nonnull Locale locale, @Nonnull ImageClass cls) {
        switch( cls ) {
            case MACHINE: return "AMI";
            case KERNEL: return "AKI";
            case RAMDISK: return "ARI";
        }<commit_msg>Removed method that was made final in super-class<commit_after>/**
 * Copyright (C) 2009-2012 enStratus Networks Inc
 *
 * ====================================================================
 * 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 java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.log4j.Logger;
import org.dasein.cloud.AsynchronousTask;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.OperationNotSupportedException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.Requirement;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.Tag;
import org.dasein.cloud.aws.AWSCloud;
import org.dasein.cloud.aws.storage.S3Method;
import org.dasein.cloud.compute.AbstractImageSupport;
import org.dasein.cloud.compute.Architecture;
import org.dasein.cloud.compute.ImageClass;
import org.dasein.cloud.compute.ImageCreateOptions;
import org.dasein.cloud.compute.ImageFilterOptions;
import org.dasein.cloud.compute.MachineImage;
import org.dasein.cloud.compute.MachineImageFormat;
import org.dasein.cloud.compute.MachineImageState;
import org.dasein.cloud.compute.MachineImageSupport;
import org.dasein.cloud.compute.MachineImageType;
import org.dasein.cloud.compute.Platform;
import org.dasein.cloud.compute.VirtualMachine;
import org.dasein.cloud.compute.VmState;
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;

/**
 * @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 AWSCloud provider = null;
	
	AMI(AWSCloud provider) {
		super(provider);
        this.provider = provider;
	}

    @Override
    public void addImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber) throws CloudException, InternalException {
        APITrace.begin(provider, "addImageShare");
        try {
            setPrivateShare(providerImageId, true, accountNumber);
        }
        finally {
            APITrace.end();
        }
    }

    @Override
    public void addPublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
        APITrace.begin(provider, "addPublicShare");
        try {
            setPublicShare(providerImageId, true);
        }
        finally {
            APITrace.end();
        }
    }

    private @Nonnull MachineImage captureImage(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        APITrace.begin(provider, "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 = provider.getComputeServices().getVirtualMachineSupport().getVirtualMachine(options.getVirtualMachineId());
                    if( vm == null || VmState.TERMINATED.equals(vm.getCurrentState()) ) {
                        break;
                    }
                    if( !vm.isPersistent() ) {
                        throw new OperationNotSupportedException("You cannot capture instance-backed virtual machines");
                    }
                    if( VmState.RUNNING.equals(vm.getCurrentState()) || VmState.STOPPED.equals(vm.getCurrentState()) ) {
                        break;
                    }
                }
                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 ) {
                if( vm.getPlatform().isWindows() ) {
                    String bucket = provider.getStorageServices().getBlobStoreSupport().createBucket("dsnwin" + (System.currentTimeMillis() % 10000), true).getBucketName();

                    if( bucket == null ) {
                        throw new CloudException("There is no bucket");
                    }
                    return captureWindows(ctx, options, bucket, task);
                }
                else {
                    Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.CREATE_IMAGE);
                    NodeList blocks;
                    EC2Method method;
                    Document doc;

                    parameters.put("InstanceId", options.getVirtualMachineId());
                    parameters.put("Name", options.getName());
                    parameters.put("Description", options.getDescription());
                    method = new EC2Method(provider, provider.getEc2Url(), 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);
                            }
                        }
                        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();
        }
    }

    @Override
    protected MachineImage capture(@Nonnull ImageCreateOptions options, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        ProviderContext ctx = provider.getContext();

        if( ctx == null ) {
            throw new CloudException("No context was set for this request");
        }
        return captureImage(ctx, options, task);
    }

    private MachineImage captureWindows(@Nonnull ProviderContext ctx, @Nonnull ImageCreateOptions options, @Nonnull String bucket, @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        APITrace.begin(provider, "captureWindows");
        try {
            Map<String,String> parameters = provider.getStandardParameters(provider.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", provider.signUploadPolicy(base64Policy));
            method = new EC2Method(provider, provider.getEc2Url(), 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<MachineImage>();
                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();
        }
    }

    private @Nonnull Iterable<MachineImage> executeImageSearch(@Nullable String ownerId, @Nullable String keyword, @Nullable Platform platform, @Nullable Architecture architecture, @Nonnull ImageClass cls) throws CloudException, InternalException {
        APITrace.begin(provider, "executeImageSearch");
        try {
            ProviderContext ctx = provider.getContext();

            if( ctx == null ) {
                throw new CloudException("No context was set for this request");
            }
            Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.DESCRIBE_IMAGES);
            ArrayList<MachineImage> list = new ArrayList<MachineImage>();
            NodeList blocks;
            EC2Method method;
            Document doc;

            parameters.put("ExecutableBy.1", ctx.getAccountNumber());
            int filter = 1;
            if( architecture != null ) {
                parameters.put("Filter." + filter + ".Name", "architecture");
                parameters.put("Filter." + (filter++) + ".Value.1", architecture.equals(Architecture.I32) ? "i386" : "x86_64");
            }
            if( platform != null && platform.equals(Platform.WINDOWS) ) {
                parameters.put("Filter." + filter + ".Name", "platform");
                parameters.put("Filter." + (filter++) + ".Value.1", "windows");
            }
            String t = "machine";

            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);
            parameters.put("Filter." + filter + ".Name", "state");
            parameters.put("Filter." + filter + ".Value.1", "available");
            method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                NodeList instances = blocks.item(i).getChildNodes();

                for( int j=0; j<instances.getLength(); j++ ) {
                    Node instance = instances.item(j);

                    if( instance.getNodeName().equals("item") ) {
                        MachineImage image = toMachineImage(instance);

                        if( image != null ) {
                            if( matches(image, keyword, platform, architecture, cls) && ownerId == null || ownerId.equals(image.getProviderOwnerId()) ) {
                                list.add(image);
                            }
                        }
                    }
                }
            }
            return list;
        }
        finally {
            APITrace.end();
        }
    }

    private @Nonnull Iterable<MachineImage> executePublicImageSearch(@Nullable String keyword, @Nullable Platform platform, @Nullable Architecture architecture, @Nonnull ImageClass cls) throws CloudException, InternalException {
        APITrace.begin(provider, "executePublicImageSearch");
        try {
            Map<String,String> parameters = provider.getStandardParameters(provider.getContext(), EC2Method.DESCRIBE_IMAGES);
            ArrayList<MachineImage> list = new ArrayList<MachineImage>();
            NodeList blocks;
            EC2Method method;
            Document doc;

            parameters.put("ExecutableBy.1", "all");
            int filter = 1;
            if( architecture != null ) {
                parameters.put("Filter." + filter + ".Name", "architecture");
                parameters.put("Filter." + (filter++) + ".Value.1", architecture.equals(Architecture.I32) ? "i386" : "x86_64");
            }
            if( platform != null && platform.equals(Platform.WINDOWS) ) {
                parameters.put("Filter." + filter + ".Name", "platform");
                parameters.put("Filter." + (filter++) + ".Value.1", "windows");
            }
            String t = "machine";

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

            parameters.put("Filter." + filter + ".Name", "state");
            parameters.put("Filter." + filter + ".Value.1", "available");
            method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                NodeList instances = blocks.item(i).getChildNodes();

                for( int j=0; j<instances.getLength(); j++ ) {
                    Node instance = instances.item(j);

                    if( instance.getNodeName().equals("item") ) {
                        MachineImage image = toMachineImage(instance);

                        if( image != null ) {
                            if( matches(image, keyword, platform, architecture, cls) ) {
                                list.add(image);
                            }
                        }
                    }
                }
            }
            return list;
        }
        finally {
            APITrace.end();
        }
    }

    @Override
    public @Nullable MachineImage getImage(@Nonnull String providerImageId) throws CloudException, InternalException {
        APITrace.begin(provider, "getImage");
        try {
            ProviderContext ctx = provider.getContext();

            if( ctx == null ) {
                throw new CloudException("No context was set for this request");
            }
            if( provider.getEC2Provider().isAWS() ) {
                Map<String,String> parameters = provider.getStandardParameters(ctx, EC2Method.DESCRIBE_IMAGES);
                NodeList blocks;
                EC2Method method;
                Document doc;

                parameters.put("ImageId", providerImageId);
                method = new EC2Method(provider, provider.getEc2Url(), 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<blocks.getLength(); i++ ) {
                    NodeList instances = blocks.item(i).getChildNodes();

                    for( int j=0; j<instances.getLength(); j++ ) {
                        Node instance = instances.item(j);

                        if( instance.getNodeName().equals("item") ) {
                            MachineImage image = toMachineImage(instance);

                            if( image != null && image.getProviderMachineImageId().equals(providerImageId) ) {
                                return image;
                            }
                        }
                    }
                }
                return null;
            }
            else {
                for( MachineImage image : listImages(ImageClass.MACHINE) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                for( MachineImage image : listImages(ImageClass.KERNEL) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                for( MachineImage image : listImages(ImageClass.RAMDISK) ) {
                    if( image.getProviderMachineImageId().equals(providerImageId) ) {
                        return image;
                    }
                }
                return null;
            }
        }
        finally {
            APITrace.end();
        }
    }
	
	@Override
	public @Nonnull String getProviderTermForImage(@Nonnull Locale locale) {
        return getProviderTermForImage(locale, ImageClass.MACHINE);
    }

    @Override
    public @Nonnull String getProviderTermForImage(@Nonnull Locale locale, @Nonnull ImageClass cls) {
        switch( cls ) {
            case MACHINE: return "AMI";
            case KERNEL: return "AKI";
            case RAMDISK: return "ARI";
        }
{'fraction_non_alphanumeric': 0.11148277023234932, 'fraction_numerical': 0.00592659108766416, 'mean_word_length': 1.652494938668572, 'pattern_counts': {'":': 8, '<': 49, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5930874', 'n_tokens_mistral': 11052, 'n_tokens_neox': 10473, 'n_words': 2347}