Package voldemort.utils

Source Code of voldemort.utils.ClusterGenerator

/*
* Copyright 2009 LinkedIn, 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 voldemort.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.cluster.Zone;

/**
* ClusterGenerator generates a cluster.xml file given either a list of hosts or
* a list of ClusterNodeDescriptor instances.
*
* <p/>
*
* This is largely the same as the generate_cluster_xml.py script, but was
* created because we need to be able to create new cluster.xml files
* dynamically from JUnit. It seemed overly kludgey to have a script that calls
* Java that then calls Python.
*
* <p/>
*
* <b>A note about host names</b>: the host name that is referred to in this
* class is a system's <i>internal</i> host name, rather than its external name.
* That is, depending on the network topology, a system may have an internal
* host name by which it is known by in its local network and an external host
* name by which it's known by systems external to that network.
*
* <p/>
*
* For example, EC2 systems have both internal host names and external host
* names. When a system external to EC2 (e.g. a developer's machine or a machine
* running a testing framework) wants to communicate with EC2 (via SSH, et al.),
* he must use the EC2 instance's external host name. However, for the EC2
* instances to communicate amongst themselves (e.g. when running the Voldemort
* tests), the Voldemort cluster nodes and Voldemort test nodes must use the
* internal host name. The external host name is used by the development/testing
* system to reach EC2 for orchestrating the testing. But the communication of
* the test and server nodes in the test are all on the same network, using the
* internal host name.
*
*
* @see ClusterNodeDescriptor
*/

public class ClusterGenerator {

    private final static long SEED = 5276239082346L;

    /**
     * Creates a list of ClusterNodeDescriptor instances given a list of host
     * names. The returned list of ClusterNodeDescriptor have only the host
     * name, ID, and partition list attributes set.
     *
     * <p/>
     *
     * The list of partitions is randomly distributed over the list of hosts
     * using a hard-coded random seed. This mimics the behavior of
     * generate_cluster_xml.py.
     *
     * <p/>
     *
     * <b>Please see "A note about host names"</b> in the class' JavaDoc for
     * important clarification as to the type of host names used.
     *
     * @param hostNames <i>Internal</i> host name
     * @param numPartitions Number of partitions <b>per host</b>
     *
     * @return List of ClusterNodeDescriptor
     */
    public List<ClusterNodeDescriptor> createClusterNodeDescriptors(List<String> hostNames,
                                                                    int numPartitions) {
        HashMap<Integer, List<String>> zoneToHostName = new HashMap<Integer, List<String>>();
        zoneToHostName.put(Zone.DEFAULT_ZONE_ID, hostNames);
        return createClusterNodeDescriptors(zoneToHostName, numPartitions);
    }

    /**
     * Creates a list of ClusterNodeDescriptor instances
     *
     * @param zoneToHostNames A mapping from zone to list of host names
     * @param numPartitions Number of partitions per host
     *
     * @return List of ClusterNodeDescriptor
     */
    private List<ClusterNodeDescriptor> createClusterNodeDescriptors(HashMap<Integer, List<String>> zoneToHostNames,
                                                                     int numPartitions) {
        int numHosts = 0;
        for(List<String> hostNames: zoneToHostNames.values()) {
            numHosts += hostNames.size();
        }

        // Create a list of integers [0..totalPartitions).
        int totalPartitions = numHosts * numPartitions;
        List<Integer> allPartitionIds = new ArrayList<Integer>();

        for(int i = 0; i < totalPartitions; i++)
            allPartitionIds.add(i);

        Random random = new Random(SEED);
        Collections.shuffle(allPartitionIds, random);

        List<ClusterNodeDescriptor> list = new ArrayList<ClusterNodeDescriptor>();

        int nodeId = 0;
        for(int zoneId = 0; zoneId < zoneToHostNames.size(); zoneId++) {
            List<String> hostNames = zoneToHostNames.get(zoneId);

            for(int i = 0; i < hostNames.size(); i++) {
                String hostName = hostNames.get(i);
                List<Integer> partitions = allPartitionIds.subList(nodeId * numPartitions,
                                                                   (nodeId + 1) * numPartitions);
                Collections.sort(partitions);

                ClusterNodeDescriptor cnd = new ClusterNodeDescriptor();
                cnd.setHostName(hostName);
                cnd.setId(nodeId);
                cnd.setPartitions(partitions);
                cnd.setZoneId(zoneId);

                nodeId++;
                list.add(cnd);
            }
        }

        return list;
    }

    /**
     * Creates a list of ClusterNodeDescriptor instances
     *
     * @param zoneToHostNames A mapping from zone to list of host names
     * @param cluster The cluster describing the various hosts
     *
     * @return List of ClusterNodeDescriptor
     */
    public List<ClusterNodeDescriptor> createClusterNodeDescriptors(HashMap<Integer, List<String>> zoneToHostNames,
                                                                    Cluster cluster) {
        if(cluster.getNumberOfZones() != zoneToHostNames.size())
            throw new IllegalStateException("zone size does not match");

        int numHosts = 0;
        for(List<String> hostNames: zoneToHostNames.values()) {
            numHosts += hostNames.size();
        }

        if(cluster.getNumberOfNodes() > numHosts)
            throw new IllegalStateException("cluster size exceeds the number of available instances");

        List<ClusterNodeDescriptor> list = new ArrayList<ClusterNodeDescriptor>();

        int nodeId = 0;
        for(int zoneId = 0; zoneId < zoneToHostNames.size(); zoneId++) {
            List<String> hostNames = zoneToHostNames.get(zoneId);

            for(int i = 0; i < cluster.getNumberOfNodes(); i++) {
                Node node = cluster.getNodeById(nodeId);
                String hostName = hostNames.get(i);
                List<Integer> partitions = node.getPartitionIds();

                ClusterNodeDescriptor cnd = new ClusterNodeDescriptor();
                cnd.setHostName(hostName);
                cnd.setId(nodeId);
                cnd.setSocketPort(node.getSocketPort());
                cnd.setHttpPort(node.getHttpPort());
                cnd.setAdminPort(node.getAdminPort());
                cnd.setPartitions(partitions);
                cnd.setZoneId(zoneId);

                nodeId++;
                list.add(cnd);
            }
        }

        return list;
    }

    /**
     * @param hostNames <i>Internal</i> host name
     * @param cluster Number of partitions <b>per host</b>
     *
     * @return List of ClusterNodeDescriptor
     */
    public List<ClusterNodeDescriptor> createClusterNodeDescriptors(List<String> hostNames,
                                                                    Cluster cluster) {
        HashMap<Integer, List<String>> zoneToHostName = new HashMap<Integer, List<String>>();
        zoneToHostName.put(Zone.DEFAULT_ZONE_ID, hostNames);
        return createClusterNodeDescriptors(zoneToHostName, cluster);
    }

    /**
     * Creates a String representing the format used by cluster.xml given the
     * cluster name, host names, and number of partitions for each host.
     *
     * @param clusterName Name of cluster
     * @param zoneToHostNames Zone to list of HostNames map
     * @param numPartitions Number of partitions <b>per host</b>
     *
     * @return String of formatted XML as used by cluster.xml
     */

    public String createClusterDescriptor(String clusterName,
                                          HashMap<Integer, List<String>> zoneToHostNames,
                                          int numPartitions) {
        List<ClusterNodeDescriptor> clusterNodeDescriptors = createClusterNodeDescriptors(zoneToHostNames,
                                                                                          numPartitions);

        return createClusterDescriptor(clusterName, clusterNodeDescriptors);
    }

    /**
     * Creates a String representing the format used by cluster.xml given the
     * cluster name and a list of ClusterNodeDescriptor instances.
     *
     * @param clusterName Name of cluster
     * @param clusterNodeDescriptors List of ClusterNodeDescriptor instances
     *        with which the string is generated
     *
     * @return String of formatted XML as used by cluster.xml
     */

    public String createClusterDescriptor(String clusterName,
                                          List<ClusterNodeDescriptor> clusterNodeDescriptors) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        pw.println("<cluster>");
        pw.println("\t<name>" + clusterName + "</name>");

        StringBuffer nodesBuffer = new StringBuffer();
        Set<Integer> zoneIds = new HashSet<Integer>();
        for(ClusterNodeDescriptor cnd: clusterNodeDescriptors) {
            String partitions = StringUtils.join(cnd.getPartitions(), ", ");

            if(nodesBuffer.length() > 0)
                nodesBuffer.append("\n");

            nodesBuffer.append("\t<server>\n");
            nodesBuffer.append("\t\t<id>" + cnd.getId() + "</id>\n");
            nodesBuffer.append("\t\t<host>" + cnd.getHostName() + "</host>\n");
            nodesBuffer.append("\t\t<http-port>" + cnd.getHttpPort() + "</http-port>\n");
            nodesBuffer.append("\t\t<socket-port>" + cnd.getSocketPort() + "</socket-port>\n");
            nodesBuffer.append("\t\t<admin-port>" + cnd.getAdminPort() + "</admin-port>\n");
            nodesBuffer.append("\t\t<partitions>" + partitions + "</partitions>\n");
            nodesBuffer.append("\t\t<zone-id>" + cnd.getZoneId() + "</zone-id>\n");
            nodesBuffer.append("\t</server>");

            zoneIds.add(cnd.getZoneId());
        }

        // Insert zones
        for(Integer zoneId: zoneIds) {
            pw.println("\t<zone>");
            pw.println("\t\t<zone-id>" + zoneId + "</zone-id>");
            pw.println("\t\t<proximity-list>" + generateProximityList(zoneId, zoneIds.size())
                       + "</proximity-list>");
            pw.println("\t</zone>");
        }

        // Insert servers
        pw.println(nodesBuffer.toString());
        pw.println("</cluster>");

        return sw.toString();
    }

    /**
     * Generate sequential proximity list
     *
     * @param zoneId Id of the Zone for which the proximity List is being
     *        generated
     * @param totalZones Total number of zones
     * @return String of list of zones
     */
    private String generateProximityList(int zoneId, int totalZones) {
        List<Integer> proximityList = new ArrayList<Integer>();
        int currentZoneId = (zoneId + 1) % totalZones;
        for(int i = 0; i < (totalZones - 1); i++) {
            proximityList.add(currentZoneId);
            currentZoneId = (currentZoneId + 1) % totalZones;
        }
        return StringUtils.join(proximityList, ", ");
    }
}
TOP

Related Classes of voldemort.utils.ClusterGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.