org.dasein.cloud.terremark.Terremark.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.terremark.Terremark.java

Source

/**
 * Copyright (C) 2009-2013 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.terremark;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.NameValuePair;
import org.apache.log4j.Logger;
import org.dasein.cloud.AbstractCloud;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.dc.Region;
import org.dasein.cloud.terremark.TerremarkMethod.HttpMethodName;
import org.dasein.cloud.terremark.compute.Template;
import org.dasein.cloud.terremark.compute.TerremarkComputeServices;
import org.dasein.cloud.terremark.identity.TerremarkIdentityServices;
import org.dasein.cloud.terremark.network.FirewallRule;
import org.dasein.cloud.terremark.network.TerremarkNetworkServices;
import org.dasein.cloud.terremark.network.TerremarkNetworkSupport;
import org.dasein.util.CalendarWrapper;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Terremark extends AbstractCloud {
    public class Task {
        public String taskId;
        public String errorMessage;
        public String status;
    }

    static private final Logger logger = Logger.getLogger(Terremark.class);

    private transient volatile TerremarkProvider provider;

    //Required Headers
    static public final String AUTHORIZATION = "Authorization";

    static public final String DATE = "Date";

    static public final String TMRK_AUTHORIZATION = "x-tmrk-authorization";

    static public final String TMRK_DATE = "x-tmrk-date";

    static public final String TMRK_VERSION = "x-tmrk-version";

    //Conditional Headers
    static public final String ACCEPT = "Accept";

    static public final String CONTENT_LENGTH = "Content-Length";

    static public final String CONTENT_LOCATION = "Content-Location";

    static public final String CONTENT_RANGE = "Content-Range";

    static public final String CONTENT_TYPE = "Content-Type";

    static public final String LOCATION = "Location";
    static public final String GUEST_PASSWORD = "X-Guest-Password";
    static public final String GUEST_USER = "X-Guest-User";
    static public final String RESPONDING_HOST = "X-Responding-Host";
    static public final String TMRK_CONTENTHASH = "x-tmrk-contenthash";

    static public final String TMRK_TOKEN = "x-tmrk-token";
    //Response-Only Headers
    static public final String TMRK_CURRENTUSER = "x-tmrk-currentuser";
    static public final String TMRK_DEPRECATED = "x-tmrk-deprecated";
    // Header Values
    static public final String VERSION = "2012-09-01";
    static public final String ALGORITHM = "HmacSha512";
    public final static String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
    public final static String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
    public final static String ISO8601_NO_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public final static String TMRK_URI = "https://services.enterprisecloud.terremark.com";
    public final static String URI_PATH = "/cloudapi/ecloud";
    public final static String LIVE_SPEC_URI_PATH = "/cloudapi/spec";
    public final static String DEFAULT_URI_PATH = URI_PATH;

    public final static String JSON = "application/json";
    public final static String XML = "application/xml";

    // API Calls
    public final static String ORGANZIATIONS = "organizations";
    public final static String ACTION = "action";
    public final static String ADMIN = "admin";
    // Common Response Attributes
    public final static String NAME = "name";
    public final static String HREF = "href";
    public final static String TYPE = "type";
    public final static String ACCESSIBLE = "accessible";
    // Task Tags & Status
    public final static String TASK_TAG = "Task";
    public final static String OPERATION_TAG = "Operation";
    public final static String STATUS_TAG = "Status";
    public final static String ERROR_MESSAGE_TAG = "ErrorMessage";

    public final static String TASK_COMPLETE = "Complete";
    public final static String TASK_QUEUED = "Queued";
    public final static String TASK_RUNNING = "Running";

    public final static String TASK_ERROR = "Error";
    public final static int TASK_ERROR_COUNT = 5;

    static private String getLastItem(String name) {
        int idx = name.lastIndexOf('.');

        if (idx < 0) {
            return name;
        } else if (idx == (name.length() - 1)) {
            return "";
        }
        return name.substring(idx + 1);
    }

    static public Logger getLogger(Class<?> cls) {
        String pkg = getLastItem(cls.getPackage().getName());

        if (pkg.equals("terremark")) {
            pkg = "";
        } else {
            pkg = pkg + ".";
        }
        return Logger.getLogger("dasein.cloud.terremark.std." + pkg + getLastItem(cls.getName()));
    }

    private static String getRfcDate() {
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat(RFC1123_PATTERN);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        return format.format(date);
    }

    public static String getTaskHref(Document doc, String taskName) {
        String href = null;
        NodeList taskElements = doc.getElementsByTagName(Terremark.TASK_TAG);
        for (int i = 0; i < taskElements.getLength(); i++) {
            Node taskElement = taskElements.item(i);
            NodeList taskChildren = taskElement.getChildNodes();
            for (int j = 0; j < taskChildren.getLength(); j++) {
                Node taskChild = taskChildren.item(j);
                if (taskChild.getNodeName().equals(Terremark.OPERATION_TAG)) {
                    if (taskChild.getTextContent().equals(taskName)) {
                        href = taskElement.getAttributes().getNamedItem(Terremark.HREF).getNodeValue();
                        break;
                    }
                }
            }
            if (href != null) {
                break;
            }
        }
        return href;
    }

    public static String getTemplateIdFromHref(String templateHref) {
        String id = null;
        String templateString = "/" + Template.TEMPLATES + "/";
        String cpLowerString = "/" + EnvironmentsAndComputePools.COMPUTE_POOLS.toLowerCase() + "/";
        String cpString = "/" + EnvironmentsAndComputePools.COMPUTE_POOLS + "/";
        templateHref = templateHref.toLowerCase();
        if (templateHref.contains(templateString) && templateHref.contains(cpLowerString)) {
            int startTemplateId = templateHref.indexOf(templateString) + templateString.length();
            int startCPId = templateHref.indexOf(cpLowerString) + cpLowerString.length();
            id = templateHref.substring(startTemplateId, templateHref.indexOf(cpLowerString)) + ":"
                    + templateHref.substring(startCPId) + ":" + Template.ImageType.TEMPLATE.name();
        } else if (templateHref.contains(templateString) && templateHref.contains(cpString)) {
            int startTemplateId = templateHref.indexOf(templateString) + templateString.length();
            int startCPId = templateHref.indexOf(cpString) + cpString.length();
            id = templateHref.substring(startTemplateId, templateHref.indexOf(cpString)) + ":"
                    + templateHref.substring(startCPId) + ":" + Template.ImageType.TEMPLATE.name();
        } else {
            id = null;
            logger.warn("getTemplateIdFromHref(): Failed to parse template href " + templateHref);
        }
        return id;
    }

    static public Logger getWireLogger(Class<?> cls) {
        return Logger.getLogger("dasein.cloud.terremark.wire." + getLastItem(cls.getPackage().getName()) + "."
                + getLastItem(cls.getName()));
    }

    /**
     * Converts a firewall acls href to the ID format ({custom | nodeServices}/{firewall rule identifier | node service identifier}).
     * @param href A FirewallAcl href
     * @return the firewall rule ID in the format {custom | nodeServices}/{firewall rule identifier | node service identifier}
     */
    public static String hrefToFirewallRuleId(String href) {
        final String uri = FirewallRule.FIREWALL_ACLS + "/";
        final String lowerUri = FirewallRule.FIREWALL_ACLS.toLowerCase() + "/";
        String firewallRuleId = "";
        if (href.contains(lowerUri)) {
            firewallRuleId = href.substring(href.lastIndexOf(lowerUri) + lowerUri.length());
        } else if (href.contains(uri)) {
            firewallRuleId = href.substring(href.lastIndexOf(uri) + uri.length());
        } else {
            firewallRuleId = href;
            logger.warn("hrefToFirewallRuleId(): Failed to parse firewall rule href " + href);
        }
        return firewallRuleId;
    }

    public static String hrefToId(String href) {
        return href.substring(href.lastIndexOf("/") + 1);
    }

    /**
     * Converts a network or IP address href to the ID format (network_id or netowrk_id/ipv6 or network_id/ip_address or network_id/ipv6/ip_address).
     * @param href A network or IP address href
     * @return the network or IP address ID in the format network_id or netowrk_id/ipv6 or network_id/ip_address or network_id/ipv6/ip_address
     */
    public static String hrefToNetworkId(String href) {
        String networkId = null;
        if (href.contains(TerremarkNetworkSupport.NETWORKS)) {
            int beginIndex = href.indexOf(TerremarkNetworkSupport.NETWORKS + "/")
                    + TerremarkNetworkSupport.NETWORKS.length() + 1;
            networkId = href.substring(beginIndex, href.length());
        } else if (href.contains(TerremarkNetworkSupport.NETWORKS.toLowerCase())) {
            int beginIndex = href.indexOf(TerremarkNetworkSupport.NETWORKS.toLowerCase() + "/")
                    + TerremarkNetworkSupport.NETWORKS.length() + 1;
            networkId = href.substring(beginIndex, href.length());
        } else {
            networkId = href;
            logger.warn("hrefToNetworkId(): Failed to parse network href " + href);
        }
        return networkId;
    }

    public static Date parseIsoDate(String isoDateString) {
        java.text.DateFormat df = new SimpleDateFormat(ISO8601_PATTERN);
        df.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date result = null;
        try {
            result = df.parse(isoDateString);
        } catch (ParseException e) {
            df = new SimpleDateFormat(ISO8601_NO_MS_PATTERN);
            df.setTimeZone(TimeZone.getTimeZone("UTC"));
            result = null;
            try {
                result = df.parse(isoDateString);
            } catch (ParseException e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    private static String sign(byte[] key, String authString, String algorithm) throws InternalException {
        try {
            Mac mac = Mac.getInstance(algorithm);

            mac.init(new SecretKeySpec(key, algorithm));
            return new String(Base64.encodeBase64(mac.doFinal(authString.getBytes("utf-8"))));
        } catch (NoSuchAlgorithmException e) {
            logger.error(e);
            e.printStackTrace();
            throw new InternalException(e);
        } catch (InvalidKeyException e) {
            logger.error(e);
            e.printStackTrace();
            throw new InternalException(e);
        } catch (IllegalStateException e) {
            logger.error(e);
            e.printStackTrace();
            throw new InternalException(e);
        } catch (UnsupportedEncodingException e) {
            logger.error(e);
            e.printStackTrace();
            throw new InternalException(e);
        }
    }

    private transient volatile Organization currentOrg;

    public Terremark() {
    }

    @Override
    public String getCloudName() {
        String name = getContext().getCloudName();

        return ((name == null) ? getProviderName() : name);
    }

    @Override
    public @Nonnull TerremarkComputeServices getComputeServices() {
        return new TerremarkComputeServices(this);
    }

    @Override
    public @Nonnull EnvironmentsAndComputePools getDataCenterServices() {
        return new EnvironmentsAndComputePools(this);
    }

    public Map<String, String> getHeaders(ProviderContext ctx, HttpMethodName methodType, String url,
            NameValuePair[] queryParamters, String body) {
        HashMap<String, String> headers = new HashMap<String, String>();

        String accessKey = "";
        try {
            accessKey = new String(ctx.getAccessPublic(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            logger.warn(e.getMessage());
        }
        String signatureType = ALGORITHM;
        String signature = null;
        String contentLength = "";
        String contentType = "";
        String formattedDate = getRfcDate();
        String canonicalizedHeaders = "";
        String canonicalizedResource = "";

        headers.put(TMRK_DATE, formattedDate);
        headers.put(TMRK_VERSION, VERSION);
        headers.put(ACCEPT, XML);
        if (body != null && (methodType.equals(HttpMethodName.PUT) || methodType.equals(HttpMethodName.POST)
                || methodType.equals(HttpMethodName.DELETE))) {
            contentLength = new String("" + body.getBytes().length);
        }
        if (body != null && !body.equals("")) {
            contentType = XML;
            headers.put(CONTENT_TYPE, contentType);
        }

        List<String> canonicalizedHeadersList = new ArrayList<String>();
        HashMap<String, String> tempHeadersLower = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            if (entry.getKey().toLowerCase().startsWith("x-tmrk-") && !entry.getKey().equals(TMRK_AUTHORIZATION))
                canonicalizedHeadersList.add(entry.getKey().toLowerCase().trim());
            tempHeadersLower.put(entry.getKey().toLowerCase().trim(), entry.getValue().trim());
        }
        Collections.sort(canonicalizedHeadersList);
        for (String header : canonicalizedHeadersList) {
            String value = tempHeadersLower.get(header);
            canonicalizedHeaders += header + ":" + value + "\n";
        }

        if (!url.startsWith("/")) {
            url = "/" + url;
        }

        canonicalizedResource += DEFAULT_URI_PATH + url.toLowerCase().trim() + "\n";
        List<String> queryParameterList = new ArrayList<String>();
        HashMap<String, String> tempQueryParamtersLower = new HashMap<String, String>();
        if (queryParamters != null) {
            for (NameValuePair parameter : queryParamters) {
                queryParameterList.add(parameter.getName().toLowerCase().trim());
                tempQueryParamtersLower.put(parameter.getName().toLowerCase().trim(), parameter.getValue().trim());
            }
            Collections.sort(queryParameterList);
        }

        for (String paramter : queryParameterList) {
            String value = tempQueryParamtersLower.get(paramter);
            canonicalizedResource += paramter + ":" + value + "\n";
        }

        // use String constructor for UTF-8 encoding
        String stringToSign = new String(methodType + "\n" + contentLength + "\n" + contentType + "\n"
                + formattedDate + "\n" + canonicalizedHeaders + canonicalizedResource);
        try {
            signature = sign(ctx.getAccessPrivate(), stringToSign, ALGORITHM);
        } catch (InternalException e) {
            logger.warn(e.getMessage());
            e.printStackTrace();
        }

        String autorizationString = "CloudApi AccessKey=\"" + accessKey + "\" SignatureType=\"" + signatureType
                + "\" Signature=\"" + signature + "\"";
        headers.put(TMRK_AUTHORIZATION, autorizationString);

        return headers;
    }

    @Override
    public TerremarkIdentityServices getIdentityServices() {
        return new TerremarkIdentityServices(this);
    }

    @Override
    public @Nonnull TerremarkNetworkServices getNetworkServices() {
        return new TerremarkNetworkServices(this);
    }

    public Organization getOrganization() throws CloudException, InternalException {
        if (currentOrg == null) {
            String url = "/" + ORGANZIATIONS + "/";
            TerremarkMethod method = new TerremarkMethod(this, HttpMethodName.GET, url, null, null);
            Document doc = method.invoke();
            if (doc != null) {
                currentOrg = toOrg(doc);
            }
        }
        return currentOrg;
    }

    @Override
    public String getProviderName() {
        ProviderContext ctx = getContext();
        String name = (ctx == null ? null : ctx.getProviderName());

        return ((name == null) ? TerremarkProvider.ENTERPRISE_CLOUD.getName() : name);
    }

    public String getProxyHost() {
        return getContext().getCustomProperties().getProperty("proxyHost");
    }

    public int getProxyPort() {
        String port = getContext().getCustomProperties().getProperty("proxyPort");

        if (port != null) {
            return Integer.parseInt(port);
        }
        return -1;
    }

    public String getTaskStatus(String taskHref) throws CloudException, InternalException {
        String status = null;
        TerremarkMethod method = new TerremarkMethod(this, HttpMethodName.GET, taskHref, null, null);
        Document doc = method.invoke();
        if (doc != null) {
            status = doc.getElementsByTagName(STATUS_TAG).item(0).getTextContent();
        }
        return status;
    }

    public @Nonnull TerremarkProvider getTerremarkProvider() {
        if (provider == null) {
            provider = TerremarkProvider.valueOf(getProviderName());
        }
        return provider;
    }

    @Override
    public @Nullable String testContext() {
        try {
            ProviderContext ctx = getContext();

            if (ctx == null) {
                System.out.println("context is null");
                return null;
            }
            if (ctx.getRegionId() == null) {
                Collection<Region> regions = getDataCenterServices().listRegions();
                if (regions.size() > 0) {
                    ctx.setRegionId(regions.iterator().next().getProviderRegionId());
                }
            }

            if (!getComputeServices().getVirtualMachineSupport().isSubscribed()) {
                System.out.println("isSubscribed is false");
                return null;
            }
            return ctx.getAccountNumber();
        } catch (Throwable t) {
            logger.warn("Failed to test Terremark connection context: " + t.getMessage());
            if (logger.isDebugEnabled()) {
                t.printStackTrace();
            }
            return null;
        }
    }

    private Organization toOrg(Document doc) throws CloudException {
        Logger logger = getLogger(Terremark.class);
        Organization org = new Organization();
        NodeList blocks = doc.getElementsByTagName("Organization");
        if (blocks == null) {
            throw new CloudException("Did not find any organizations");
        } else if (blocks.getLength() > 1) {
            logger.warn(
                    "There is more than one organization in this account. This must be a reseller account. Only using the first organization.");
        }

        Node organization = blocks.item(0);
        NamedNodeMap attributes = organization.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            if (node.getNodeName().equals(Terremark.HREF)) {
                org.setId(hrefToId(node.getNodeValue()));
            } else if (node.getNodeName().equals(Terremark.NAME)) {
                org.setName(node.getNodeValue());
            }
        }

        return org;
    }

    public Task toTask(Node node) throws CloudException, InternalException {
        NodeList childNodes = node.getChildNodes();
        Task task = new Task();

        task.taskId = hrefToId(node.getAttributes().getNamedItem(Terremark.HREF).getNodeValue());

        for (int i = 0; i < childNodes.getLength(); i++) {
            Node child = childNodes.item(i);
            String name = childNodes.item(i).getNodeName();

            if (name.equals(Terremark.STATUS_TAG)) {
                task.status = child.getTextContent();
            } else if (name.equals(Terremark.ERROR_MESSAGE_TAG)) {
                task.errorMessage = child.getTextContent();
            }
        }
        return task;
    }

    public void waitForTask(String taskHref, long sleepTime, long timeout)
            throws CloudException, InternalException {
        logger.debug("enter - waitForTask(): " + taskHref);
        boolean complete = false;
        long startTime = System.currentTimeMillis();
        long failurePoint = -1L;
        int failedCalls = 0;
        while (!complete && failedCalls < TASK_ERROR_COUNT) {
            TerremarkMethod method;
            Document doc = null;

            method = new TerremarkMethod(this, HttpMethodName.GET, taskHref, null, null);
            try {
                doc = method.invoke();
            } catch (CloudException e) {
                logger.warn("waitForTask Error: " + e);
                failedCalls++;
            }
            if (doc != null) {
                Node taskNode = doc.getElementsByTagName(Terremark.TASK_TAG).item(0);
                Task lt = toTask(taskNode);

                if (lt.status.equals(Terremark.TASK_COMPLETE)) {
                    complete = true;
                } else if (lt.status.equals(Terremark.TASK_ERROR)) {
                    String message = lt.errorMessage;

                    if (message == null) {
                        if (failurePoint == -1L) {
                            failurePoint = System.currentTimeMillis();
                        }
                        if ((System.currentTimeMillis() - failurePoint) > (CalendarWrapper.MINUTE * 2)) {
                            message = "Task failed without further information.";
                        }
                    }
                    if (message != null) {
                        throw new CloudException(message);
                    }
                }
                logger.debug("Time ellapsed since task start time " + (System.currentTimeMillis() - startTime));
                if (!complete) {
                    if ((System.currentTimeMillis() - startTime) > timeout) {
                        throw new InternalException("Timed out waiting for the task to complete.");
                    }
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
        if (failedCalls >= TASK_ERROR_COUNT) {
            throw new CloudException("waitForTask(): Get task call failed " + failedCalls + " times. Giving up.");
        }
        logger.debug("exit - waitForTask(): " + taskHref);
    }

    public static String removeCommas(String tag) {
        Pattern p = Pattern.compile("[, ]");
        Matcher m = p.matcher(tag);
        String clean = m.replaceFirst("");
        return clean;
    }

    public static String getCatalogIdFromHref(String catalogHref) {
        // "/" + Terremark.ADMIN + "/" + Template.CATALOG + "/" + catalogId
        String id = null;
        String catalogString = "/" + Terremark.ADMIN + "/" + Template.CATALOG + "/";
        catalogHref = catalogHref.toLowerCase();
        if (catalogHref.contains(catalogString)) {
            int startCatalogId = catalogHref.indexOf(catalogString) + catalogString.length();
            id = catalogHref.substring(startCatalogId) + "::" + Template.ImageType.CATALOG_ENTRY.name();
        } else {
            id = null;
            logger.warn("getCatalogIdFromHref(): Failed to parse catalog href " + catalogHref);
        }
        return id;
    }

}