Package org.globus.workspace.cloud.client.cluster

Source Code of org.globus.workspace.cloud.client.cluster.ClusterUtil

/*
* Copyright 1999-2008 University of Chicago
*
* 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.globus.workspace.cloud.client.cluster;

import org.globus.workspace.client_core.ParameterProblem;
import org.globus.workspace.cloud.client.util.CloudClientUtil;
import org.globus.workspace.cloud.client.util.HistoryUtil;
import org.globus.workspace.common.print.Print;
import org.globus.wsrf.encoding.DeserializationException;
import org.globus.wsrf.encoding.ObjectDeserializer;
import org.nimbustools.ctxbroker.generated.gt4_0.description.*;
import org.xml.sax.InputSource;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClusterUtil {

    // -------------------------------------------------------------------------
    // File intake
    // -------------------------------------------------------------------------

    /**
     * @param path path to file
     * @return deserialized
     * @throws DeserializationException problem with parsing
     * @throws IOException problem with file etc.
     */
    public static Cloudcluster_Type parseClusterDocument(String path)
            throws DeserializationException, IOException {

        Cloudcluster_Type cluster = null;
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(path));
            cluster = (Cloudcluster_Type)
                            ObjectDeserializer.deserialize(
                                new InputSource(in), Cloudcluster_Type.class);
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return cluster;
    }

    // -------------------------------------------------------------------------
    // Deployment file intake
    // -------------------------------------------------------------------------

    /**
     * @param path path to file
     * @return deserialized
     * @throws DeserializationException problem with parsing
     * @throws IOException problem with file etc.
     */
    public static Clouddeployment_Type parseDeployDocument(String path)
            throws DeserializationException, IOException {

        Clouddeployment_Type deployment = null;
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(path));
            deployment = (Clouddeployment_Type)
                            ObjectDeserializer.deserialize(
                                new InputSource(in), Clouddeployment_Type.class);
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return deployment;
    }

    /**
     * Builds a map of workspace names -> deployment lists from a
     * deployment document
     * @param deployment
     * @return
     * @throws ParameterProblem
     */
    public static Map<String,Clouddeploy_Type[]> parseDeployment(
        Clouddeployment_Type deployment) throws ParameterProblem {

        if (deployment == null) {
            throw new IllegalArgumentException("deployment cannot be null");
        }

        Clouddeployworkspace_Type[] workspaces = deployment.getWorkspace();


        final HashMap<String,Clouddeploy_Type[]> map =
            new HashMap<String, Clouddeploy_Type[]>(workspaces.length);

        for (Clouddeployworkspace_Type ws : workspaces) {

            String wsName = ws.getName();
            if (wsName == null || wsName.trim().length() == 0) {
                throw new ParameterProblem(
                    "Workspace name cannot be empty or null");
            }

            Clouddeploy_Type[] deploys = ws.getDeploy();
            if (deploys == null || deploys.length == 0) {
                throw new ParameterProblem("Each provided deployment "+
                    "workspace must have at least one deploy element");
            }


           if (map.put(wsName, deploys) != null) {
               throw new ParameterProblem("Each deployment workspace entry "+
                   "must have a unique name");
           }

        }

        return map;
    }

    // -------------------------------------------------------------------------
    // XML intake
    // -------------------------------------------------------------------------

    /**
     * @param cluster deserialized object, may not be null
     * @param memberIndex which member
     * @param print pr
     * @param brokerLocalNicPrefix search string for rigging broker nic names
     * @param brokerPublicNicPrefix search string for rigging broker nic names
     * @return valid ClusterMember object, never null
     * @throws ParameterProblem problem
     */
    public static ClusterMember parseRequest(Cloudcluster_Type cluster,
                                             int memberIndex,
                                             Print print,
                                             String brokerLocalNicPrefix,
                                             String brokerPublicNicPrefix)
            throws ParameterProblem {

        if (cluster == null) {
            throw new IllegalArgumentException("cluster may not be null");
        }

        final Cloudworkspace_Type[] members =
                shallowCloneCluster(cluster.getWorkspace(), print);

        final Cloudworkspace_Type member = members[memberIndex];

        String name = member.getName();
        if (name != null && name.trim().length() == 0) {
            name = null;
        }

        final String image = member.getImage();
        if (image == null || image.trim().length() == 0) {
            throw new ParameterProblem("Cluster member has no image");
        }

        final short quantity = member.getQuantity();
        if (quantity < 1) {
            throw new ParameterProblem("Illegal, requested less than 1 " +
                    "instance of cluster member '" + name + "'");
        }

        final Cloudnic_Type[] xmlnics = member.getNic();
        if (xmlnics == null || xmlnics.length == 0) {
            throw new ParameterProblem("Cluster member '" + name +
                                            "' has no nic definition.");
        }

        final ClusterMemberNic[] nics = new ClusterMemberNic[xmlnics.length];

        final boolean nicNamesRequired = nics.length > 1;

        for (int i = 0; i < nics.length; i++) {
           
            if (xmlnics[i] == null) {
                throw new ParameterProblem("Cluster member '" + name +
                        "' has null nic element"); // ??
            }

            final String assoc = xmlnics[i].get_value();
            if (assoc == null || assoc.trim().length() == 0) {
                throw new ParameterProblem("Cluster member '" + name +
                        "' has \"<nic>\" element with no network name.");
            }

            String iface = xmlnics[i].get_interface();
           
            if (iface == null || iface.trim().length() == 0) {

                if (nicNamesRequired) {

                    throw new ParameterProblem("Cluster member '" + name +
                        "' has multiple NICs and a \"<nic>\" element with " +
                        "empty or missing interface name.  With multiple " +
                        "NICs you need to explicitly name each one (either" +
                        "'publicnic' or 'localnic'), see " +
                        "samples.");

                } else {

                    final String assocComp = assoc.toLowerCase().trim();
                    final String pubComp;
                    if (brokerPublicNicPrefix != null) {
                        pubComp = brokerPublicNicPrefix.toLowerCase().trim();
                    } else {
                        pubComp = "";
                    }
                    final String privComp;
                    if (brokerLocalNicPrefix != null) {
                        privComp = brokerLocalNicPrefix.toLowerCase().trim();
                    } else {
                        privComp = "";
                    }

                    final String nicName;
                    if (assocComp.startsWith(pubComp)) {
                        nicName = "publicnic";
                    } else if (assocComp.startsWith(privComp)) {
                        nicName = "localnic";
                    } else {
                        throw new ParameterProblem("Cannot pick a broker " +
                                "NIC name for the network '" + assocComp + "'");
                    }

                    iface = doctorContext(nicName,
                                          member.getCtx(),
                                          print,
                                          "Cluster member '" + name + "'");
                }
            }

            if (member.getCtx() != null) {
                if (!iface.equalsIgnoreCase("publicnic")
                        && !iface.equalsIgnoreCase("localnic")) {
                    print.errln("\n*** Warning: you are using broker NIC " +
                            "names in the cluster contextualization " +
                            "document that will probably not be resolvable " +
                            "by the ctx-agent.\nThe ctx-agent expects only " +
                            "'publicnic' or 'localnic' if there is more than " +
                            "one NIC.\nYou have however provided an " +
                            "interface name '" + iface + "'");
                }
            }

            boolean login = false;
            final Boolean wantLogin = xmlnics[i].getWantlogin();
            if (wantLogin != null && wantLogin) {
                login = true;
            }

            nics[i] = new ClusterMemberNic(iface, assoc, login);
        }


        final Cloudcluster_Type newcluster;
        if (member.getCtx() != null) {
            // active flag is bolted in for "new style" contextualization:

            // The whole point of the shallow copy. This sets a unique active
            // flag for this cluster member: the user data the VM's context
            // agent gets will have a unique active flag in the ctx document
            members[memberIndex].setActive(Boolean.TRUE);

            newcluster = new Cloudcluster_Type();
            newcluster.setWorkspace(members);
        } else {
            newcluster = null;
        }
       
        Clouddeploy_Type[] deploy = member.getDeploy();

        return new ClusterMember(name, image, quantity, nics, newcluster, deploy);
    }

    // shallow: enough to make each ACTIVE flag unique, other stuff is OK to be
    // duplicate references
    private static Cloudworkspace_Type[] shallowCloneCluster(
            Cloudworkspace_Type[] workspace, Print print) {

        if (workspace == null) {
            throw new IllegalArgumentException(
                    "workspace array may not be null");
        }

        Cloudworkspace_Type[] newarr =
                new Cloudworkspace_Type[workspace.length];

        for (int i = 0; i < workspace.length; i++) {
            newarr[i] = shallowCloneOneClusterWorkspace(workspace[i],
                                                        print);
        }
       
        return newarr;
    }

    // shallow: enough to make each member's ACTIVE flag unique, other stuff
    // is OK to be duplicate references
    private static Cloudworkspace_Type shallowCloneOneClusterWorkspace(
            Cloudworkspace_Type one, Print print) {

        if (one == null) {
            throw new IllegalArgumentException("'one' may not be null");
        }

        Cloudworkspace_Type newone = new Cloudworkspace_Type();
        newone.setImage(one.getImage());
        newone.setName(one.getName());
        newone.setNic(one.getNic());
        newone.setQuantity(one.getQuantity());


        // contextualization: THIS CAN BE NULL
        newone.setCtx(one.getCtx());

        // unlikely, person that did this would be some kind of developer etc.
        if (Boolean.TRUE.equals(one.getActive())) {
            print.errln("Warning: you have set an active=true " +
                    "flag in the cluster definition provided to the cloud " +
                    "client?  Why did you do that? " +
                    "It's going to be set to false now.");
        }

        // ALL start with False
        newone.setActive(Boolean.FALSE);

        return newone;
    }

    private static String doctorContext(String iface,
                                        Contextualization_Type ctx,
                                        Print print,
                                        String memberName)
            throws ParameterProblem {

        /*
            Situations:

            A. No ctx section: do nothing
            B. No provided identities in ctx: make it the given iface
            C. More than one provided identity in ctx: error, needs explicit nics
            D. The one provided identity in ctx has a name: return matching name
         */

        if (ctx == null) {
            return iface; // *** EARLY RETURN ***
        }

        final Print pr;
        if (print == null) {
            pr = new Print();
        } else {
            pr = print;
        }

        final Provides_Type provides = ctx.getProvides();

        if (provides == null) {
            throw new ParameterProblem("no provides element?");
        }

        final IdentityProvides_Type[] idents = provides.getIdentity();
        if (idents == null || idents.length == 0) {
            final IdentityProvides_Type ident = new IdentityProvides_Type();
            ident.set_interface(iface);
            final IdentityProvides_Type[] newidents = {ident};
            provides.setIdentity(newidents);

            pr.debugln(memberName + ": found no provides identities, created " +
                    "new, empty one with iface '" + iface + "'");

            return iface; // *** EARLY RETURN ***
        }

        if (idents.length > 1) {
            throw new ParameterProblem(memberName + ": found multiple " +
                    "identities in context 'provides' section, but " +
                    "only one NIC is defined in the cluster definition");
        }

        final String ctxIface = idents[0].get_interface();

        if (ctxIface != null) {
            pr.debugln(memberName + ": found interface name " +
                       "in the identity in context 'provides' section, but " +
                        "there is an explicit name for it -- ClusterMemberNic " +
                        "will now match this ('" + ctxIface + "')");
            return ctxIface;
        } else {
            idents[0].set_interface(iface);
            doctorContextProvides(ctx, iface, pr);
            return iface;
        }
    }

    private static void doctorContextProvides(Contextualization_Type ctx,
                                              String iface,
                                              Print pr) {

        final Provides_Type provides = ctx.getProvides();
        final Provides_TypeRole[] roles = provides.getRole();
        if (roles == null || roles.length == 0) {
            return;
        }
        for (final Provides_TypeRole role : roles) {
            pr.debugln("Role '" + role.get_value() + "' doctored. " +
                    "Interface was '" + role.get_interface() +
                    "' and is now '" + iface + "'");
            role.set_interface(iface);
        }
    }

    /**
     * Resolves absolute path to SSH hosts file; ensures it exists with proper permissions
     * @param ssh_hostsfile path to hosts file
     * @param print
     * @return possibly modified path
     * @throws ParameterProblem
     */
    public static String expandSshHostsFile(String ssh_hostsfile, Print print)
        throws ParameterProblem {

        if (ssh_hostsfile == null) {
            throw new IllegalArgumentException("ssh_hostsfile cannot be null");
        }

        if (ssh_hostsfile.startsWith("~")) {

            final String homedir = System.getProperty("user.home");

            if (homedir == null || homedir.trim().length() == 0) {
                throw new ParameterProblem("Need to replace tilde in " +
                        "SSH known hosts file, but cannot determine " +
                        "user home directory.  Please hardcode, see " +
                        "properties file.");
            }

            print.debugln("\n(tilde expansion necessary)");
            print.debugln("$user.home = " + homedir);

            final String result =
                    ssh_hostsfile.replaceFirst("~", homedir);

            print.infoln("SSH known_hosts contained tilde:");
            print.infoln("  - '" + ssh_hostsfile + "' --> '" +
                                            result + "'");

            ssh_hostsfile = result;
        }

        final File f2 = new File(ssh_hostsfile);
        ssh_hostsfile = f2.getAbsolutePath();

        print.debugln("Examining '" + ssh_hostsfile + "'");

        if (!CloudClientUtil.
                fileExistsAndReadwritable(ssh_hostsfile)) {

            throw new ParameterProblem("SSH known_hosts file does " +
                    "not exist or is not read+writable: '" +
                    ssh_hostsfile + "'");
        }

        print.debugln("Exists, readable, and writable: '" +
                                        ssh_hostsfile + "'");
        return ssh_hostsfile;
    }

    /**
     * Parses and validates cluster members in the provided cluster definition file
     * @param clusterPath path to cluster definition
     * @param brokerLocalNicPrefix search string for rigging broker nic names
     * @param brokerPublicNicPrefix search string for rigging broker nic names
     * @param print
     * @return array of cluster members
     * @throws ParameterProblem
     */
    public static ClusterMember[] getClusterMembers(String clusterPath,
                                                    String brokerLocalNicPrefix,
                                                    String brokerPublicNicPrefix,
                                                    Print print)
        throws ParameterProblem {

        final Cloudcluster_Type cluster = getCluster(clusterPath, print);

        return getClusterMembers(cluster, brokerLocalNicPrefix, brokerPublicNicPrefix, print);
    }

    public static Cloudcluster_Type getCluster(String clusterPath, Print print)
        throws ParameterProblem {

        if (clusterPath == null) {
            throw new IllegalArgumentException("clusterPath may not be null");
        }

        final File f = new File(clusterPath);
        clusterPath = f.getAbsolutePath();

        print.debugln("Examining cluster definition @ '" +
                                        clusterPath + "'");

        if (!CloudClientUtil.fileExistsAndReadable(clusterPath)) {
            throw new ParameterProblem("Given cluster description file " +
                    "does not exist or is not readable: '" +
                    clusterPath + "'");
        }

        print.debugln("Exists and readable: '" + clusterPath + "'");

        print.infoln();

        final Cloudcluster_Type cluster;
        try {
            cluster = parseClusterDocument(clusterPath);
            if (cluster == null) {
                throw new DeserializationException("No parsing result?");
            }
        } catch (DeserializationException e) {
            final String msg = "Could not parse the contents of the cluster " +
                    "definition file you provided.\n - Path: '" +
                    clusterPath + "'\n - Is it legal XML?  Try " +
                    "diffing your file with one of the example files or see " +
                    "the online instructions.\n - Error: " + e.getMessage();
            throw new ParameterProblem(msg, e);
        } catch (IOException e) {
            final String msg = "Problem with the cluster definition file: " +
                    e.getMessage();
            throw new ParameterProblem(msg, e);
        }
        return cluster;
    }

    public static ClusterMember[] getClusterMembers(Cloudcluster_Type cluster,
                                                    String brokerLocalNicPrefix,
                                                    String brokerPublicNicPrefix,
                                                    Print print)
        throws ParameterProblem {

        final Cloudworkspace_Type[] members = cluster.getWorkspace();
        if (members == null || members.length == 0) {
            throw new ParameterProblem("No members of the cluster described " +
                    "in document");
        }

        print.debugln("Found " + members.length + " cluster members");

        ClusterMember[] clusterMembers = new ClusterMember[members.length];

        for (int i = 0; i < members.length; i++) {

            if (members[i] == null) {
                throw new ParameterProblem(
                        "Cluster member (xml) unexpectedly null");
            }

            clusterMembers[i] =
                parseRequest(cluster, i, print,
                    brokerLocalNicPrefix,
                    brokerPublicNicPrefix);

        }

        // duplicate names would hose EPR writing
        for (int i = 0; i < members.length; i++) {
            final String thisName = members[i].getName();
            if (thisName != null) {
                for (int j = 0; j < members.length; j++) {
                    if (j == i) {
                        continue;
                    }
                    if (thisName.equals(members[j].getName())) {
                        throw new ParameterProblem("Found a duplicate " +
                                "nickname in the cluster definition ('" +
                                thisName + "').  Each <workspace> section " +
                                "needs a unique name.  You can leave out " +
                                "the <name> tag of one or more to have it " +
                                "autogenerated if you like.");
                    }
                }
            }
        }
        return clusterMembers;
    }

    public static KnownHostsTask[] constructKnownHostTasks(
        ClusterMember[] clusterMembers,
        boolean perHostDir) {

        final List knownHostsList = new ArrayList(clusterMembers.length);
        for (int i = 0; i < clusterMembers.length; i++) {

            final int memberIndex = i;
            final ClusterMember member = clusterMembers[memberIndex];

            if (member.isOneLoginFlagPresent()) {

                final ClusterMemberNic[] nics = member.getNics();
                for (int j = 0; j < nics.length; j++) {
                    if (nics[j].loginDesired) {
                        knownHostsList.add(
                                new KnownHostsTask(memberIndex,
                                                   null,
                                                   nics[j].iface,
                                                   member.getPrintName(),
                                                   perHostDir,
                                                   null));
                    }
                }
            }

        }


        final KnownHostsTask[] knownHostTasks;
        if (knownHostsList.isEmpty()) {
            knownHostTasks = null;
        } else {
            knownHostTasks =
                    (KnownHostsTask[]) knownHostsList.toArray(
                            new KnownHostsTask[knownHostsList.size()]);
        }
        return knownHostTasks;
    }

    public static void printClusterInfo(ClusterMember[] clusterMembers, Print print) {
        print.infoln("\nRequesting cluster.");
        for (int i = 0; i < clusterMembers.length; i++) {
            final ClusterMember member = clusterMembers[i];
            String inststr = " instance";
            if (member.getQuantity() > 1) {
                inststr += "s";
            }

            final String mname;
            if (member.getPrintName() == null) {
                mname = HistoryUtil.getMemberName(i+1);
            } else {
                mname = member.getPrintName();
            }

            print.infoln("  - " + mname + ": image '" +
                    member.getImageName() + "', " + member.getQuantity() +
                    inststr);
        }
    }
}
TOP

Related Classes of org.globus.workspace.cloud.client.cluster.ClusterUtil

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.