ape.Main.java Source code

Java tutorial

Introduction

Here is the source code for ape.Main.java

Source

/**
  * Copyright (c) 2012 Yahoo! Inc. All rights reserved.
  * 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. See accompanying LICENSE file.
*/

package ape;

import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Arrays;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * This is the main class that is executed when the Ape jar is run.
 * Parameters are passed in over the command line and parsed by the Apache
 * CLI parser.  The commands are loaded into an Options object using
 * a service loader (see src/main/resources/META-INF/services/ape.ApeCommand for
 * a complete list of commands loaded by the service loader).  See this 
 * class' createOptions method to see other Options that are added or 
 * simply run this program with the -h flag to see the help dialog. 
 * 
 * The version number is also stored in this class.  If it is changed,
 * it must also be changed in the pom.xml.
 * 
 * The log4j configuration is also located in this class.
 * 
 *
 */
public class Main {
    public static boolean VERBOSE = true;
    private static Options opts;
    private static CommandLine line;
    // If the version is modified here, it must also be modified in pom.xml
    private static double VERSION = 0.2;
    private static int cmdN = -1;
    private static int modeN = -1;
    private static ServiceLoader<ApeCommand> loader = ServiceLoader.load(ApeCommand.class);
    private static int MAX_OPTION_LENGTH;

    public static final Logger logger = Logger.getLogger(Main.class.getName());

    public static void main(String[] args) {
        // Creating the Properties object for log4j
        Properties ppt = new Properties();
        ppt.setProperty("log4j.rootLogger", "INFO, appender1");
        ppt.setProperty("log4j.appender.appender1", "org.apache.log4j.DailyRollingFileAppender");
        ppt.setProperty("log4j.appender.appender1.File", "/var/log/ape.log");
        ppt.setProperty("log4j.appender.appender1.DatePattern", ".yyyy-MM-dd");
        ppt.setProperty("log4j.appender.appender1.layout", "org.apache.log4j.PatternLayout");

        // Configuring log4j to use the Properties object created above
        PropertyConfigurator.configure(ppt);

        // Log the current date and time
        logger.info("\n---------------------------------\nStarting time:");
        logTime();

        // Initialize all of the Option objects for each command (these are used by the CLI parser)
        createOptions();

        // There should be an array of strings passed in as an argument (even if it's empty)
        // If we get null, we exit
        if (args == null) {
            System.err
                    .println("Invalid arguments.  main(String[] args) method expected array of strings, got null.");
            logger.info("Invalid arguments.  main(String[] args) method expected array of strings, got null");
            printHelp();
            return;
        }

        // If an empty array is passed in, print the help dialog and exit
        if (args.length == 0) {
            printHelp();
            return;
        }

        // Use the CLI parser to attempt to parse the command into a series of Option objects
        try {
            System.out.println(Arrays.toString(args));
            logger.info(Arrays.toString(args));
            line = getCommand(args);
            //System.out.println(line.toString());   
        } catch (MissingArgumentException e) {
            System.out.println("Missing an argument.  Check your syntax.");
            logger.info("Missing an argument.");
            logger.info("Dumping args array:");
            for (int i = 0; i < args.length; i++) {
                logger.info(i + ": " + args[i]);
            }
            printHelp();
            return;
        } catch (ParseException e) {
            System.out.println("Parsing error, see help dialog:");
            logger.info("Parsing error, see help dialog.");
            printHelp();
            return;
        }

        // Get the array of options that were parsed from the command line 
        Option[] options = line.getOptions();

        if (line.hasOption("v")) {
            MAX_OPTION_LENGTH = 3;
        } else {
            MAX_OPTION_LENGTH = 2;
        }

        if (options == null || options.length > MAX_OPTION_LENGTH || options.length < 1) {
            System.out.println("Too many options");
            logger.info("Too many options");
            printHelp();
            return;
        }

        if (line.hasOption("v")) {
            VERBOSE = true;
            logger.info("Executing Ape verbosely.");
            System.out.println("Executing Ape verbosely");
        }

        //Find which option is cmd, which is -local/-remote, order might be disturbed
        for (int k = 0; k < options.length; k++) {
            if (VERBOSE) {
                System.out.println(options[k]);
                logger.info(options[k]);
            }
            if (!options[k].getOpt().equals("v")) {
                if (options[k].getOpt() == "L" || options[k].getOpt() == "R") {
                    modeN = k;
                } else
                    cmdN = k;
            }
        }

        // If the version flag was in the command, print the version and exit
        if (line.hasOption("V")) {
            logger.info("Printing out current version: " + VERSION);
            System.out.println("ChaosMonkey version: " + VERSION);
            return;
        }

        if (line.hasOption('h') || options.length < 1 || modeN == cmdN || modeN == -1 || cmdN == -1) {
            if (cmdN == -1) {
                System.out.println("Failure commands were not specified.");
                logger.info("Failure commands were not specified.");
            }
            System.out.println("Exiting ...");
            logger.info("Exiting ...");
            printHelp();
            return;
        }

        if (VERBOSE) {
            System.out.println("Mode is " + options[modeN].getLongOpt());

            if (options[modeN].getOpt() == "R") {
                System.out.println("List of Hosts:");
                for (int j = 0; j < line.getOptionValues("R").length; j++) {
                    System.out.println(line.getOptionValues("R")[j]);
                }
            }

            System.out.println("Command is " + options[cmdN].getLongOpt());

            if (line.getOptionValues(options[cmdN].getOpt()) != null) {
                for (int l = 0; l < line.getOptionValues(options[cmdN].getOpt()).length; l++)
                    System.out.println("Command Argument: " + line.getOptionValues(options[cmdN].getOpt())[l]);
            }
        }

        logger.info("Type of Event " + options[cmdN].getLongOpt());

        // Remote command execution
        if (line.hasOption("R")) {
            //go to remote
            String[] passIn = line.getOptionValues("R");
            logger.info("Executing a command remotely");
            logger.info("hosts: ");

            for (int k = 0; k < passIn.length; k++) {
                logger.info(passIn[k]);
            }

            CommunicationInterface r = new PDSHCommunication(options[cmdN].getOpt(),
                    line.getOptionValues(options[cmdN].getOpt()), passIn);
            try {
                // If the command executed successfully
                if (r.execute()) {
                    logger.info("End time");

                    logTime();

                    System.out.println("Running Remote Command Succeeded");
                }
                // If the command exited with an error
                else {
                    System.out.println("Running remote command failed");
                    logger.info("Running remote command failed");
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
            return;

        }
        // Local command execution
        else if (line.hasOption("L")) {
            logger.info("Running Locally");

            ApeCommand ac = ApeCommand.getCommand(options[cmdN].getLongOpt());
            if (ac == null) {
                System.out.println(options[cmdN].getLongOpt() + " is not a valid command.");
                System.out.println(
                        "This can occur if a new command class is added but an entry is not added in the ApeCommand file.");
                System.out.println(
                        "See src/main/resources/META-INF/services/ape.ApeCommand and ensure that the command's class is there.");
                logger.info(options[cmdN].getLongOpt() + " is not a valid command.");

                return;
            }

            try {
                String[] cmdArgs = line.getOptionValues(options[cmdN].getOpt());
                if (ac.exec(cmdArgs)) {
                    System.out.println("Running local command succeeded");
                    logger.info("End time");

                    logTime();

                } else {
                    System.out.println("Running local command failed");
                    logger.info("Running local command failed");
                }
                return;
            } catch (ParseException e) {
                if (Main.VERBOSE) {
                    System.out.println("VERBOSE: A parse exception was thrown.  ");
                    System.out.println(
                            "VERBOSE: Interpreting this as an invalid number of arguments for a particular flag and printing the help dialog.");
                    System.out.println("VERBOSE: Stack trace:");
                    e.printStackTrace();
                    logger.info("VERBOSE: A parse exception was thrown.  ");
                    logger.info(
                            "VERBOSE: Interpreting this as an invalid number of arguments for a particular flag and printing the help dialog.");
                    logger.info("VERBOSE: Stack trace:");
                    logger.info(e);
                }
                System.out.println("Invalid number of arguments.");
                logger.info("Invalid number of arguments");
                printHelp();
            } catch (IOException e) {
                System.out.println("Running local command failed");
                logger.info("Running local command failed");
                e.printStackTrace();
            }
        }
        // If the local or remote flags were not used then print the help dialog
        else {
            printHelp();
        }
    }

    /**
     * This method would parse the array that store all arguments read from StdIn 
     * and return a CommandLine object
     */
    public static CommandLine getCommand(String[] args) throws ParseException {
        if (args == null || args.length < 1 || args[0] == null) {
            printHelp();
            return null;
        }

        CommandLineParser parser = new PosixParser();

        return parser.parse(opts, args);
    }

    /**
     * This method generates all the options, and stores them in opts
     */
    public static void createOptions() {
        //System.out.println("creating options ...");
        Options options = new Options();
        options.addOption("h", "help", false, "Displays this help menu");
        options.addOption("V", "version", false, "Displays the version number");
        options.addOption(OptionBuilder.withValueSeparator().withDescription("Turn on verbose mode")
                .withLongOpt("verbose").create("v"));

        // Adds all of the commands in the service loader to an OptionGroup so that they are all mutually exclusive
        OptionGroup apeCommands = new OptionGroup();
        Iterator<ApeCommand> iter = loader.iterator();
        while (iter.hasNext()) {
            ApeCommand ac = iter.next();
            apeCommands.addOption(ac.getOption());
            //System.out.println(ac.getOption());
        }
        options.addOptionGroup(apeCommands);

        // Makes the local and remote commands mutually exclusive
        OptionGroup remoteOrLocal = new OptionGroup();
        remoteOrLocal.addOption(OptionBuilder.withArgName("HostnameList").hasArgs().withValueSeparator()
                .withDescription("Run commands remotely").withLongOpt("remote").create("R"));
        remoteOrLocal.addOption(OptionBuilder.withArgName("Command").withValueSeparator()
                .withDescription("Run commands locally").withLongOpt("local").create("L"));
        options.addOptionGroup(remoteOrLocal);

        opts = options;
    }

    /**
     * This method logs the current time into the ape.log file
     */
    private static void logTime() {
        // Get a Date object with the current date and time
        Date d = new Date();

        // Log it in a format like so: Aug 11, 2011 11:51:21 AM
        logger.info(DateFormat.getDateTimeInstance().format(d));
    }

    /**
     * This method prints out all the options
     */
    public static void printHelp() {
        // Add a logging message
        logger.info("Printing the help dialog.");

        // Use the Apache CLI library's build in help dialog printer
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("ape [options] ... <failure command>\noptions:", opts);
        System.out.println("command:");
        System.out.println(" -fb <lambda> -k <lambda>\tfork bomb");
        System.out.println(" -kp <lambda> -k <lambda>\tkernel panic");
        System.out.println(" -r <lambda> -k <lambda>\tremount root as read only");
        System.out.println(" -kn <lambda> -k <lambda>\tkill a node process");
        System.out.println(" -dos <lambda> -k <lambda>\tdenial of service by launching 4 bombarding threads");
        System.out.println(" -cb <lambda> -k <lambda>\tcorrupt a random HDFS block");
        System.out.println(" -cf <lambda> -k <lambda>\tcorrupt a file at the given address");
        System.out.println(" -nic <lambda> -k <lambda>\tinterface");
        System.out.println(" -p <lambda> -k <lambda>\tpacket drop");
    }

    /**
     * @return The current version number
     */
    public static double getVersion() {
        return VERSION;
    }

    /**
     * @return The Options object which contains all of the possible options
     */
    public static Options getOptions() {
        return opts;
    }
}