org.dasein.cloud.rackspace.compute.CloudServerImages.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.rackspace.compute.CloudServerImages.java

Source

/**
 * 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.rackspace.compute;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.dasein.cloud.AsynchronousTask;
import org.dasein.cloud.CloudErrorType;
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.compute.Architecture;
import org.dasein.cloud.compute.ImageClass;
import org.dasein.cloud.compute.ImageCreateOptions;
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.identity.ServiceAction;
import org.dasein.cloud.rackspace.RackspaceCloud;
import org.dasein.cloud.rackspace.RackspaceException;
import org.dasein.cloud.rackspace.RackspaceMethod;
import org.dasein.util.CalendarWrapper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class CloudServerImages implements MachineImageSupport {
    private RackspaceCloud provider;

    CloudServerImages(RackspaceCloud provider) {
        this.provider = provider;
    }

    @Override
    public void addImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Override
    public void addPublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Nonnull
    @Override
    public String bundleVirtualMachine(@Nonnull String virtualMachineId, @Nonnull MachineImageFormat format,
            @Nonnull String bucket, @Nonnull String name) throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Override
    public void bundleVirtualMachineAsync(@Nonnull String virtualMachineId, @Nonnull MachineImageFormat format,
            @Nonnull String bucket, @Nonnull String name, @Nonnull AsynchronousTask<String> trackingTask)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    private @Nonnull MachineImage capture(@Nonnull ImageCreateOptions options,
            @Nullable AsynchronousTask<MachineImage> task) throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudServerImages.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudServerImages.class.getName() + ".capture(" + options + "," + task + ")");
        }
        try {
            ProviderContext ctx = provider.getContext();

            if (ctx == null) {
                throw new CloudException("No context is established for this request");
            }
            if (!provider.isMyRegion()) {
                throw new CloudException("You are not allowed to image in " + ctx.getRegionId());
            }
            HashMap<String, Object> wrapper = new HashMap<String, Object>();
            HashMap<String, Object> json = new HashMap<String, Object>();

            json.put("name", options.getName());
            json.put("serverId", Long.parseLong(options.getVirtualMachineId()));
            wrapper.put("image", json);

            if (task != null) {
                task.setStartTime(System.currentTimeMillis());
            }
            RackspaceMethod method = new RackspaceMethod(provider);
            JSONObject result = method.postServers("/images", null, new JSONObject(wrapper));

            if (result.has("image")) {
                try {
                    JSONObject img = result.getJSONObject("image");
                    MachineImage image = toImage(img);

                    if (image != null) {
                        if (task != null) {
                            task.completeWithResult(image);
                        }
                        return image;
                    }
                } catch (JSONException e) {
                    logger.error("imageVirtualMachine(): Unable to understand image response: " + e.getMessage());
                    if (logger.isTraceEnabled()) {
                        e.printStackTrace();
                    }
                    throw new CloudException(e);
                }
            }
            logger.error(
                    "imageVirtualMachine(): No image was created by the imaging attempt, and no error was returned");
            throw new CloudException("No image was created");

        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudServerImages.class.getName() + ".capture()");
            }
        }
    }

    @Nonnull
    @Override
    public MachineImage captureImage(@Nonnull ImageCreateOptions options) throws CloudException, InternalException {
        return capture(options, null);
    }

    @Override
    public void captureImageAsync(final @Nonnull ImageCreateOptions options,
            final @Nonnull AsynchronousTask<MachineImage> taskTracker) throws CloudException, InternalException {
        Thread t = new Thread() {
            public void run() {
                try {
                    capture(options, taskTracker);
                } catch (Throwable t) {
                    taskTracker.complete(t);
                } finally {
                    provider.release();
                }
            }
        };
        provider.hold();

        t.setName("Image Capture: " + options.getVirtualMachineId());
        t.setDaemon(true);
        t.start();
    }

    @Override
    public MachineImage getImage(@Nonnull String providerImageId) throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudServerImages.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace(
                    "enter - " + CloudServerImages.class.getName() + ".getMachineImage(" + providerImageId + ")");
        }
        try {
            if (!provider.isMyRegion()) {
                return null;
            }
            RackspaceMethod method = new RackspaceMethod(provider);
            JSONObject ob = method.getServers("/images", providerImageId);

            if (ob == null) {
                return null;
            }
            try {
                if (ob.has("image")) {
                    JSONObject server = ob.getJSONObject("image");
                    MachineImage img = toImage(server);

                    if (img != null) {
                        return img;
                    }
                }
            } catch (JSONException e) {
                logger.error("getMachineImage(): Unable to identify expected values in JSON: " + e.getMessage());
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson",
                        "Missing JSON element for images: " + e.getMessage());
            }
            return null;
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudServerImages.class.getName() + ".getMachineImage()");
            }
        }
    }

    @Override
    @Deprecated
    public @Nullable MachineImage getMachineImage(@Nonnull String machineImageId)
            throws CloudException, InternalException {
        return getImage(machineImageId);
    }

    @Override
    public @Nonnull String getProviderTermForImage(@Nonnull Locale locale) {
        return "image";
    }

    @Nonnull
    @Override
    public String getProviderTermForImage(@Nonnull Locale locale, @Nonnull ImageClass cls) {
        return "image";
    }

    @Nonnull
    @Override
    public String getProviderTermForCustomImage(@Nonnull Locale locale, @Nonnull ImageClass cls) {
        return "image";
    }

    @Override
    public boolean hasPublicLibrary() {
        return false;
    }

    @Nonnull
    @Override
    public Requirement identifyLocalBundlingRequirement() throws CloudException, InternalException {
        return Requirement.NONE;
    }

    @Override
    public @Nonnull AsynchronousTask<String> imageVirtualMachine(@Nonnull String vmId, @Nonnull String name,
            @Nonnull String description) throws CloudException, InternalException {
        VirtualMachine vm = provider.getComputeServices().getVirtualMachineSupport().getVirtualMachine(vmId);

        if (vm == null) {
            throw new CloudException("No such virtual machine: " + vmId);
        }
        final ImageCreateOptions options = ImageCreateOptions.getInstance(vm, name, description);
        final AsynchronousTask<String> task = new AsynchronousTask<String>();

        Thread t = new Thread() {
            public void run() {
                try {
                    task.completeWithResult(capture(options, null).getProviderMachineImageId());
                } catch (Throwable t) {
                    task.complete(t);
                } finally {
                    provider.release();
                }
            }
        };
        provider.hold();

        t.setName("Image Capture: " + options.getVirtualMachineId());
        t.setDaemon(true);
        t.start();

        return task;
    }

    @Override
    public boolean isImageSharedWithPublic(@Nonnull String machineImageId)
            throws CloudException, InternalException {
        return false;
    }

    @Override
    public boolean isSubscribed() throws CloudException, InternalException {
        return (provider.isMyRegion() && provider.testContext() != null);
    }

    @Nonnull
    @Override
    public Iterable<ResourceStatus> listImageStatus(@Nonnull ImageClass cls)
            throws CloudException, InternalException {
        ArrayList<ResourceStatus> status = new ArrayList<ResourceStatus>();

        for (MachineImage img : listImages(cls)) {
            status.add(new ResourceStatus(img.getProviderMachineImageId(), img.getCurrentState()));
        }
        return status;
    }

    @Nonnull
    @Override
    public Iterable<MachineImage> listImages(@Nonnull ImageClass cls) throws CloudException, InternalException {
        if (!cls.equals(ImageClass.MACHINE)) {
            return Collections.emptyList();
        }
        Logger logger = RackspaceCloud.getLogger(CloudServerImages.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudServerImages.class.getName() + ".listMachineImages()");
        }
        try {
            if (!provider.isMyRegion()) {
                return Collections.emptyList();
            }
            RackspaceMethod method = new RackspaceMethod(provider);
            JSONObject ob = method.getServers("/images", null);
            ArrayList<MachineImage> images = new ArrayList<MachineImage>();

            try {
                if (ob.has("images")) {
                    JSONArray list = ob.getJSONArray("images");

                    for (int i = 0; i < list.length(); i++) {
                        JSONObject image = list.getJSONObject(i);
                        MachineImage img = toImage(image);

                        if (img != null) {
                            images.add(img);
                        }

                    }
                }
            } catch (JSONException e) {
                logger.error("listMachineImages(): Unable to identify expected values in JSON: " + e.getMessage());
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson",
                        "Missing JSON element for images: " + e.getMessage());
            }
            return images;
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudServerImages.class.getName() + ".listMachineImages()");
            }
        }
    }

    @Nonnull
    @Override
    public Iterable<MachineImage> listImages(@Nonnull ImageClass cls, @Nonnull String ownedBy)
            throws CloudException, InternalException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context");
        }
        if (!ownedBy.equals(ctx.getAccountNumber())) {
            return Collections.emptyList();
        }
        return listImages(cls);
    }

    @Override
    public @Nonnull Iterable<MachineImage> listMachineImages() throws CloudException, InternalException {
        return listImages(ImageClass.MACHINE);
    }

    @Override
    public @Nonnull Iterable<MachineImage> listMachineImagesOwnedBy(String accountId)
            throws CloudException, InternalException {
        return listMachineImages();
    }

    @Override
    public @Nonnull Iterable<MachineImageFormat> listSupportedFormats() throws CloudException, InternalException {
        return Collections.emptyList();
    }

    @Nonnull
    @Override
    public Iterable<MachineImageFormat> listSupportedFormatsForBundling() throws CloudException, InternalException {
        return Collections.emptyList();
    }

    @Override
    public @Nonnull Iterable<String> listShares(@Nonnull String forMachineImageId)
            throws CloudException, InternalException {
        return Collections.emptyList();
    }

    @Nonnull
    @Override
    public Iterable<ImageClass> listSupportedImageClasses() throws CloudException, InternalException {
        return Collections.singletonList(ImageClass.MACHINE);
    }

    @Nonnull
    @Override
    public Iterable<MachineImageType> listSupportedImageTypes() throws CloudException, InternalException {
        return Collections.singleton(MachineImageType.VOLUME);
    }

    @Nonnull
    @Override
    public MachineImage registerImageBundle(@Nonnull ImageCreateOptions options)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Override
    public @Nonnull String[] mapServiceAction(@Nonnull ServiceAction action) {
        return new String[0];
    }

    @Override
    public void remove(@Nonnull String machineImageId) throws CloudException, InternalException {
        remove(machineImageId, false);
    }

    @Override
    public void remove(@Nonnull String providerImageId, boolean checkState)
            throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudServerImages.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudServerImages.class.getName() + ".remove(" + providerImageId + ")");
        }
        try {
            RackspaceMethod method = new RackspaceMethod(provider);
            long timeout = System.currentTimeMillis() + CalendarWrapper.HOUR;

            do {
                try {
                    method.deleteServers("/images", providerImageId);
                    return;
                } catch (RackspaceException e) {
                    if (e.getHttpCode() != HttpServletResponse.SC_CONFLICT) {
                        throw e;
                    }
                }
                try {
                    Thread.sleep(CalendarWrapper.MINUTE);
                } catch (InterruptedException e) {
                    /* ignore */ }
            } while (System.currentTimeMillis() < timeout);
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudServerImages.class.getName() + ".remove()");
            }
        }
    }

    @Override
    public void removeAllImageShares(@Nonnull String providerImageId) throws CloudException, InternalException {
        // NO-OP
    }

    @Override
    public void removeImageShare(@Nonnull String providerImageId, @Nonnull String accountNumber)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Override
    public void removePublicShare(@Nonnull String providerImageId) throws CloudException, InternalException {
        throw new OperationNotSupportedException("Not supported");
    }

    @Override
    public @Nonnull Iterable<MachineImage> searchMachineImages(@Nullable String keyword,
            @Nullable Platform platform, @Nullable Architecture architecture)
            throws CloudException, InternalException {
        return searchImages(null, keyword, platform, architecture, ImageClass.MACHINE);
    }

    @Nonnull
    @Override
    public Iterable<MachineImage> searchImages(@Nullable String accountNumber, @Nullable String keyword,
            @Nullable Platform platform, @Nullable Architecture architecture, @Nullable ImageClass... imageClasses)
            throws CloudException, InternalException {
        if (!provider.isMyRegion()) {
            return Collections.emptyList();
        }
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context");
        }
        if (accountNumber != null && !accountNumber.equals(ctx.getAccountNumber())) {
            return Collections.emptyList();
        }
        ArrayList<MachineImage> images = new ArrayList<MachineImage>();

        for (MachineImage img : listMachineImages()) {
            if (architecture != null) {
                if (!architecture.equals(img.getArchitecture())) {
                    continue;
                }
            }
            if (platform != null && !platform.equals(Platform.UNKNOWN)) {
                Platform p = img.getPlatform();

                if (p.equals(Platform.UNKNOWN)) {
                    continue;
                } else if (platform.isWindows()) {
                    if (!p.isWindows()) {
                        continue;
                    }
                } else if (platform.equals(Platform.UNIX)) {
                    if (!p.isUnix()) {
                        continue;
                    }
                } else if (!platform.equals(p)) {
                    continue;
                }
            }
            if (keyword != null) {
                if (!img.getName().contains(keyword)) {
                    if (!img.getDescription().contains(keyword)) {
                        if (!img.getProviderMachineImageId().contains(keyword)) {
                            continue;
                        }
                    }
                }
            }
            images.add(img);
        }
        return images;
    }

    @Nonnull
    @Override
    public Iterable<MachineImage> searchPublicImages(@Nullable String keyword, @Nullable Platform platform,
            @Nullable Architecture architecture, @Nullable ImageClass... imageClasses)
            throws CloudException, InternalException {
        return Collections.emptyList();
    }

    @Override
    public void shareMachineImage(@Nonnull String machineImageId, @Nullable String withAccountId, boolean allow)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("Rackspace does not support image sharing");
    }

    @Override
    public boolean supportsCustomImages() {
        return true;
    }

    @Override
    public boolean supportsDirectImageUpload() throws CloudException, InternalException {
        return false;
    }

    @Override
    public boolean supportsImageCapture(@Nonnull MachineImageType type) throws CloudException, InternalException {
        return type.equals(MachineImageType.VOLUME);
    }

    @Override
    public boolean supportsImageSharing() {
        return false;
    }

    @Override
    public boolean supportsImageSharingWithPublic() {
        return false;
    }

    @Override
    public boolean supportsPublicLibrary(@Nonnull ImageClass cls) throws CloudException, InternalException {
        return false;
    }

    @Override
    public void updateTags(@Nonnull String imageId, @Nonnull Tag... tags) throws CloudException, InternalException {
        // NO-OP
    }

    public @Nullable MachineImage toImage(@Nullable JSONObject json) throws JSONException {
        Logger logger = RackspaceCloud.getLogger(CloudServerImages.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudServerImages.class.getName() + ".toImage(" + json + ")");
        }
        try {
            if (json == null) {
                return null;
            }
            MachineImage image = new MachineImage();

            image.setArchitecture(Architecture.I64);
            image.setPlatform(Platform.UNKNOWN);
            image.setProviderOwnerId(provider.getContext().getAccountNumber());
            image.setProviderRegionId(provider.getContext().getRegionId());
            image.setTags(new HashMap<String, String>());
            image.setType(MachineImageType.VOLUME);
            image.setImageClass(ImageClass.MACHINE);
            image.setSoftware("");
            if (json.has("id")) {
                image.setProviderMachineImageId(json.getString("id"));
            }
            if (json.has("name")) {
                image.setName(json.getString("name"));
            }
            if (json.has("description")) {
                image.setDescription(json.getString("description"));
            }
            if (json.has("status")) {
                String s = json.getString("status").toLowerCase();

                if (s.equals("saving")) {
                    image.setCurrentState(MachineImageState.PENDING);
                } else if (s.equals("active") || s.equals("queued") || s.equals("preparing")) {
                    image.setCurrentState(MachineImageState.ACTIVE);
                } else if (s.equals("deleting")) {
                    image.setCurrentState(MachineImageState.PENDING);
                } else if (s.equals("failed")) {
                    return null;
                } else {
                    logger.warn("toImage(): Unknown image status: " + s);
                    image.setCurrentState(MachineImageState.PENDING);
                }
            }
            if (image.getProviderMachineImageId() == null) {
                return null;
            }
            if (image.getName() == null) {
                image.setName(image.getProviderMachineImageId());
            }
            if (image.getDescription() == null) {
                image.setDescription(image.getName());
            }
            image.setPlatform(Platform.guess(image.getName() + " " + image.getDescription()));
            return image;
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudServerImages.class.getName() + ".toImage()");
            }
        }
    }
}