org.dasein.cloud.ibm.sce.compute.vm.SCEVirtualMachine.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.ibm.sce.compute.vm.SCEVirtualMachine.java

Source

/**
 * Copyright (C) 2012-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.ibm.sce.compute.vm;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
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.AbstractVMSupport;
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.MachineImageState;
import org.dasein.cloud.compute.Platform;
import org.dasein.cloud.compute.VMLaunchOptions;
import org.dasein.cloud.compute.VirtualMachine;
import org.dasein.cloud.compute.VirtualMachineProduct;
import org.dasein.cloud.compute.VmState;
import org.dasein.cloud.ibm.sce.ExtendedRegion;
import org.dasein.cloud.ibm.sce.SCE;
import org.dasein.cloud.ibm.sce.SCEConfigException;
import org.dasein.cloud.ibm.sce.SCEMethod;
import org.dasein.cloud.ibm.sce.identity.keys.SSHKeys;
import org.dasein.cloud.identity.SSHKeypair;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.util.CalendarWrapper;
import org.dasein.util.uom.storage.Gigabyte;
import org.dasein.util.uom.storage.Megabyte;
import org.dasein.util.uom.storage.Storage;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

/**
 * Implementation of the Dasein Cloud virtual machine support for IBM SmartCloud.
 * <p>Created by George Reese: 7/16/12 7:37 PM</p>
 * @author George Reese
 * @version 2012.04 initial version
 * @version 2012.09 updated with support for new 2012.09 object model (George Reese)
 * @since 2012.04
 */
public class SCEVirtualMachine extends AbstractVMSupport {
    static private final Logger logger = SCE.getLogger(SCEVirtualMachine.class, "std");

    private SCE provider;

    public SCEVirtualMachine(SCE provider) {
        super(provider);
        this.provider = provider;
    }

    @Override
    public @Nonnull VirtualMachine clone(@Nonnull String vmId, @Nonnull String intoDcId, @Nonnull String name,
            @Nonnull String description, boolean powerOn, @Nullable String... firewallIds)
            throws InternalException, CloudException {
        VirtualMachine vm = getVirtualMachine(vmId);

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

        MachineImage img = provider.getComputeServices().getImageSupport().captureImage(options);
        VirtualMachineProduct prd = getProduct(vm.getProductId());

        if (prd == null) {
            throw new CloudException("Unknown product associated with VM");
        }
        long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 10L);
        String machineImageId = img.getProviderMachineImageId();

        while (img == null || !MachineImageState.ACTIVE.equals(img.getCurrentState())) {
            if (System.currentTimeMillis() > timeout) {
                throw new CloudException("Cloud timed out while waiting for cloning image");
            }
            try {
                Thread.sleep(15000L);
            } catch (InterruptedException ignore) {
            }
            img = provider.getComputeServices().getImageSupport().getImage(machineImageId);
        }
        return launch(machineImageId, prd, intoDcId, name, description, null, null, false, false, firewallIds,
                new Tag[0]);
    }

    @Override
    public @Nonnull String getConsoleOutput(@Nonnull String vmId) throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        SCEMethod method = new SCEMethod(provider);

        Document xml = method.getAsXML("instances/" + vmId + "/logs");

        if (xml == null) {
            return "";
        }
        NodeList locations = xml.getElementsByTagName("Logs");
        StringBuilder console = new StringBuilder();

        for (int i = 0; i < locations.getLength(); i++) {
            Node item = locations.item(i);

            if (item.hasChildNodes()) {
                console.append(item.getFirstChild().getNodeValue());
                console.append("\n");
            }
        }
        return console.toString();
    }

    @Override
    public VirtualMachineProduct getProduct(@Nonnull String productId) throws InternalException, CloudException {
        for (Architecture architecture : Architecture.values()) {
            for (VirtualMachineProduct prd : listProducts(architecture)) {
                if (prd.getProviderProductId().equals(productId)) {
                    return prd;
                }
            }
        }
        return null;
    }

    @Override
    public @Nonnull String getProviderTermForServer(@Nonnull Locale locale) {
        return "instance";
    }

    @Override
    public VirtualMachine getVirtualMachine(@Nonnull String vmId) throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        SCEMethod method = new SCEMethod(provider);

        Document xml = method.getAsXML("instances/" + vmId);

        if (xml == null) {
            return null;
        }
        NodeList locations = xml.getElementsByTagName("Instance");

        for (int i = 0; i < locations.getLength(); i++) {
            Node item = locations.item(i);
            VirtualMachine vm = toVirtualMachine(ctx, item);

            if (vm != null) {
                return vm;
            }
        }
        return null;
    }

    @Override
    public @Nonnull Requirement identifyImageRequirement(@Nonnull ImageClass cls)
            throws CloudException, InternalException {
        return (cls.equals(ImageClass.MACHINE) ? Requirement.REQUIRED : Requirement.NONE);
    }

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

    @Override
    public @Nonnull Requirement identifyPasswordRequirement(Platform platform)
            throws CloudException, InternalException {
        Requirement passRequirement;
        if (platform.isWindows()) {
            passRequirement = Requirement.REQUIRED;
        } else {
            passRequirement = Requirement.NONE;
        }
        return passRequirement;
    }

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

    @Override
    public @Nonnull Requirement identifyShellKeyRequirement(Platform platform)
            throws CloudException, InternalException {
        return Requirement.REQUIRED;
    }

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

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

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

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

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

    @Override
    public boolean isSubscribed() throws CloudException, InternalException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was specified for this request");
        }
        try {
            ExtendedRegion region = provider.getDataCenterServices().getRegion(ctx.getRegionId());

            return (region != null && region.isCompute());
        } catch (CloudException e) {
            if (e.getHttpCode() == HttpServletResponse.SC_FORBIDDEN
                    || e.getHttpCode() == HttpServletResponse.SC_UNAUTHORIZED) {
                return false;
            }
            throw e;
        }
    }

    @Override
    public boolean isUserDataSupported() throws CloudException, InternalException {
        return true;
    }

    @Override
    public @Nonnull VirtualMachine launch(@Nonnull VMLaunchOptions withLaunchOptions)
            throws CloudException, InternalException {
        Logger logger = SCE.getLogger(SCEVirtualMachine.class, "launch");
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        ArrayList<NameValuePair> parameters = new ArrayList<NameValuePair>();
        String keypair = withLaunchOptions.getBootstrapKey();
        String password = null;

        parameters.add(new BasicNameValuePair("name", withLaunchOptions.getHostName()));
        parameters.add(new BasicNameValuePair("instanceType", withLaunchOptions.getStandardProductId()));
        parameters.add(new BasicNameValuePair("imageID", withLaunchOptions.getMachineImageId()));
        parameters.add(new BasicNameValuePair("location", ctx.getRegionId()));
        MachineImage launchImage = provider.getComputeServices().getImageSupport()
                .getImage(withLaunchOptions.getMachineImageId());
        if (launchImage.getPlatform().isUnix()) {
            if (keypair == null) {
                keypair = identifyKeypair();
            }
            parameters.add(new BasicNameValuePair("publicKey", keypair));
        } else if (launchImage.getPlatform().isWindows()) {
            if (withLaunchOptions.getBootstrapUser() != null) {
                logger.debug("Adding UserName parameter: " + withLaunchOptions.getBootstrapUser());
                parameters.add(new BasicNameValuePair("UserName", withLaunchOptions.getBootstrapUser()));
            }
            password = getRandomPassword(withLaunchOptions.getBootstrapUser());
            parameters.add(new BasicNameValuePair("Password", password));
        }

        if (withLaunchOptions.getVlanId() != null) {
            logger.debug("Adding vlanID parameter: " + withLaunchOptions.getVlanId());
            parameters.add(new BasicNameValuePair("vlanID", withLaunchOptions.getVlanId()));
        }

        String[] ips = withLaunchOptions.getStaticIpIds();

        if (ips.length > 0) {
            parameters.add(new BasicNameValuePair("ip", ips[0]));
            if (ips.length > 1) {
                for (int i = 1; i < ips.length; i++) {
                    if (i > 3) {
                        logger.warn(
                                "Attempt to assign more than 3 secondary IPs to a VM in IBM SmartCloud for account "
                                        + ctx.getAccountNumber());
                        break;
                    }
                    parameters.add(new BasicNameValuePair("SecondaryIP", ips[i]));
                }
            }
        }

        String userData = withLaunchOptions.getUserData();

        if (userData != null) {
            Properties p = new Properties();

            try {
                p.load(new ByteArrayInputStream(userData.getBytes("utf-8")));
            } catch (UnsupportedEncodingException e) {
                throw new InternalException(e);
            } catch (IOException e) {
                throw new InternalException(e);
            }
            for (String key : p.stringPropertyNames()) {
                String value = p.getProperty(key);

                if (value != null) {
                    parameters.add(new BasicNameValuePair(key, value));
                }
            }
        }
        SCEMethod method = new SCEMethod(provider);
        String response = method.post("instances", parameters);

        if (response == null) {
            throw new CloudException("Cloud accepted the post, but no body was in the response");
        }

        Document doc = method.parseResponse(response, true);

        NodeList locations = doc.getElementsByTagName("Instance");

        for (int i = 0; i < locations.getLength(); i++) {
            Node item = locations.item(i);
            VirtualMachine vm = toVirtualMachine(ctx, item);

            if (vm != null) {
                vm.setRootPassword(password);
                vm.setRootUser(withLaunchOptions.getBootstrapUser());
                return vm;
            }
        }
        throw new CloudException("No instance was in the XML response");
    }

    private @Nonnull String identifyKeypair() throws CloudException, InternalException {
        SSHKeys keys = provider.getIdentityServices().getShellKeySupport();

        for (SSHKeypair kp : keys.list()) {
            String id = kp.getProviderKeypairId();

            if (id != null) {
                return id;
            }
        }
        SSHKeypair kp = keys.createKeypair("dsn" + System.currentTimeMillis());
        String id = kp.getProviderKeypairId();

        if (id != null) {
            return id;
        }
        throw new CloudException("Unable to identify keys in the cloud");
    }

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

    static private Map<Architecture, Collection<VirtualMachineProduct>> products;

    @Override
    public Iterable<VirtualMachineProduct> listProducts(Architecture architecture)
            throws InternalException, CloudException {
        if (products == null) {
            ProviderContext ctx = provider.getContext();

            if (ctx == null) {
                throw new SCEConfigException("No context was configured for this request");
            }
            SCEMethod method = new SCEMethod(provider);

            Document xml = method.getAsXML("offerings/image");

            if (xml == null) {
                return Collections.emptyList();
            }
            HashMap<String, VirtualMachineProduct> t = new HashMap<String, VirtualMachineProduct>();
            HashMap<String, VirtualMachineProduct> s = new HashMap<String, VirtualMachineProduct>();
            NodeList items = xml.getElementsByTagName("Image");

            for (int i = 0; i < items.getLength(); i++) {
                HashMap<String, VirtualMachineProduct> prdMap = new HashMap<String, VirtualMachineProduct>();
                NodeList attrs = items.item(i).getChildNodes();
                Architecture a = null;

                for (int j = 0; j < attrs.getLength(); j++) {
                    Node attr = attrs.item(j);

                    if (attr.getNodeName().equalsIgnoreCase("Architecture") && attr.hasChildNodes()) {
                        String val = attr.getFirstChild().getNodeValue().trim();

                        if (val.equals("i386")) {
                            a = Architecture.I32;
                        } else if (val.startsWith("x86")) {
                            a = Architecture.I64;
                        } else {
                            System.out.println("DEBUG: Unknown architecture: " + val);
                            a = Architecture.I32;
                        }
                    } else if (attr.getNodeName().equalsIgnoreCase("SupportedInstanceTypes")
                            && attr.hasChildNodes()) {
                        NodeList types = attr.getChildNodes();

                        for (int k = 0; k < types.getLength(); k++) {
                            Node type = types.item(k);

                            if (type.getNodeName().equalsIgnoreCase("InstanceType") && type.hasChildNodes()) {
                                VirtualMachineProduct prd = new VirtualMachineProduct();
                                NodeList nodes = type.getChildNodes();

                                for (int l = 0; l < nodes.getLength(); l++) {
                                    Node node = nodes.item(l);

                                    if (node.getNodeName().equals("ID") && node.hasChildNodes()) {
                                        prd.setProviderProductId(node.getFirstChild().getNodeValue().trim());
                                    } else if (node.getNodeName().equals("Label") && node.hasChildNodes()) {
                                        prd.setName(node.getFirstChild().getNodeValue().trim());
                                    } else if (node.getNodeName().equals("Detail") && node.hasChildNodes()) {
                                        prd.setDescription(node.getFirstChild().getNodeValue().trim());
                                    }
                                }
                                if (prd.getProviderProductId() != null) {
                                    String[] parts = prd.getProviderProductId().split("/");

                                    if (parts.length == 3) {
                                        String[] sub = parts[0].split("\\.");

                                        if (sub.length > 0) {
                                            parts[0] = sub[sub.length - 1];
                                        }
                                        try {
                                            prd.setCpuCount(Integer.parseInt(parts[0]));
                                        } catch (NumberFormatException ignore) {
                                            // ignore
                                        }
                                        try {
                                            prd.setRamSize(new Storage<Megabyte>(Integer.parseInt(parts[1]),
                                                    Storage.MEGABYTE));
                                        } catch (NumberFormatException ignore) {
                                            // ignore
                                        }
                                        try {
                                            int idx = parts[2].indexOf("*");

                                            if (idx < 1) {
                                                prd.setRootVolumeSize(new Storage<Gigabyte>(
                                                        Integer.parseInt(parts[2]), Storage.GIGABYTE));
                                            } else {
                                                prd.setRootVolumeSize(new Storage<Gigabyte>(
                                                        Integer.parseInt(parts[2].substring(0, idx)),
                                                        Storage.GIGABYTE));
                                            }
                                        } catch (NumberFormatException ignore) {
                                            // ignore
                                        }
                                    }
                                    prdMap.put(prd.getProviderProductId(), prd);
                                }
                            }
                        }
                    }
                }
                if (a != null) {
                    if (a.equals(Architecture.I32)) {
                        t.putAll(prdMap);
                    } else if (a.equals(Architecture.I64)) {
                        s.putAll(prdMap);
                    }
                }
            }
            HashMap<Architecture, Collection<VirtualMachineProduct>> tmp = new HashMap<Architecture, Collection<VirtualMachineProduct>>();

            tmp.put(Architecture.I32, Collections.unmodifiableCollection(t.values()));
            tmp.put(Architecture.I64, Collections.unmodifiableCollection(s.values()));
            products = tmp;
        }
        return products.get(architecture);
    }

    @Override
    public @Nonnull Iterable<ResourceStatus> listVirtualMachineStatus() throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        SCEMethod method = new SCEMethod(provider);

        Document xml = method.getAsXML("instances");

        if (xml == null) {
            return Collections.emptyList();
        }
        NodeList locations = xml.getElementsByTagName("Instance");
        ArrayList<ResourceStatus> vms = new ArrayList<ResourceStatus>();

        for (int i = 0; i < locations.getLength(); i++) {
            Node item = locations.item(i);
            ResourceStatus vm = toStatus(item);

            if (vm != null) {
                vms.add(vm);
            }
        }
        return vms;
    }

    @Override
    public @Nonnull Iterable<VirtualMachine> listVirtualMachines() throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        SCEMethod method = new SCEMethod(provider);

        Document xml = method.getAsXML("instances");

        if (xml == null) {
            return Collections.emptyList();
        }
        NodeList locations = xml.getElementsByTagName("Instance");
        ArrayList<VirtualMachine> vms = new ArrayList<VirtualMachine>();

        for (int i = 0; i < locations.getLength(); i++) {
            Node item = locations.item(i);
            VirtualMachine vm = toVirtualMachine(ctx, item);

            if (vm != null) {
                vms.add(vm);
            }
        }
        return vms;
    }

    @Override
    public void reboot(@Nonnull String vmId) throws CloudException, InternalException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }

        ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
        SCEMethod method = new SCEMethod(provider);

        params.add(new BasicNameValuePair("state", "restart"));
        method.put("instances/" + vmId, params);
    }

    @Override
    public boolean supportsPauseUnpause(@Nonnull VirtualMachine vm) {
        return false;
    }

    @Override
    public boolean supportsStartStop(@Nonnull VirtualMachine vm) {
        return false;
    }

    @Override
    public boolean supportsSuspendResume(@Nonnull VirtualMachine vm) {
        return false;
    }

    @Override
    public void terminate(@Nonnull String vmId, @Nullable String explanation)
            throws InternalException, CloudException {
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new SCEConfigException("No context was configured for this request");
        }
        long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 20);
        VirtualMachine vm = getVirtualMachine(vmId);

        while (vm != null && vm.getCurrentState().equals(VmState.PENDING)) {
            if (System.currentTimeMillis() >= timeout) {
                throw new CloudException("Termination timed out");
            }
            try {
                Thread.sleep(15000L);
            } catch (InterruptedException ignore) {
            }
            vm = getVirtualMachine(vmId);
        }
        if (vm == null) {
            throw new CloudException("The VM " + vmId + " went away");
        }
        SCEMethod method = new SCEMethod(provider);

        method.delete("instances/" + vmId);
    }

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

    private @Nonnull String parseAddress(@Nonnull Node ipNode) {
        NodeList attrs = ipNode.getChildNodes();
        String address = null;

        for (int j = 0; j < attrs.getLength(); j++) {
            Node attr = attrs.item(j);
            if (attr.getNodeName().equalsIgnoreCase("IP") && attr.hasChildNodes()) {
                address = attr.getFirstChild().getNodeValue().trim();
            }

        }
        return address;
    }

    private @Nonnull String[] addAddress(@Nullable String[] current, @Nonnull String ipAddress) {
        String address = ipAddress;
        for (String currentIp : current) {
            if (currentIp.equals(address)) {
                address = null;
            }
        }

        if (address == null) {
            return (current == null ? new String[0] : current);
        }

        String[] addresses = new String[current == null ? 1 : current.length + 1];
        int i = 0;

        if (current != null) {
            for (String addr : current) {
                addresses[i++] = addr;
            }
        }
        addresses[i] = address;
        return addresses;
    }

    public @Nullable VirtualMachine toVirtualMachine(@Nonnull ProviderContext ctx, @Nullable Node node)
            throws CloudException, InternalException {
        if (node == null) {
            return null;
        }
        VirtualMachine vm = new VirtualMachine();

        vm.setRebootable(true);
        vm.setArchitecture(Architecture.I64);
        vm.setClonable(false);
        vm.setCurrentState(VmState.PENDING);
        vm.setImagable(true);
        vm.setPausable(false);
        vm.setPersistent(true);
        vm.setPlatform(Platform.UNKNOWN);
        NodeList nodes = node.getChildNodes();

        for (int i = 0; i < nodes.getLength(); i++) {
            Node attr = nodes.item(i);
            String nodeName = attr.getNodeName();

            if (nodeName.equalsIgnoreCase("ID") && attr.hasChildNodes()) {
                vm.setProviderVirtualMachineId(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("Name") && attr.hasChildNodes()) {
                vm.setName(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("Location") && attr.hasChildNodes()) {
                vm.setProviderRegionId(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("Owner") && attr.hasChildNodes()) {
                vm.setProviderOwnerId(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("Hostname") && attr.hasChildNodes()) {
                vm.setPublicDnsAddress(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("IP") && attr.hasChildNodes()) {
                String ip = attr.getFirstChild().getNodeValue().trim();
                boolean isPublic = isPublicIpAddress(ip);
                String[] addrs;
                if (isPublic) {
                    addrs = vm.getPublicIpAddresses();
                } else {
                    addrs = vm.getPrivateIpAddresses();
                }

                if (addrs == null || addrs.length == 0) {
                    if (isPublic) {
                        vm.setPublicIpAddresses(new String[] { ip });
                    } else {
                        vm.setPrivateIpAddresses(new String[] { ip });
                    }
                } else {
                    String[] tmp = new String[addrs.length + 1];

                    //noinspection ManualArrayCopy
                    for (int idx = 0; idx < addrs.length; idx++) {
                        tmp[idx] = addrs[idx];
                    }
                    tmp[tmp.length - 1] = ip;
                    addrs = tmp;
                    if (isPublic) {
                        vm.setPublicIpAddresses(addrs);
                    } else {
                        vm.setPrivateIpAddresses(addrs);
                    }
                }
            } else if (nodeName.equalsIgnoreCase("ImageID") && attr.hasChildNodes()) {
                vm.setProviderMachineImageId(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("InstanceType") && attr.hasChildNodes()) {
                vm.setProductId(attr.getFirstChild().getNodeValue().trim());
            } else if (nodeName.equalsIgnoreCase("Status") && attr.hasChildNodes()) {
                String status = attr.getFirstChild().getNodeValue().trim();

                vm.setCurrentState(toVmState(status));
            } else if (nodeName.equalsIgnoreCase("LaunchTime") && attr.hasChildNodes()) {
                vm.setCreationTimestamp(provider.parseTimestamp(attr.getFirstChild().getNodeValue().trim()));
            } else if (nodeName.equalsIgnoreCase("ExpirationTime") && attr.hasChildNodes()) {
                // what exactly is expiration time?
            } else if (nodeName.equalsIgnoreCase("PrimaryIP") && attr.hasChildNodes()) {
                String ipAddress = parseAddress(attr);
                boolean isPublic = isPublicIpAddress(ipAddress);
                if (isPublic) {
                    vm.setPublicIpAddresses(addAddress(vm.getPublicIpAddresses(), ipAddress));
                } else {
                    vm.setPrivateIpAddresses(addAddress(vm.getPrivateIpAddresses(), ipAddress));
                }
            } else if (nodeName.equalsIgnoreCase("SecondaryIP") && attr.hasChildNodes()) {
                String ipAddress = parseAddress(attr);
                boolean isPublic = isPublicIpAddress(ipAddress);
                if (isPublic) {
                    vm.setPublicIpAddresses(addAddress(vm.getPublicIpAddresses(), ipAddress));
                } else {
                    vm.setPrivateIpAddresses(addAddress(vm.getPrivateIpAddresses(), ipAddress));
                }
            } else if (nodeName.equalsIgnoreCase("Volume") && attr.hasChildNodes()) {
                // volume
            } else if (nodeName.equalsIgnoreCase("Vlan") && attr.hasChildNodes()) {
                NodeList items = attr.getChildNodes();

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

                    if (item.getNodeName().equalsIgnoreCase("ID") && item.hasChildNodes()) {
                        vm.setProviderVlanId(item.getFirstChild().getNodeValue().trim());
                    }
                }
            } else if (nodeName.equalsIgnoreCase("Software") && attr.hasChildNodes()) {
                NodeList software = attr.getChildNodes();

                for (int j = 0; j < software.getLength(); j++) {
                    Node snode = software.item(j);
                    String type = null;
                    String name = null;

                    if (snode.hasChildNodes()) {
                        NodeList sattrs = snode.getChildNodes();

                        for (int k = 0; k < sattrs.getLength(); k++) {
                            Node sattr = sattrs.item(k);

                            if (sattr.getNodeName().equalsIgnoreCase("Name") && sattr.hasChildNodes()) {
                                name = sattr.getFirstChild().getNodeValue().trim();
                            } else if (sattr.getNodeName().equalsIgnoreCase("Type") && sattr.hasChildNodes()) {
                                type = sattr.getFirstChild().getNodeValue().trim();
                            }
                        }
                    }
                    if (name != null && type != null && type.equalsIgnoreCase("OS")) {
                        vm.setPlatform(Platform.guess(name));
                    }
                }
            }
        }
        if (vm.getProviderVirtualMachineId() == null) {
            return null;
        }
        if (vm.getProviderRegionId() == null || !vm.getProviderRegionId().equals(ctx.getRegionId())) {
            return null;
        }
        if (vm.getName() == null) {
            vm.setName(vm.getProviderVirtualMachineId());
        }
        if (vm.getDescription() == null) {
            vm.setDescription(vm.getName());
        }
        vm.setProviderDataCenterId(vm.getProviderRegionId());
        vm.setLastBootTimestamp(vm.getCreationTimestamp());
        return vm;
    }

    private boolean isPublicIpAddress(String ipv4Address) {
        if (ipv4Address == null || ipv4Address.isEmpty()) {
            return false;
        }
        if (ipv4Address.startsWith("10.") || ipv4Address.startsWith("192.168")
                || ipv4Address.startsWith("169.254")) {
            return false;
        } else if (ipv4Address.startsWith("172.")) {
            String[] parts = ipv4Address.split("\\.");

            if (parts.length != 4) {
                return true;
            }
            int x = Integer.parseInt(parts[1]);

            if (x >= 16 && x <= 31) {
                return false;
            }
        }
        return true;
    }

    private @Nonnull VmState toVmState(@Nonnull String vmState) throws InternalException {
        if (vmState.equals("0") || vmState.equals("1") || vmState.equals("4") || vmState.equals("6")
                || vmState.equals("9") || vmState.equals("14") || vmState.equals("15")) {
            return VmState.PENDING;
        } else if (vmState.equals("2") || vmState.equals("3")) {
            return VmState.TERMINATED;
        } else if (vmState.equals("5")) {
            return VmState.RUNNING;
        } else if (vmState.equals("7") || vmState.equals("10") || vmState.equals("12")) {
            return VmState.STOPPING;
        } else if (vmState.equals("8") || vmState.equals("13")) {
            return VmState.REBOOTING;
        } else if (vmState.equals("11")) {
            return VmState.PAUSED;
        } else {
            System.out.println("DEBUG: Unknown VM state: " + vmState);
            return VmState.PENDING;
        }
    }

    public @Nullable ResourceStatus toStatus(@Nullable Node node) throws CloudException, InternalException {
        if (node == null) {
            return null;
        }
        NodeList nodes = node.getChildNodes();
        VmState state = null;
        String vmId = null;

        for (int i = 0; i < nodes.getLength(); i++) {
            Node attr = nodes.item(i);
            String nodeName = attr.getNodeName();

            if (nodeName.equalsIgnoreCase("ID") && attr.hasChildNodes()) {
                vmId = attr.getFirstChild().getNodeValue().trim();
            } else if (nodeName.equalsIgnoreCase("Status") && attr.hasChildNodes()) {
                String status = attr.getFirstChild().getNodeValue().trim();

                state = toVmState(status);
            }
            if (state != null && vmId != null) {
                break;
            }
        }
        if (vmId == null) {
            return null;
        }
        return new ResourceStatus(vmId, state == null ? VmState.PENDING : state);
    }

    static public String uppercaseAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    static public String lowercaseAlphabet = "abcdefghijklmnopqrstuvwxyz";
    static public String numbers = "0123456789";
    static public String symbols = "!$#@%^&*()-_=+[]{},.<>?/;:";
    static public String allChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$#@%^&*()-_=+[]{},.<>?/;:";
    static private final Random random = new Random();

    public String getRandomPassword(String username) {
        StringBuilder password = new StringBuilder();
        ArrayList<String> invalidSubstrings = new ArrayList<String>();
        final String[] invalidStrings = { username, "admin", "Administrator", "idcadmin" };
        for (String invalidString : invalidStrings) {
            invalidSubstrings.addAll(generateThreeCharSubstrings(invalidString));
        }
        int rnd = random.nextInt();
        int length = 16;

        if (rnd < 0) {
            rnd = -rnd;
        }
        length = length + (rnd % 8);
        while (password.length() < 1) {
            char c;

            rnd = random.nextInt();
            if (rnd < 0) {
                rnd = -rnd;
            }
            c = (char) (rnd % 255);
            if (uppercaseAlphabet.contains(String.valueOf(c))) {
                password.append(c);
            }
        }
        while (password.length() < 2) {
            char c;

            rnd = random.nextInt();
            if (rnd < 0) {
                rnd = -rnd;
            }
            c = (char) (rnd % 255);
            if (lowercaseAlphabet.contains(String.valueOf(c))) {
                password.append(c);
            }
        }
        while (password.length() < 3) {
            char c;

            rnd = random.nextInt();
            if (rnd < 0) {
                rnd = -rnd;
            }
            c = (char) (rnd % 255);
            if (numbers.contains(String.valueOf(c))) {
                password.append(c);
            }
        }
        while (password.length() < 4) {
            char c;

            rnd = random.nextInt();
            if (rnd < 0) {
                rnd = -rnd;
            }
            c = (char) (rnd % 255);
            if (symbols.contains(String.valueOf(c))) {
                password.append(c);
            }
        }
        while (password.length() < length) {
            char c;

            rnd = random.nextInt();
            if (rnd < 0) {
                rnd = -rnd;
            }
            c = (char) (rnd % 255);
            if (allChars.contains(String.valueOf(c))) {
                password.append(c);
            }
            for (String invalidSubstring : invalidSubstrings) {
                if (password.toString().contains(invalidSubstring)) {
                    password = password.delete(4, password.length());
                    break;
                }
            }

        }
        return password.toString();
    }

    private ArrayList<String> generateThreeCharSubstrings(String input) {
        ArrayList<String> substrings = new ArrayList<String>();
        Integer length = input.length();
        for (Integer i = 0; (i + 3) <= length; i++) {
            substrings.add(input.substring(i, i + 3));
        }
        return substrings;
    }

}