All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dasein.cloud.rackspace.network.CloudLoadBalancers Maven / Gradle / Ivy

The newest version!
/**
 * 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> nodes = new ArrayList>();
            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 node = new HashMap();
                
                
                node.put("address", address);
                node.put("condition", "ENABLED");
                node.put("port", port);
                nodes.add(node);
            }
            if( !nodes.isEmpty() ) {
                HashMap json = new HashMap();
            
                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 node = new HashMap();
                            
                            
                            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 lb = new HashMap();
            
            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> ips = new ArrayList>();
            HashMap ip = new HashMap();
            
            ip.put("type", "PUBLIC");
            ips.add(ip);
            lb.put("virtualIps", ips);
            
            ArrayList> nodes = new ArrayList>();
            
            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 node = new HashMap();
                    
                        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 json = new HashMap();
            
            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 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 listLoadBalancerStatus() throws CloudException, InternalException {
        ArrayList status = new ArrayList();

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

    static private transient Collection supportedAlgorithms;
    
    @Override
    public Iterable listSupportedAlgorithms() throws CloudException, InternalException {
        if( supportedAlgorithms == null ) {
            ArrayList algorithms = new ArrayList();
            
            algorithms.add(LbAlgorithm.ROUND_ROBIN);
            algorithms.add(LbAlgorithm.LEAST_CONN);
            supportedAlgorithms = Collections.unmodifiableList(algorithms);
        }
        return supportedAlgorithms;
    }

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

    static private transient Collection supportedProtocols;
    
    @Override
    public Iterable listSupportedProtocols() throws CloudException, InternalException {
        if( supportedProtocols == null ) {
            ArrayList protocols = new ArrayList();
            
            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 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 loadBalancers = new ArrayList();
                
                if( ob.has("loadBalancers") ) {
                    JSONArray lbs = ob.getJSONArray("loadBalancers");
                    
                    if( lbs.length() > 0 ) {
                        Iterable vms = provider.getComputeServices().getVirtualMachineSupport().listVirtualMachines();

                        for( int i=0; i getNodes(String loadBalancerId) throws CloudException, InternalException {
        ArrayList nodes = new ArrayList();
        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 mapNodes(String loadBalancerId, String[] serverIds) throws CloudException, InternalException {
        TreeSet nodeIds = new TreeSet();

        if( serverIds != null && serverIds.length > 0 ) {
            Collection 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 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 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 nodes = new ArrayList();
            JSONArray arr = json.getJSONArray("nodes");
            
            for( int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy