org.dasein.cloud.rackspace.network.CloudLoadBalancers.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.rackspace.network.CloudLoadBalancers.java

Source

/**
 * Copyright (C) 2009-2012 enStratus Networks Inc
 *
 * ====================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ====================================================================
 */

package org.dasein.cloud.rackspace.network;

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.TreeSet;

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

import org.apache.log4j.Logger;
import org.dasein.cloud.CloudErrorType;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.OperationNotSupportedException;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.compute.VirtualMachine;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.network.IPVersion;
import org.dasein.cloud.network.LbAlgorithm;
import org.dasein.cloud.network.LbListener;
import org.dasein.cloud.network.LbProtocol;
import org.dasein.cloud.network.LoadBalancer;
import org.dasein.cloud.network.LoadBalancerAddressType;
import org.dasein.cloud.network.LoadBalancerState;
import org.dasein.cloud.network.LoadBalancerSupport;
import org.dasein.cloud.rackspace.RackspaceCloud;
import org.dasein.cloud.rackspace.RackspaceException;
import org.dasein.cloud.rackspace.RackspaceMethod;
import org.dasein.util.CalendarWrapper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class CloudLoadBalancers implements LoadBalancerSupport {
    private RackspaceCloud provider;

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

    @Override
    public void addDataCenters(String toLoadBalancerId, String... dataCenterIdsToAdd)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("No support for data-center constrained load balancers");
    }

    @Override
    public void addServers(String toLoadBalancerId, String... serverIdsToAdd)
            throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudLoadBalancers.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudLoadBalancers.class.getName() + ".addServers(" + toLoadBalancerId + ","
                    + serverIdsToAdd + ")");
        }
        try {
            ArrayList<HashMap<String, Object>> nodes = new ArrayList<HashMap<String, Object>>();
            LoadBalancer lb = getLoadBalancer(toLoadBalancerId);
            int port = -1;

            if (lb == null) {
                logger.error("addServers(): No such load balancer: " + toLoadBalancerId);
                throw new CloudException("No such load balancer: " + toLoadBalancerId);
            }
            LbListener[] listeners = lb.getListeners();

            if (listeners != null) {
                for (LbListener listener : listeners) {
                    port = listener.getPrivatePort();
                    break;
                }
                if (port == -1) {
                    for (LbListener listener : listeners) {
                        port = listener.getPublicPort();
                        break;
                    }
                }
            }
            if (port == -1) {
                if (lb.getPublicPorts() != null && lb.getPublicPorts().length > 0) {
                    port = lb.getPublicPorts()[0];
                }
                if (port == -1) {
                    logger.error("addServers(): Could not determine a proper private port for mapping");
                    throw new CloudException("No port understanding exists for this load balancer");
                }
            }
            for (String id : serverIdsToAdd) {
                if (logger.isTraceEnabled()) {
                    logger.trace("addServers(): Adding " + id + "...");
                }
                VirtualMachine vm = provider.getComputeServices().getVirtualMachineSupport().getVirtualMachine(id);

                if (vm == null) {
                    logger.error("addServers(): Failed to add " + id + " because it does not exist");
                    throw new CloudException("No such server: " + id);
                }
                String address = null;

                if (vm.getProviderRegionId().equals(provider.getContext().getRegionId())) {
                    for (String addr : vm.getPrivateIpAddresses()) {
                        address = addr;
                        break;
                    }
                }
                if (address == null) {
                    for (String addr : vm.getPublicIpAddresses()) {
                        address = addr;
                        break;
                    }
                }
                if (address == null) {
                    logger.error("addServers(): No address exists for mapping the load balancer to this server");
                    throw new CloudException("The virtual machine " + id + " has no mappable addresses");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("addServers(): Mapping IP is: " + address);
                }
                HashMap<String, Object> node = new HashMap<String, Object>();

                node.put("address", address);
                node.put("condition", "ENABLED");
                node.put("port", port);
                nodes.add(node);
            }
            if (!nodes.isEmpty()) {
                HashMap<String, Object> json = new HashMap<String, Object>();

                json.put("nodes", nodes);
                RackspaceMethod method = new RackspaceMethod(provider);

                if (logger.isTraceEnabled()) {
                    logger.debug("addServers(): Calling cloud...");
                }
                try {
                    method.postLoadBalancers("/loadbalancers", toLoadBalancerId + "/nodes", new JSONObject(json));
                } catch (RackspaceException e) {
                    if (e.getHttpCode() == 422 && nodes.size() == 1) {
                        nodes.clear();
                        for (String id : serverIdsToAdd) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("addServers(): Adding " + id + "...");
                            }
                            VirtualMachine vm = provider.getComputeServices().getVirtualMachineSupport()
                                    .getVirtualMachine(id);

                            if (vm == null) {
                                logger.error("addServers(): Failed to add " + id + " because it does not exist");
                                throw new CloudException("No such server:" + id);
                            }
                            String address = null;

                            for (String addr : vm.getPublicIpAddresses()) {
                                address = addr;
                                break;
                            }
                            if (address == null) {
                                logger.error(
                                        "addServers(): No public address exists for mapping the load balancer to this server");
                                throw new CloudException(
                                        "The virtual machine " + id + " has no publicly mappable addresses");
                            }
                            if (logger.isDebugEnabled()) {
                                logger.debug("addServers(): Mapping IP is: " + address);
                            }
                            HashMap<String, Object> node = new HashMap<String, Object>();

                            node.put("address", address);
                            node.put("condition", "ENABLED");
                            node.put("port", port);
                            nodes.add(node);
                        }
                        json.clear();
                        json.put("nodes", nodes);
                        if (logger.isTraceEnabled()) {
                            logger.debug("addServers(): Attemptign with public IP...");
                        }
                        method.postLoadBalancers("/loadbalancers", toLoadBalancerId + "/nodes",
                                new JSONObject(json));
                    }
                }
                if (logger.isTraceEnabled()) {
                    logger.debug("addServers(): Done.");
                }
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudLoadBalancers.class.getName() + ".addServers()");
            }
        }
    }

    @Override
    public String create(String name, String description, String addressId, String[] dataCenterIds,
            LbListener[] listeners, String[] serverIds) throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudLoadBalancers.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudLoadBalancers.class.getName() + ".create(" + name + "," + description
                    + "," + addressId + "," + dataCenterIds + "," + listeners + "," + serverIds + ")");
        }
        try {
            if (listeners == null || listeners.length < 1) {
                logger.error("create(): Call failed to specify any listeners");
                throw new CloudException("Rackspace requires exactly one listener");
            }
            HashMap<String, Object> lb = new HashMap<String, Object>();

            lb.put("name", name);
            lb.put("port", listeners[0].getPublicPort());
            if (listeners[0].getNetworkProtocol().equals(LbProtocol.HTTP)) {
                lb.put("protocol", "HTTP");
            } else if (listeners[0].getNetworkProtocol().equals(LbProtocol.HTTPS)) {
                lb.put("protocol", "HTTPS");
            } else if (listeners[0].getNetworkProtocol().equals(LbProtocol.RAW_TCP)) {
                lb.put("protocol", matchProtocol(listeners[0].getPublicPort()));
            } else {
                logger.error("create(): Invalid protocol: " + listeners[0].getNetworkProtocol());
                throw new CloudException("Unsupported protocol: " + listeners[0].getNetworkProtocol());
            }
            if (listeners[0].getAlgorithm().equals(LbAlgorithm.LEAST_CONN)) {
                lb.put("algorithm", "LEAST_CONNECTIONS");
            } else if (listeners[0].getAlgorithm().equals(LbAlgorithm.ROUND_ROBIN)) {
                lb.put("algorithm", "ROUND_ROBIN");
            } else {
                logger.error("create(): Invalid algorithm: " + listeners[0].getAlgorithm());
                throw new CloudException("Unsupported algorithm: " + listeners[0].getAlgorithm());
            }
            ArrayList<Map<String, Object>> ips = new ArrayList<Map<String, Object>>();
            HashMap<String, Object> ip = new HashMap<String, Object>();

            ip.put("type", "PUBLIC");
            ips.add(ip);
            lb.put("virtualIps", ips);

            ArrayList<Map<String, Object>> nodes = new ArrayList<Map<String, Object>>();

            for (String id : serverIds) {
                VirtualMachine vm = provider.getComputeServices().getVirtualMachineSupport().getVirtualMachine(id);

                if (vm != null) {
                    String address = null;

                    if (vm.getProviderRegionId().equals(provider.getContext().getRegionId())) {
                        for (String addr : vm.getPrivateIpAddresses()) {
                            address = addr;
                            break;
                        }
                    }
                    if (address == null) {
                        for (String addr : vm.getPublicIpAddresses()) {
                            address = addr;
                            break;
                        }
                    }
                    if (address != null) {
                        HashMap<String, Object> node = new HashMap<String, Object>();

                        node.put("address", address);
                        node.put("condition", "ENABLED");
                        node.put("port", listeners[0].getPrivatePort());
                        nodes.add(node);
                    }
                }
            }
            if (nodes.isEmpty()) {
                logger.error("create(): Rackspace requires at least one node assignment");
                throw new CloudException("Rackspace requires at least one node assignment");
            }
            lb.put("nodes", nodes);

            HashMap<String, Object> json = new HashMap<String, Object>();

            json.put("loadBalancer", lb);
            RackspaceMethod method = new RackspaceMethod(provider);

            if (logger.isTraceEnabled()) {
                logger.trace("create(): Posting new load balancer data...");
            }
            JSONObject result = method.postLoadBalancers("/loadbalancers", null, new JSONObject(json));

            if (result == null) {
                logger.error("create(): Method executed successfully, but no load balancer was created");
                throw new CloudException("Method executed successfully, but no load balancer was created");
            }
            try {
                if (result.has("loadBalancer")) {
                    JSONObject ob = result.getJSONObject("loadBalancer");

                    if (ob != null) {
                        return ob.getString("id");
                    }
                }
                logger.error("create(): Method executed successfully, but no load balancer was found in JSON");
                throw new CloudException("Method executed successfully, but no load balancer was found in JSON");
            } catch (JSONException e) {
                logger.error(
                        "create(): Failed to identify a load balancer ID in the cloud response: " + e.getMessage());
                throw new CloudException(
                        "Failed to identify a load balancer ID in the cloud response: " + e.getMessage());
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudLoadBalancers.class.getName() + ".create()");
            }
        }
    }

    private String matchProtocol(int port) throws CloudException, InternalException {
        RackspaceMethod method = new RackspaceMethod(provider);
        JSONObject ob = method.getLoadBalancers("/loadbalancers", "protocols");

        if (ob == null) {
            return "TCP";
        } else {
            if (ob.has("protocols")) {
                try {
                    JSONArray list = ob.getJSONArray("protocols");

                    for (int i = 0; i < list.length(); i++) {
                        JSONObject p = list.getJSONObject(i);

                        if (p.has("port") && p.getInt("port") == port) {
                            return p.getString("name");
                        }
                    }
                } catch (JSONException e) {
                    throw new CloudException("Unable to parse protocols from Rackspace: " + e.getMessage());
                }
            }
            return "TCP";
        }
    }

    @Override
    public LoadBalancer getLoadBalancer(String loadBalancerId) throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudLoadBalancers.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace(
                    "enter - " + CloudLoadBalancers.class.getName() + ".getLoadBalancer(" + loadBalancerId + ")");
        }
        try {
            RackspaceMethod method = new RackspaceMethod(provider);
            JSONObject ob = method.getLoadBalancers("/loadbalancers", loadBalancerId);

            if (ob == null) {
                return null;
            }
            Iterable<VirtualMachine> vms = provider.getComputeServices().getVirtualMachineSupport()
                    .listVirtualMachines();

            try {
                if (ob.has("loadBalancer")) {
                    LoadBalancer lb = toLoadBalancer(ob.getJSONObject("loadBalancer"), vms);

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

    @Override
    public LoadBalancerAddressType getAddressType() throws CloudException, InternalException {
        return LoadBalancerAddressType.IP;
    }

    @Override
    public int getMaxPublicPorts() throws CloudException, InternalException {
        return 1;
    }

    @Override
    public String getProviderTermForLoadBalancer(Locale locale) {
        return "load balancer";
    }

    @Override
    public Iterable<ResourceStatus> listLoadBalancerStatus() throws CloudException, InternalException {
        ArrayList<ResourceStatus> status = new ArrayList<ResourceStatus>();

        for (LoadBalancer lb : listLoadBalancers()) {
            status.add(new ResourceStatus(lb.getProviderLoadBalancerId(), lb.getCurrentState()));
        }
        return status;
    }

    static private transient Collection<LbAlgorithm> supportedAlgorithms;

    @Override
    public Iterable<LbAlgorithm> listSupportedAlgorithms() throws CloudException, InternalException {
        if (supportedAlgorithms == null) {
            ArrayList<LbAlgorithm> algorithms = new ArrayList<LbAlgorithm>();

            algorithms.add(LbAlgorithm.ROUND_ROBIN);
            algorithms.add(LbAlgorithm.LEAST_CONN);
            supportedAlgorithms = Collections.unmodifiableList(algorithms);
        }
        return supportedAlgorithms;
    }

    @Override
    public @Nonnull Iterable<IPVersion> listSupportedIPVersions() throws CloudException, InternalException {
        return Collections.singletonList(IPVersion.IPV4);
    }

    static private transient Collection<LbProtocol> supportedProtocols;

    @Override
    public Iterable<LbProtocol> listSupportedProtocols() throws CloudException, InternalException {
        if (supportedProtocols == null) {
            ArrayList<LbProtocol> protocols = new ArrayList<LbProtocol>();

            protocols.add(LbProtocol.HTTP);
            protocols.add(LbProtocol.HTTPS);
            supportedProtocols = Collections.unmodifiableList(protocols);
        }
        return supportedProtocols;
    }

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

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

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

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

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

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

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

    @Override
    public Iterable<LoadBalancer> listLoadBalancers() throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudLoadBalancers.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + CloudLoadBalancers.class.getName() + ".listLoadBalancers()");
        }
        try {
            RackspaceMethod method = new RackspaceMethod(provider);
            JSONObject ob = method.getLoadBalancers("/loadbalancers", null);

            try {
                ArrayList<LoadBalancer> loadBalancers = new ArrayList<LoadBalancer>();

                if (ob.has("loadBalancers")) {
                    JSONArray lbs = ob.getJSONArray("loadBalancers");

                    if (lbs.length() > 0) {
                        Iterable<VirtualMachine> vms = provider.getComputeServices().getVirtualMachineSupport()
                                .listVirtualMachines();

                        for (int i = 0; i < lbs.length(); i++) {
                            JSONObject tmp = lbs.getJSONObject(i);

                            if (tmp.has("id")) {
                                JSONObject actual = method.getLoadBalancers("/loadbalancers", tmp.getString("id"));

                                if (actual != null && actual.has("loadBalancer")) {
                                    LoadBalancer lb = this.toLoadBalancer(actual.getJSONObject("loadBalancer"),
                                            vms);

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

    @Override
    public void remove(String loadBalancerId) throws CloudException, InternalException {
        Logger logger = RackspaceCloud.getLogger(CloudLoadBalancers.class, "std");

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

            do {
                try {
                    method.deleteLoadBalancers("/loadbalancers", loadBalancerId);
                    return;
                } catch (RackspaceException e) {
                    if (e.getHttpCode() != HttpServletResponse.SC_CONFLICT || e.getHttpCode() == 422) {
                        throw e;
                    }
                }
                try {
                    Thread.sleep(CalendarWrapper.MINUTE);
                } catch (InterruptedException e) {
                }
            } while (System.currentTimeMillis() < timeout);
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + CloudLoadBalancers.class.getName() + ".remove()");
            }
        }
    }

    static private class Node {
        public String nodeId;
        public String address;
    }

    public Collection<Node> getNodes(String loadBalancerId) throws CloudException, InternalException {
        ArrayList<Node> nodes = new ArrayList<Node>();
        RackspaceMethod method = new RackspaceMethod(provider);
        JSONObject response = method.getLoadBalancers("/loadbalancers", loadBalancerId + "/nodes");

        if (response != null && response.has("nodes")) {
            try {
                JSONArray arr = response.getJSONArray("nodes");

                for (int i = 0; i < arr.length(); i++) {
                    JSONObject node = arr.getJSONObject(i);
                    Node n = new Node();

                    n.nodeId = node.getString("id");
                    n.address = node.getString("address");
                    nodes.add(n);
                }
            } catch (JSONException e) {
                throw new CloudException("Unable to read nodes: " + e.getMessage());
            }
        }
        return nodes;
    }

    private Collection<String> mapNodes(String loadBalancerId, String[] serverIds)
            throws CloudException, InternalException {
        TreeSet<String> nodeIds = new TreeSet<String>();

        if (serverIds != null && serverIds.length > 0) {
            Collection<Node> nodes = getNodes(loadBalancerId);

            for (String serverId : serverIds) {
                VirtualMachine vm = provider.getComputeServices().getVirtualMachineSupport()
                        .getVirtualMachine(serverId);

                if (vm != null) {
                    boolean there = false;

                    if (vm.getProviderRegionId().equals(provider.getContext().getRegionId())) {
                        String[] addrs = vm.getPrivateIpAddresses();

                        if (addrs != null) {
                            for (String addr : addrs) {
                                for (Node n : nodes) {
                                    if (n.address.equals(addr)) {
                                        nodeIds.add(n.nodeId);
                                        there = true;
                                        break;
                                    }
                                }
                                if (there) {
                                    break;
                                }
                            }
                        }
                    }
                    if (!there) {
                        String[] addrs = vm.getPublicIpAddresses();

                        if (addrs != null) {
                            for (String addr : addrs) {
                                for (Node n : nodes) {
                                    if (n.address.equals(addr)) {
                                        nodeIds.add(n.nodeId);
                                        there = true;
                                        break;
                                    }
                                }
                                if (there) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return nodeIds;
    }

    @Override
    public void removeDataCenters(String fromLoadBalancerId, String... dataCenterIdsToRemove)
            throws CloudException, InternalException {
        throw new OperationNotSupportedException("No data center constraints in Rackspace");
    }

    @Override
    public void removeServers(String fromLoadBalancerId, String... serverIdsToRemove)
            throws CloudException, InternalException {
        Collection<String> nodeIds = mapNodes(fromLoadBalancerId, serverIdsToRemove);

        if (nodeIds.size() < 1) {
            return;
        }
        StringBuilder nodeString = new StringBuilder();

        for (String id : nodeIds) {
            if (nodeString.length() > 0) {
                nodeString.append("&");
            }
            nodeString.append("nodeId=" + id);
        }
        RackspaceMethod method = new RackspaceMethod(provider);

        method.deleteLoadBalancers("/loadbalancers", fromLoadBalancerId + "/nodes?" + nodeString.toString());
    }

    private LoadBalancer toLoadBalancer(JSONObject json, Iterable<VirtualMachine> possibleNodes)
            throws JSONException, CloudException {
        LoadBalancer loadBalancer = new LoadBalancer();

        loadBalancer.setProviderDataCenterIds(new String[] { provider.getContext().getRegionId() + "1" });
        loadBalancer.setProviderOwnerId(provider.getContext().getAccountNumber());
        loadBalancer.setProviderRegionId(provider.getContext().getRegionId());
        loadBalancer.setAddressType(LoadBalancerAddressType.IP);
        if (json.has("id")) {
            loadBalancer.setProviderLoadBalancerId(json.getString("id"));
        }
        if (json.has("name")) {
            loadBalancer.setName(json.getString("name"));
        }
        if (json.has("created")) {
            JSONObject ob = json.getJSONObject("created");

            if (ob.has("time")) {
                loadBalancer.setCreationTimestamp(provider.parseTimestamp(ob.getString("time")));
            }
        }
        if (json.has("status")) {
            String s = json.getString("status").toLowerCase();

            if (s.equals("active")) {
                loadBalancer.setCurrentState(LoadBalancerState.ACTIVE);
            } else {
                loadBalancer.setCurrentState(LoadBalancerState.PENDING);
            }
        }
        if (json.has("virtualIps")) {
            JSONArray arr = json.getJSONArray("virtualIps");

            for (int i = 0; i < arr.length(); i++) {
                JSONObject ob = arr.getJSONObject(i);

                if (ob.has("ipVersion") && ob.getString("ipVersion").equalsIgnoreCase("ipv4")) {
                    if (ob.has("address")) {
                        loadBalancer.setAddress(ob.getString("address"));
                        break;
                    }
                }
            }
        }
        int privatePort = -1;

        loadBalancer.setProviderServerIds(new String[0]);
        if (json.has("nodes")) {
            ArrayList<String> nodes = new ArrayList<String>();
            JSONArray arr = json.getJSONArray("nodes");

            for (int i = 0; i < arr.length(); i++) {
                JSONObject ob = arr.getJSONObject(i);

                if (ob.has("address")) {
                    String address = ob.getString("address");
                    VirtualMachine node = null;

                    for (VirtualMachine vm : possibleNodes) {
                        String[] addrs = vm.getPublicIpAddresses();

                        if (addrs != null) {
                            for (String addr : addrs) {
                                if (address.equals(addr)) {
                                    node = vm;
                                    break;
                                }
                            }
                        }
                        if (node == null) {
                            addrs = vm.getPrivateIpAddresses();
                            if (addrs != null) {
                                for (String addr : addrs) {
                                    if (address.equals(addr)) {
                                        node = vm;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if (node != null) {
                        nodes.add(node.getProviderVirtualMachineId());
                    }
                } else if (ob.has("port")) {
                    privatePort = ob.getInt("port");
                }
            }
            loadBalancer.setProviderServerIds(nodes.toArray(new String[nodes.size()]));
        }
        if (loadBalancer.getProviderLoadBalancerId() == null) {
            return null;
        }
        int port = -1;

        if (json.has("port")) {
            port = json.getInt("port");
            if (privatePort == -1) {
                privatePort = port;
            }
        }
        loadBalancer.setPublicPorts(new int[] { port });

        LbProtocol protocol = LbProtocol.RAW_TCP;

        if (json.has("protocol")) {
            String p = json.getString("protocol");

            if (p.equals("HTTP")) {
                protocol = LbProtocol.HTTP;
            } else if (p.equals("HTTPS")) {
                protocol = LbProtocol.HTTPS;
            } else if (p.equals("AJP")) {
                protocol = LbProtocol.AJP;
            }
        }

        LbAlgorithm algorithm = LbAlgorithm.ROUND_ROBIN;

        if (json.has("algorithm")) {
            String a = json.getString("algorithm").toLowerCase();

            if (a.equals("round_robin")) {
                algorithm = LbAlgorithm.ROUND_ROBIN;
            } else if (a.equals("least_connections")) {
                algorithm = LbAlgorithm.LEAST_CONN;
            }
        }
        LbListener l = new LbListener();

        l.setAlgorithm(algorithm);
        l.setNetworkProtocol(protocol);
        l.setPublicPort(port);
        l.setPrivatePort(privatePort);
        loadBalancer.setListeners(new LbListener[] { l });
        if (loadBalancer.getName() == null) {
            loadBalancer.setName(loadBalancer.getProviderLoadBalancerId());
        }
        if (loadBalancer.getDescription() == null) {
            loadBalancer.setDescription(loadBalancer.getName());
        }
        return loadBalancer;
    }
}