001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.chaos.util; 019 020import java.io.IOException; 021import java.util.Properties; 022import java.util.Set; 023 024import org.apache.commons.lang3.StringUtils; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.HBaseConfiguration; 027import org.apache.hadoop.hbase.HConstants; 028import org.apache.hadoop.hbase.IntegrationTestingUtility; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.chaos.factories.MonkeyFactory; 031import org.apache.hadoop.hbase.chaos.monkies.ChaosMonkey; 032import org.apache.hadoop.hbase.util.AbstractHBaseTool; 033import org.apache.hadoop.util.ToolRunner; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 037import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 038 039public class ChaosMonkeyRunner extends AbstractHBaseTool { 040 private static final Logger LOG = LoggerFactory.getLogger(ChaosMonkeyRunner.class); 041 042 public static final String MONKEY_LONG_OPT = "monkey"; 043 public static final String CHAOS_MONKEY_PROPS = "monkeyProps"; 044 public static final String TABLE_NAME_OPT = "tableName"; 045 public static final String FAMILY_NAME_OPT = "familyName"; 046 047 protected IntegrationTestingUtility util; 048 protected ChaosMonkey monkey; 049 protected String monkeyToUse; 050 protected Properties monkeyProps; 051 protected boolean noClusterCleanUp = false; 052 private String tableName = "ChaosMonkeyRunner.tableName"; 053 private String familyName = "ChaosMonkeyRunner.familyName"; 054 055 @Override 056 public void addOptions() { 057 addOptWithArg("m", MONKEY_LONG_OPT, "Which chaos monkey to run"); 058 addOptWithArg(CHAOS_MONKEY_PROPS, "The properties file for specifying chaos " 059 + "monkey properties."); 060 addOptWithArg(TABLE_NAME_OPT, "Table name in the test to run chaos monkey against"); 061 addOptWithArg(FAMILY_NAME_OPT, "Family name in the test to run chaos monkey against"); 062 } 063 064 @Override 065 protected void processOptions(CommandLine cmd) { 066 if (cmd.hasOption(MONKEY_LONG_OPT)) { 067 monkeyToUse = cmd.getOptionValue(MONKEY_LONG_OPT); 068 } 069 monkeyProps = new Properties(); 070 if (cmd.hasOption(CHAOS_MONKEY_PROPS)) { 071 String chaosMonkeyPropsFile = cmd.getOptionValue(CHAOS_MONKEY_PROPS); 072 if (StringUtils.isNotEmpty(chaosMonkeyPropsFile)) { 073 try { 074 monkeyProps.load(this.getClass().getClassLoader() 075 .getResourceAsStream(chaosMonkeyPropsFile)); 076 } catch (IOException e) { 077 LOG.warn(e.toString(), e); 078 System.exit(EXIT_FAILURE); 079 } 080 } 081 } 082 if (cmd.hasOption(TABLE_NAME_OPT)) { 083 this.tableName = cmd.getOptionValue(TABLE_NAME_OPT); 084 } 085 if (cmd.hasOption(FAMILY_NAME_OPT)) { 086 this.familyName = cmd.getOptionValue(FAMILY_NAME_OPT); 087 } 088 } 089 090 @Override 091 protected int doWork() throws Exception { 092 setUpCluster(); 093 getAndStartMonkey(); 094 while (!monkey.isStopped()) { 095 // loop here until got killed 096 try { 097 // TODO: make sleep time configurable 098 Thread.sleep(5000); // 5 seconds 099 } catch (InterruptedException ite) { 100 // Chaos monkeys got interrupted. 101 // It is ok to stop monkeys and exit. 102 monkey.stop("Interruption occurred."); 103 break; 104 } 105 } 106 monkey.waitForStop(); 107 return 0; 108 } 109 110 public void stopRunner() { 111 if (monkey != null) { 112 monkey.stop("Program Control"); 113 } 114 } 115 116 public void setUpCluster() throws Exception { 117 util = getTestingUtil(getConf()); 118 boolean isDistributed = isDistributedCluster(getConf()); 119 if (isDistributed) { 120 util.createDistributedHBaseCluster(); 121 util.checkNodeCount(1);// make sure there's at least 1 alive rs 122 } else { 123 throw new RuntimeException("ChaosMonkeyRunner must run against a distributed cluster," 124 + " please check and point to the right configuration dir"); 125 } 126 this.setConf(util.getConfiguration()); 127 } 128 129 private boolean isDistributedCluster(Configuration conf) { 130 return conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, false); 131 } 132 133 public void getAndStartMonkey() throws Exception { 134 util = getTestingUtil(getConf()); 135 MonkeyFactory fact = MonkeyFactory.getFactory(monkeyToUse); 136 if (fact == null) { 137 fact = getDefaultMonkeyFactory(); 138 } 139 monkey = 140 fact.setUtil(util).setTableName(getTablename()).setProperties(monkeyProps) 141 .setColumnFamilies(getColumnFamilies()).build(); 142 monkey.start(); 143 } 144 145 protected IntegrationTestingUtility getTestingUtil(Configuration conf) { 146 if (this.util == null) { 147 if (conf == null) { 148 this.util = new IntegrationTestingUtility(); 149 this.setConf(util.getConfiguration()); 150 } else { 151 this.util = new IntegrationTestingUtility(conf); 152 } 153 } 154 return util; 155 } 156 157 protected MonkeyFactory getDefaultMonkeyFactory() { 158 // Run with slow deterministic monkey by default 159 return MonkeyFactory.getFactory(MonkeyFactory.SLOW_DETERMINISTIC); 160 } 161 162 public TableName getTablename() { 163 return TableName.valueOf(tableName); 164 } 165 166 protected Set<String> getColumnFamilies() { 167 return Sets.newHashSet(familyName); 168 } 169 170 /* 171 * If caller wants to add config parameters contained in a file, the path of conf file 172 * can be passed as the first two arguments like this: 173 * -c <path-to-conf> 174 */ 175 public static void main(String[] args) throws Exception { 176 Configuration conf = HBaseConfiguration.create(); 177 String[] actualArgs = args; 178 if (args.length > 0 && "-c".equals(args[0])) { 179 int argCount = args.length - 2; 180 if (argCount < 0) { 181 throw new IllegalArgumentException("Missing path for -c parameter"); 182 } 183 // load the resource specified by the second parameter 184 conf.addResource(args[1]); 185 actualArgs = new String[argCount]; 186 System.arraycopy(args, 2, actualArgs, 0, argCount); 187 } 188 IntegrationTestingUtility.setUseDistributedCluster(conf); 189 int ret = ToolRunner.run(conf, new ChaosMonkeyRunner(), actualArgs); 190 System.exit(ret); 191 } 192 193}