/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hms.client; import java.io.IOException; import java.net.URL; import javax.activity.InvalidActivityException; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hms.common.entity.Response; import org.apache.hms.common.entity.cluster.MachineState; import org.apache.hms.common.entity.command.CommandStatus; import org.apache.hms.common.entity.command.CreateClusterCommand; import org.apache.hms.common.entity.command.DeleteClusterCommand; import org.apache.hms.common.entity.command.UpgradeClusterCommand; import org.apache.hms.common.entity.manifest.ClusterManifest; import org.apache.hms.common.entity.manifest.ConfigManifest; import org.apache.hms.common.entity.manifest.NodesManifest; import org.apache.hms.common.entity.manifest.SoftwareManifest; import org.apache.hms.common.util.ExceptionUtil; import org.apache.hms.common.util.JAXBUtil; import com.sun.jersey.api.Responses; import com.sun.jersey.api.client.UniformInterfaceException; public class Client { private static Log log = LogFactory.getLog(Client.class); private static Executor clientRunner = Executor.getInstance(); @SuppressWarnings("static-access") private final static Option help = OptionBuilder.withLongOpt("help").withDescription("Output usage menu and quit").create("h"); @SuppressWarnings("static-access") private final static Option createCluster = OptionBuilder.withLongOpt("create-cluster").withArgName("cluster-name") .hasArg().withDescription("Create a cluster") .create(); @SuppressWarnings("static-access") private final static Option deleteCluster = OptionBuilder.withLongOpt("delete-cluster").withArgName("cluster-name") .hasArg().withDescription("Deletea cluster") .create(); @SuppressWarnings("static-access") private final static Option clusterStatus = OptionBuilder.withLongOpt("cluster-status").withArgName("cluster-name") .hasArg().withDescription("Check cluster status") .create("cs"); @SuppressWarnings("static-access") private final static Option upgradeCluster = OptionBuilder.withLongOpt("upgrade-cluster").withArgName("cluster-name") .hasArg().withDescription("Upgrade a cluster") .create(); @SuppressWarnings("static-access") private final static Option nodeStatus = OptionBuilder.withLongOpt( "node-status" ).withArgName( "nodepath" ) .hasArg().withDescription("check node status") .create("ns"); @SuppressWarnings("static-access") private final static Option cmdStatus = OptionBuilder.withArgName("command-id") .hasArg().withDescription("Check command status") .create("q"); @SuppressWarnings("static-access") private final static Option softwareManifest = OptionBuilder.withLongOpt("software").withArgName("software-url") .hasArg().withDescription("Location of software manifest") .create(); @SuppressWarnings("static-access") private final static Option nodesManifest = OptionBuilder.withLongOpt("nodes").withArgName("nodes-url") .hasArg().withDescription("Location of nodes manifest") .create(); @SuppressWarnings("static-access") private final static Option configManifest = OptionBuilder.withLongOpt("config").withArgName("config-url") .hasArg().withDescription("Location of config manifest") .create(); @SuppressWarnings("static-access") private final static Option dryRun = OptionBuilder.withLongOpt( "dryrun" ) .withDescription( "Test command only" ).create(); @SuppressWarnings("static-access") private final static Option verbose = OptionBuilder.withLongOpt( "verbose" ) .withDescription( "Print verbose information" ).create("v"); private static Options opt = setupOptions(); public static Options setupOptions() { if(opt==null) { opt = new Options(); } opt.addOption(help); opt.addOption(nodeStatus); opt.addOption(cmdStatus); opt.addOption(createCluster); opt.addOption(deleteCluster); opt.addOption(upgradeCluster); opt.addOption(clusterStatus); opt.addOption(nodesManifest); opt.addOption(configManifest); opt.addOption(softwareManifest); opt.addOption(verbose); opt.addOption(dryRun); return opt; } /** * Construct a create cluster command * @param clusterName - Cluster name * @param nodes - Nodes manifest is a url to a XML file which describes the server compositions of the cluster * @param software - Software manifest is a url to a XML file which describes the software compositions of the cluster * @param config - Configuration manifest is a url to a XML file which describes the configuration steps for the cluster * @return * @throws IOException */ public Response createCluster(String clusterName, URL nodes, URL software, URL config) throws IOException { ClusterManifest cluster = new ClusterManifest(); cluster.setClusterName(clusterName); NodesManifest nodesM = new NodesManifest(); nodesM.setUrl(nodes); cluster.setNodes(nodesM); SoftwareManifest softwareM = new SoftwareManifest(); softwareM.setUrl(software); cluster.setSoftware(softwareM); ConfigManifest configM = new ConfigManifest(); configM.setUrl(config); cluster.setConfig(configM); return clientRunner.sendToController(new CreateClusterCommand(cluster)); } /** * Construct a upgrade cluster command * @param clusterName - Cluster name * @param nodes - Nodes manifest is a url to a XML file which describes the server compositions of the cluster * @param software - Software manifest is a url to a XML file which describes the software compositions of the cluster * @param config - Configuration manifest is a url to a XML file which describes the configuration steps for the cluster * @return * @throws IOException */ public Response upgradeCluster(String clusterName, URL nodes, URL software, URL config) throws IOException { ClusterManifest cluster = new ClusterManifest(); cluster.setClusterName(clusterName); NodesManifest nodesM = new NodesManifest(); nodesM.setUrl(nodes); cluster.setNodes(nodesM); SoftwareManifest softwareM = new SoftwareManifest(); softwareM.setUrl(software); cluster.setSoftware(softwareM); ConfigManifest configM = new ConfigManifest(); configM.setUrl(config); cluster.setConfig(configM); return clientRunner.sendToController(new UpgradeClusterCommand(cluster)); } /** * Construct a delete cluster command * @param clusterName - Cluster name * @param config - Configuration manifest is a url to a XML file which describes the decommission steps for the cluster * @return * @throws IOException */ public Response deleteCluster(String clusterName, URL config) throws IOException { ClusterManifest cluster = new ClusterManifest(); ConfigManifest configM = new ConfigManifest(); configM.setUrl(config); cluster.setConfig(configM); return clientRunner.sendToController(new DeleteClusterCommand(clusterName, cluster)); } /** * Query command status * @param id - Command ID * @return * @throws IOException */ public CommandStatus queryCommandStatus(String id) throws IOException { return clientRunner.queryController(id); } /** * Parse command line arguments and construct HMS command for HMS Client Executor class * @param args */ public void run(String[] args) { BasicParser parser = new BasicParser(); try { CommandLine cl = parser.parse(opt, args); /* Dry run */ boolean dryRun = false; if ( cl.hasOption("t") ) { dryRun = true; } if ( cl.hasOption("q")) { String cmdid = cl.getOptionValue("q"); try { CommandStatus cs = queryCommandStatus(cmdid); if( cl.hasOption("v")) { System.out.println(JAXBUtil.print(cs)); } else { System.out.println("Command ID: "+cmdid); System.out.println("Status: "+cs.getStatus()); System.out.println("Total actions: "+cs.getTotalActions()); System.out.println("Completed actions: "+cs.getCompletedActions()); } } catch(UniformInterfaceException e) { if(e.getResponse().getStatus()==Responses.NOT_FOUND) { System.out.println("Command ID:"+cmdid+" does not exist."); } else { System.out.println("Unknown error occurred, check stack trace."); System.out.println(ExceptionUtil.getStackTrace(e)); } } } else if ( cl.hasOption("delete-command")) { // TODO: Remove command from the system String cmdId = cl.getOptionValue("delete-command"); if (cmdId == null) { throw new RuntimeException("Command ID must be specified for Delete operation"); } // System.out.println(clientRunner.sendToController(new DeleteCommand(cmdId))); } else if ( cl.hasOption("delete-cluster") ) { /* delete a cluster */ String clusterName = cl.getOptionValue("delete-cluster"); if (clusterName == null) { throw new RuntimeException("cluster name must be specified for DELETE operation"); } URL config = new URL(cl.getOptionValue("config-manifest")); if (config == null) { throw new RuntimeException("config manifest must be specified for DELETE operation"); } try { Response response = deleteCluster(clusterName, config); showResponse(response, cl.hasOption("v")); } catch(Throwable e) { showErrors(e); } } else if ( cl.hasOption("create-cluster") ) { /* create a cluster */ String clusterName = cl.getOptionValue("create-cluster"); if (clusterName == null) { throw new RuntimeException("cluster name must be specified for CREATE operation"); } URL nodes = new URL(cl.getOptionValue("nodes")); if (nodes == null) { throw new RuntimeException("nodes manifest must be specified for CREATE operation"); } URL software = new URL(cl.getOptionValue("software")); if (software == null) { throw new RuntimeException("software manifest must be specified for CREATE operation"); } URL config = new URL(cl.getOptionValue("config")); if (config == null) { throw new RuntimeException("config manifest must be specified for CREATE operation"); } Response response = createCluster(clusterName, nodes, software, config); showResponse(response, cl.hasOption("v")); } else if ( cl.hasOption("upgrade-cluster") ) { /* upgrade a cluster */ String clusterName = cl.getOptionValue("upgrade-cluster"); if (clusterName == null) { throw new RuntimeException("cluster name must be specified for CREATE operation"); } URL nodes = new URL(cl.getOptionValue("nodes")); if (nodes == null) { throw new RuntimeException("nodes manifest must be specified for CREATE operation"); } URL software = new URL(cl.getOptionValue("software")); if (software == null) { throw new RuntimeException("software manifest must be specified for CREATE operation"); } URL config = new URL(cl.getOptionValue("config")); if (config == null) { throw new RuntimeException("config manifest must be specified for CREATE operation"); } Response response = upgradeCluster(clusterName, nodes, software, config); showResponse(response, cl.hasOption("v")); } else if ( cl.hasOption("cluster-status") ) { /* check cluster status */ String clusterId = cl.getOptionValue("cluster-status"); if (clusterId == null) { throw new RuntimeException("Cluster path must be specified for cluster-status operation"); } ClusterManifest cm = clientRunner.checkClusterStatus(clusterId); System.out.println(JAXBUtil.print(cm)); } else if ( cl.hasOption("node-status") ) { /* check node status */ String nodepath = cl.getOptionValue("node-status"); if (nodepath == null) { throw new RuntimeException("nodePath must be specified for nodestatus operation"); } MachineState ms = clientRunner.checkNodeStatus(nodepath); System.out.println(JAXBUtil.print(ms)); } else if ( cl.hasOption("help")) { usage(); } else { throw new InvalidActivityException("Invalid arguement."); } } catch (InvalidActivityException e) { usage(); System.out.println("Argument Error: " + e.getMessage()); } catch (Throwable e) { showErrors(e); } } /** * Generic utility to handle error feedback for HMS command line client. * @param e */ private void showErrors(Throwable e) { log.error(ExceptionUtil.getStackTrace(e)); System.out.println("Error in issuing command."); System.out.println(ExceptionUtil.getStackTrace(e)); } /** * Generic utility method to display the response of HMS Controller Rest API. * @param response - Response object from HMS Controller Rest API. * @param verbose - Display response verbosely. * @throws IOException */ private void showResponse(Response response, boolean verbose) throws IOException { if(response.getCode()==0) { System.out.println("Command has been queued. Command ID: "+response.getOutput()); } if(verbose) { System.out.println("Verbose Output:"); System.out.println(JAXBUtil.print(response)); } } /** * Display usage of HMS command line client */ public static void usage() { HelpFormatter f = new HelpFormatter(); f.printHelp("hms client", opt); } public static void main(String[] args) { Client c = new Client(); c.run(args); } }