org.dasein.cloud.joyent.compute.Machine.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.joyent.compute.Machine.java

Source

/**
 * Copyright (C) 2009-2014 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.joyent.compute;

import org.apache.log4j.Logger;
import org.dasein.cloud.*;
import org.dasein.cloud.compute.*;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.joyent.JoyentException;
import org.dasein.cloud.joyent.JoyentMethod;
import org.dasein.cloud.joyent.SmartDataCenter;
import org.dasein.cloud.util.Cache;
import org.dasein.cloud.util.CacheLevel;
import org.dasein.util.CalendarWrapper;
import org.dasein.util.uom.storage.Megabyte;
import org.dasein.util.uom.storage.Storage;
import org.dasein.util.uom.time.Day;
import org.dasein.util.uom.time.TimePeriod;
import org.dasein.util.uom.time.TimePeriodUnit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class Machine extends AbstractVMSupport<SmartDataCenter> {
    Logger logger = SmartDataCenter.getLogger(Machine.class, "std");

    private SmartDataCenter provider;
    private transient volatile MachineCapabilities capabilities;

    Machine(SmartDataCenter sdc) {
        super(sdc);
        provider = sdc;
    }

    @Override
    /**
     * Only resizing of type=smartmachine is supported.
     */
    public VirtualMachine alterVirtualMachineProduct(@Nonnull String virtualMachineId, @Nonnull String productId)
            throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        method.doPostString(provider.getEndpoint(), "machines/" + virtualMachineId,
                "action=resize&package=" + productId);
        return getVirtualMachine(virtualMachineId);
    }

    @Override
    public void start(@Nonnull String vmId) throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=start");
    }

    static private class MiData {
        public Architecture architecture;
        public Platform platform;
    }

    static private HashMap<String, MiData> miCache = new HashMap<String, MiData>();

    private void discover(@Nonnull VirtualMachine vm) throws InternalException, CloudException {
        String miId = vm.getProviderMachineImageId();

        if (miCache.containsKey(miId)) {
            MiData d = miCache.get(miId);

            vm.setArchitecture(d.architecture);
            vm.setPlatform(d.platform);
        } else {
            if (!provider.getComputeServices().hasImageSupport()) {
                vm.setArchitecture(Architecture.I64);
                vm.setPlatform(Platform.UNKNOWN);
                return;
            }
            MachineImage img = provider.getComputeServices().getImageSupport().getImage(miId);

            if (img == null) {
                vm.setArchitecture(Architecture.I64);
                vm.setPlatform(Platform.UNKNOWN);
            } else {
                if (img.getName().contains("64")) {
                    vm.setArchitecture(Architecture.I64);
                } else if (img.getName().contains("32")) {
                    vm.setArchitecture(Architecture.I32);
                } else {
                    vm.setArchitecture(Architecture.I64);
                }
                vm.setPlatform(Platform.guess(img.getName() + " " + img.getDescription()));
                MiData d = new MiData();

                d.architecture = vm.getArchitecture();
                d.platform = vm.getPlatform();
                miCache.put(miId, d);
            }
        }
    }

    static private HashMap<String, VirtualMachineProduct> productCache = new HashMap<String, VirtualMachineProduct>();

    @Nonnull
    @Override
    public VirtualMachineCapabilities getCapabilities() throws InternalException, CloudException {
        if (capabilities == null) {
            capabilities = new MachineCapabilities(provider);
        }
        return capabilities;
    }

    @Override
    public @Nullable VirtualMachineProduct getProduct(@Nonnull String productId)
            throws InternalException, CloudException {
        if (productCache.containsKey(productId)) {
            return productCache.get(productId);
        }
        for (VirtualMachineProduct prd : listProducts(Architecture.I64)) {
            if (prd.getProviderProductId().equals(productId)) {
                productCache.put(productId, prd);
                return prd;
            }
        }
        return null;
    }

    @Override
    public @Nullable VirtualMachine getVirtualMachine(@Nonnull String vmId)
            throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        try {
            String json = method.doGetJson(provider.getEndpoint(), "machines/" + vmId);

            if (json == null) {
                return null;
            }
            return toVirtualMachine(new JSONObject(json));
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    @Override
    public boolean isSubscribed() throws CloudException, InternalException {

        org.dasein.cloud.util.Cache<Boolean> cache = org.dasein.cloud.util.Cache.getInstance(getProvider(),
                "isSubscribedVirtualMachine", Boolean.class, CacheLevel.REGION_ACCOUNT);
        final Iterable<Boolean> cachedIsSubscribed = cache.get(getContext());
        if (cachedIsSubscribed != null && cachedIsSubscribed.iterator().hasNext()) {
            final Boolean isSubscribed = cachedIsSubscribed.iterator().next();
            if (isSubscribed != null) {
                return isSubscribed;
            }
        }

        JoyentMethod method = new JoyentMethod(provider);
        try {
            method.doGetJson(provider.getEndpoint(), "packages");
        } catch (JoyentException e) {
            if (e.getErrorType().equals(CloudErrorType.AUTHENTICATION)) {
                cache.put(getContext(), Collections.singleton(false));
                return false;
            }
            throw e;
        }
        cache.put(getContext(), Collections.singleton(true));
        return true;
    }

    @Override
    public @Nonnull VirtualMachine launch(@Nonnull VMLaunchOptions withLaunchOptions)
            throws CloudException, InternalException {
        JoyentMethod method = new JoyentMethod(provider);
        Map<String, Object> post = new HashMap<String, Object>();

        if (withLaunchOptions.getUserData() != null) {
            post.put("metadata.user-script", withLaunchOptions.getUserData());
        }
        if (withLaunchOptions.getHostName() != null) {
            String name = validateName(withLaunchOptions.getHostName());

            post.put("name", name);
        }
        // Joyent will use datacenter default package/dataset if missing
        if (withLaunchOptions.getStandardProductId() != null) {
            post.put("package", withLaunchOptions.getStandardProductId());
        }
        if (withLaunchOptions.getMachineImageId() != null) {
            post.put("dataset", withLaunchOptions.getMachineImageId());
        }

        Map<String, Object> meta = withLaunchOptions.getMetaData();

        if (meta.size() > 0) {
            for (Map.Entry<String, Object> entry : meta.entrySet()) {
                post.put("metadata." + entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
        post.put("metadata.dsnTrueImage", withLaunchOptions.getMachineImageId());
        post.put("metadata.dsnTrueProduct", withLaunchOptions.getStandardProductId());
        post.put("metadata.dsnDescription", withLaunchOptions.getDescription());
        String json = method.doPostString(provider.getEndpoint(), "machines", new JSONObject(post).toString());

        if (json == null) {
            throw new CloudException("No machine was created");
        }
        try {
            return toVirtualMachine(new JSONObject(json));
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public @Nonnull VirtualMachine launch(@Nonnull String fromMachineImageId,
            @Nonnull VirtualMachineProduct product, @Nonnull String dataCenterId, @Nonnull String name,
            @Nonnull String description, @Nullable String withKeypairId, @Nullable String inVlanId,
            boolean withAnalytics, boolean asSandbox, @Nullable String[] firewallIds, @Nullable Tag... tags)
            throws InternalException, CloudException {
        VMLaunchOptions options;

        if (inVlanId == null) {
            options = VMLaunchOptions
                    .getInstance(product.getProviderProductId(), fromMachineImageId, name, description)
                    .inDataCenter(dataCenterId);
        } else {
            options = VMLaunchOptions
                    .getInstance(product.getProviderProductId(), fromMachineImageId, name, description)
                    .inVlan(null, dataCenterId, inVlanId);
        }
        if (withKeypairId != null) {
            options = options.withBoostrapKey(withKeypairId);
        }
        if (tags != null) {
            for (Tag t : tags) {
                options = options.withMetaData(t.getKey(), t.getValue());
            }
        }
        if (firewallIds != null) {
            options = options.behindFirewalls(firewallIds);
        }

        // this is backwards compat for some old enStratus stuff in Joyent
        // you really should be using the launch(VMLaunchOptions) and you won't be burdened
        // by this legacy stuff
        options = options.withUserData("#!/usr/bin/env sh\n" + "\n"
                + "export PATH=/opt/local/bin:/opt/local/sbin:/usr/bin:/usr/sbin\n" + "\n"
                + "if [[ $EUID -ne 0 ]] ; then\n" + "  echo \"Must be root to install enstratus package\"\n"
                + "  exit 1\n" + "fi\n" + "\n" + "echo \"==> Updating package lists...\"\n"
                + "out=$(pkgin update)\n" + "if [[ $? -ne 0 ]] ; then\n"
                + "  echo \"error updating package lists\"\n" + "  exit 1\n" + "fi\n" + "\n"
                + "echo \"==> Installing Sun-jdk6...\"\n" + "out=$(yes | pkgin in sun-jdk6 sun-jre6)\n"
                + "if [[ $? -ne 0 ]] ; then\n" + "  echo \"error installing java\"\n" + "  exit 1\n" + "fi\n" + "\n"
                + "echo \"==> Installing enstratus package...\"\n" + "out=$(pkgin -y in enstratus-agent)\n"
                + "if [[ $? -ne 0 ]] ; then\n" + "  echo \"error installing enstratus agent\"\n" + "  exit 1\n"
                + "fi\n");
        return launch(options);
    }

    @Override
    public @Nonnull Iterable<String> listFirewalls(@Nonnull String vmId) throws InternalException, CloudException {
        // TODO
        return Collections.emptyList();
    }

    @Override
    public @Nonnull Iterable<VirtualMachineProduct> listProducts(VirtualMachineProductFilterOptions options,
            Architecture architecture) throws InternalException, CloudException {
        Cache<VirtualMachineProduct> cache = Cache.getInstance(getProvider(), "VM.listProducts",
                VirtualMachineProduct.class, CacheLevel.REGION_ACCOUNT, TimePeriod.valueOf(1, "day"));
        final Iterable<VirtualMachineProduct> cachedProducts = cache.get(getContext());
        if (cachedProducts != null && cachedProducts.iterator().hasNext()) {
            return cachedProducts;
        }
        JoyentMethod method = new JoyentMethod(provider);
        String json = method.doGetJson(provider.getEndpoint(), "packages");

        if (json == null) {
            return Collections.emptyList();
        }
        try {
            ArrayList<VirtualMachineProduct> products = new ArrayList<VirtualMachineProduct>();
            JSONArray list = new JSONArray(json);

            for (int i = 0; i < list.length(); i++) {
                JSONObject ob = list.getJSONObject(i);
                VirtualMachineProduct prd = new VirtualMachineProduct();

                if (ob.has("name")) {
                    prd.setName(ob.getString("name"));
                }
                if (ob.has("memory")) {
                    prd.setRamSize(new Storage<Megabyte>(ob.getInt("memory"), Storage.MEGABYTE));
                }
                if (ob.has("disk")) {
                    prd.setRootVolumeSize(new Storage<Megabyte>(ob.getInt("disk"), Storage.MEGABYTE));
                }
                if (ob.has("vcpus")) {
                    prd.setCpuCount(ob.getInt("vcpus"));
                }
                // SmartOS products are returned with 0 vCPUs as this metric doesn't apply
                // to them according to Joyent. In SmartOS you get some burstable capacity.
                // We will set them to 1 CPU anyway, as zero CPU is no CPU.
                if (prd.getCpuCount() == 0) {
                    prd.setCpuCount(1);
                }
                if (ob.has("description")) {
                    prd.setDescription(ob.getString("description"));
                } else {
                    prd.setDescription(prd.getName());
                }
                prd.setProviderProductId(ob.getString("id"));
                if (options != null) {
                    if (options.matches(prd))
                        products.add(prd);
                } else {
                    products.add(prd);
                }
            }
            cache.put(getContext(), products);
            return products;
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    @Override
    public @Nonnull Iterable<ResourceStatus> listVirtualMachineStatus() throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        try {
            JSONArray machines = new JSONArray(method.doGetJson(provider.getEndpoint(), "machines"));
            ArrayList<ResourceStatus> vms = new ArrayList<ResourceStatus>();

            for (int i = 0; i < machines.length(); i++) {
                ResourceStatus vm = toStatus(machines.getJSONObject(i));

                if (vm != null) {
                    vms.add(vm);
                }
            }
            return vms;
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    @Override
    public @Nonnull Iterable<VirtualMachine> listVirtualMachines() throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        try {
            JSONArray machines = new JSONArray(method.doGetJson(provider.getEndpoint(), "machines"));
            ArrayList<VirtualMachine> vms = new ArrayList<VirtualMachine>();

            for (int i = 0; i < machines.length(); i++) {
                VirtualMachine vm = toVirtualMachine(machines.getJSONObject(i));

                if (vm != null) {
                    vms.add(vm);
                }
            }
            return vms;
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

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

    @Override
    public void stop(@Nonnull String vmId, boolean force) throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);

        method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=stop");
    }

    @Override
    public void reboot(@Nonnull String vmId) throws CloudException, InternalException {
        JoyentMethod method = new JoyentMethod(provider);

        method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=reboot");
    }

    @Override
    public void terminate(@Nonnull String vmId, @Nullable String explanation)
            throws InternalException, CloudException {
        long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 20);
        JoyentMethod method = new JoyentMethod(provider);
        VirtualMachine vm = getVirtualMachine(vmId);

        if (vm == null) {
            throw new CloudException("No such server: " + vmId);
        }
        VmState currentState = vm.getCurrentState();
        while (VmState.PENDING.equals(currentState) && System.currentTimeMillis() < timeout) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                /* ignore */ }
            vm = getVirtualMachine(vmId);
            if (vm == null) {
                return;
            }
            currentState = vm.getCurrentState();
        }
        if (VmState.RUNNING.equals(currentState)) {
            method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=stop");
        }
        while (!VmState.STOPPED.equals(currentState) && !VmState.TERMINATED.equals(currentState)
                && System.currentTimeMillis() < timeout) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                /* ignore */ }
            vm = getVirtualMachine(vmId);
            if (vm == null) {
                return;
            }
            currentState = vm.getCurrentState();
        }
        method.doDelete(provider.getEndpoint(), "machines/" + vmId);
        if (timeout < CalendarWrapper.MINUTE * 5) {
            timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 5);
        }
        while (System.currentTimeMillis() < timeout) {
            if (vm == null || VmState.TERMINATED.equals(vm.getCurrentState())) {
                return;
            }
            try {
                Thread.sleep(10000L);
            } catch (InterruptedException ignore) {
            }
            vm = getVirtualMachine(vmId);
        }
        logger.warn("System timed out waiting for VM termination");
    }

    @Override
    public @Nullable String getPassword(@Nonnull String vmId) throws InternalException, CloudException {
        final String[] adminUsernames = { "root", "administrator", "admin" };
        JoyentMethod method = new JoyentMethod(provider);
        try {
            JSONObject ob = new JSONObject(
                    method.doGetJson(provider.getEndpoint(), "machines/" + vmId + "/metadata?credentials=true"));
            if (ob.has("credentials")) {
                JSONObject credentials = ob.getJSONObject("credentials");
                for (String username : adminUsernames) {
                    if (ob.has(username)) {
                        return ob.getString(username);
                    }
                }
            }
        } catch (JSONException e) {
            throw new InternalException("Unable to parse the response", e);
        }
        return null;
    }

    @Override
    public @Nullable String getUserData(@Nonnull String vmId) throws InternalException, CloudException {
        JoyentMethod method = new JoyentMethod(provider);
        try {
            JSONObject ob = new JSONObject(
                    method.doGetJson(provider.getEndpoint(), "machines/" + vmId + "/metadata"));
            if (ob.has("user-script")) {
                return ob.getString("user-script");
            }
        } catch (JSONException e) {
            throw new InternalException("Unable to parse the response", e);
        }
        return null;
    }

    private VirtualMachine toVirtualMachine(JSONObject ob) throws CloudException, InternalException {
        if (ob == null) {
            return null;
        }
        try {
            VirtualMachine vm = new VirtualMachine();

            vm.setClonable(false);
            vm.setImagable(false);
            vm.setLastPauseTimestamp(-1L);
            vm.setPersistent(true);
            vm.setProviderDataCenterId(provider.getContext().getRegionId() + "a");
            vm.setProviderOwnerId(provider.getContext().getAccountNumber());
            vm.setProviderRegionId(provider.getContext().getRegionId());
            vm.setTerminationTimestamp(-1L);

            if (ob.has("id")) {
                vm.setProviderVirtualMachineId(ob.getString("id"));
            }
            if (ob.has("name")) {
                vm.setName(ob.getString("name"));
            }
            if (ob.has("ips")) {
                JSONArray ips = ob.getJSONArray("ips");
                ArrayList<String> pubIp = new ArrayList<String>();
                ArrayList<String> privIp = new ArrayList<String>();

                for (int i = 0; i < ips.length(); i++) {
                    String addr = ips.getString(i);
                    boolean pub = false;

                    if (!addr.startsWith("10.") && !addr.startsWith("192.168.")) {
                        if (addr.startsWith("172.")) {
                            String[] nums = addr.split("\\.");

                            if (nums.length != 4) {
                                pub = true;
                            } else {
                                try {
                                    int x = Integer.parseInt(nums[1]);

                                    if (x < 16 || x > 31) {
                                        pub = true;
                                    }
                                } catch (NumberFormatException ignore) {
                                    // ignore
                                }
                            }
                        } else {
                            pub = true;
                        }
                    }
                    if (pub) {
                        pubIp.add(addr);
                    } else {
                        privIp.add(addr);
                    }
                }
                if (!pubIp.isEmpty()) {
                    vm.setPublicIpAddresses(pubIp.toArray(new String[pubIp.size()]));
                }
                if (!privIp.isEmpty()) {
                    vm.setPrivateIpAddresses(privIp.toArray(new String[privIp.size()]));
                }
            }
            if (ob.has("metadata")) {
                JSONObject md = ob.getJSONObject("metadata");
                JSONArray names = md.names();

                if (names != null) {
                    for (int i = 0; i < names.length(); i++) {
                        String name = names.getString(i);

                        if (name.equals("dsnDescription")) {
                            vm.setDescription(md.getString(name));
                        } else if (name.equals("dsnTrueImage")) {
                            vm.setProviderMachineImageId(md.getString(name));
                        } else if (name.equals("dsnTrueProduct")) {
                            vm.setProductId(md.getString(name));
                        } else {
                            vm.addTag(name, md.getString(name));
                        }
                    }
                }
            }
            if (vm.getProviderMachineImageId() == null && ob.has("dataset")) {
                vm.setProviderMachineImageId(getImageIdFromUrn(ob.getString("dataset")));
            }
            if (ob.has("created")) {
                vm.setCreationTimestamp(provider.parseTimestamp(ob.getString("created")));
            }
            vm.setPausable(false); // can't ever pause/resume joyent vms
            vm.setRebootable(false);
            if (ob.has("state")) {
                vm.setCurrentState(toState(ob.getString("state")));

                if (VmState.RUNNING.equals(vm.getCurrentState())) {
                    vm.setRebootable(true);
                } else if (VmState.STOPPED.equals(vm.getCurrentState())) {
                    vm.setImagable(true);
                }
            }
            vm.setLastBootTimestamp(vm.getCreationTimestamp());
            if (vm.getName() == null) {
                vm.setName(vm.getProviderVirtualMachineId());
            }
            if (vm.getDescription() == null) {
                vm.setDescription(vm.getName());
            }
            discover(vm);
            boolean isVMSmartOs = (vm.getPlatform().equals(Platform.SMARTOS));
            if (vm.getProductId() == null) {
                VirtualMachineProduct d = null;
                int disk, ram;

                disk = ob.getInt("disk");
                ram = ob.getInt("memory");
                for (VirtualMachineProduct prd : listProducts(vm.getArchitecture())) {
                    d = prd;
                    boolean isProductSmartOs = prd.getName().contains("smartos");
                    if (prd.getRootVolumeSize().convertTo(Storage.MEGABYTE).intValue() == disk
                            && prd.getRamSize().intValue() == ram) {
                        if (isVMSmartOs && !isProductSmartOs) {
                            continue;
                        }
                        if (!isVMSmartOs && isProductSmartOs) {
                            continue;
                        }
                        vm.setProductId(prd.getProviderProductId());
                        break;
                    }
                }
                if (vm.getProductId() == null) {
                    vm.setProductId(d.getProviderProductId());
                }
            }
            return vm;
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    private @Nonnull VmState toState(@Nonnull String s) {
        if (s.equalsIgnoreCase("running")) {
            return VmState.RUNNING;
        } else if (s.equalsIgnoreCase("provisioning")) {
            return VmState.PENDING;
        } else if (s.equalsIgnoreCase("stopping")) {
            return VmState.STOPPING;
        } else if (s.equalsIgnoreCase("stopped")) {
            return VmState.STOPPED;
        } else if (s.equalsIgnoreCase("deleted")) {
            return VmState.TERMINATED;
        } else {
            logger.warn("DEBUG: Unknown Joyent VM state: " + s);
            return VmState.PENDING;
        }
    }

    private @Nullable ResourceStatus toStatus(@Nullable JSONObject ob) throws CloudException, InternalException {
        if (ob == null) {
            return null;
        }
        try {
            VmState state = VmState.PENDING;
            String vmId = null;

            if (ob.has("id")) {
                vmId = ob.getString("id");
            }
            if (ob.has("state")) {
                state = toState(ob.getString("state"));
            }
            if (vmId == null) {
                return null;
            }
            return new ResourceStatus(vmId, state);
        } catch (JSONException e) {
            throw new CloudException(e);
        }
    }

    static private HashMap<String, String> urnMapping = new HashMap<String, String>();

    private String getImageIdFromUrn(String urn) throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + Machine.class.getName() + ".getImageIdFromUrn(" + urn + ")");
        }
        try {
            if (urnMapping.containsKey(urn)) {
                return urnMapping.get(urn);
            }
            MachineImage img = provider.getComputeServices().getImageSupport().getImage(urn);

            if (img != null) {
                String id = img.getProviderMachineImageId();

                if (id != null) {
                    urnMapping.put(urn, id);
                    return id;
                }
            }
            return urn;
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + Machine.class.getName() + ".getImageIdFromUrn()");
            }
        }
    }

    /*
    TODO: fix
    @see org.dasein.cloud.joyent.compute.MachineCapabilities#getVirtualMachineNamingConstraints
    */
    private String validateName(String originalName) {
        StringBuilder name = new StringBuilder();

        for (int i = 0; i < originalName.length(); i++) {
            char c = originalName.charAt(i);

            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                name.append(c);
            } else if (((c >= '0' && c <= '9') || c == '-' || c == '_' || c == ' ') && name.length() > 0) {
                if (c == ' ') {
                    c = '-';
                }
                name.append(c);
            }
        }
        if (name.length() < 1) {
            return "unnamed-" + System.currentTimeMillis();
        }
        if (name.charAt(name.length() - 1) == '-' || name.charAt(name.length() - 1) == '_') { // check for trailing - or _
            name.deleteCharAt(name.length() - 1);
        }
        return name.toString();
    }
}