blob: 0590c73f1d52139388c1441bcc5b114f7d7533dd [file] [log] [blame]
/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.ambari.metrics.core.loadsimulator.jmetertest.jmetertest;
import org.apache.commons.io.IOUtils;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.reporters.Summariser;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
public class JmeterTestPlanTask implements Runnable {
private static StandardJMeterEngine jmeterEngine = null;
private final static Logger LOG = LoggerFactory.getLogger(JmeterTestPlanTask.class);
private List<AppGetMetric> appGetMetrics;
private Properties amsJmeterProperties;
private HashTree amsTestPlanTree;
private TestPlan amsTestPlan;
private static final String JMETER_HOME = "loadsimulator";
private static final String JMETER_PROPERTIES_FILE = JMETER_HOME + "/jmeter.properties";
private static final String SAVESERVICE_PROPERTIES_FILE = JMETER_HOME + "/saveservice.properties";
public enum ClientApp {
HOST("HOST"),
NAMENODE("NAMENODE"),
HBASE("HBASE"),
NIMBUS("NIMBUS"),
KAFKA_BROKER("KAFKA_BROKER"),
FLUME_HANDLER("FLUME_HANDLER"),
AMS_HBASE("AMS-HBASE"),
NODEMANAGER("NODEMANAGER"),
RESOURCEMANAGER("RESOURCEMANAGER"),
DATANODE("DATANODE");
private String id;
private ClientApp(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
public JmeterTestPlanTask(List<AppGetMetric> appGetMetrics, Properties amsJmeterProperties) {
this.appGetMetrics = appGetMetrics;
this.amsJmeterProperties = amsJmeterProperties;
amsTestPlanTree = new HashTree();
amsTestPlan = new TestPlan("AMS JMeter Load Test plan");
System.out.println("Starting AMS Jmeter load testing");
}
public void run() {
if (jmeterEngine != null) {
Object[] threadGroups = amsTestPlanTree.getArray(amsTestPlan);
for (Object threadGroupObj : threadGroups) {
if (threadGroupObj instanceof ThreadGroup) {
ThreadGroup threadGroup = (ThreadGroup) threadGroupObj;
threadGroup.stop();
}
}
amsTestPlanTree.clear();
jmeterEngine.askThreadsToStop();
jmeterEngine.stopTest();
JMeterContextService.endTest();
}
//Start the new test plan for the new app.
try {
//Initialize Jmeter essentials
jmeterEngine = new StandardJMeterEngine();
JMeterContextService.getContext().setEngine(jmeterEngine);
//Workaround to supply JMeterUtils with jmeter.prooperties from JAR.
JMeterUtils.setJMeterHome("");
Field f = new JMeterUtils().getClass().getDeclaredField("appProperties");
f.setAccessible(true);
f.set(null, AMSJMeterLoadTest.readProperties(JMETER_PROPERTIES_FILE));
//Copy saveservices.properties file to tmp dir for JMeter to consume.
InputStream inputStream = ClassLoader.getSystemResourceAsStream(SAVESERVICE_PROPERTIES_FILE);
if (inputStream == null) {
inputStream = new FileInputStream(SAVESERVICE_PROPERTIES_FILE);
}
String tmpDir = System.getProperty("java.io.tmpdir");
OutputStream outputStream = new FileOutputStream(tmpDir + "/saveservice.properties");
IOUtils.copy(inputStream, outputStream);
outputStream.close();
JMeterUtils.setProperty("saveservice_properties", tmpDir + "/saveservice.properties");
//Initialize Test plan
amsTestPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());
amsTestPlanTree.add("AMS Test plan", amsTestPlan);
//Choose a random APP to run the perform GET metrics request.
int currentAppIndex = new Random().nextInt(appGetMetrics.size());
//Create ThreadGroup for the App
createThreadGroupHashTree(currentAppIndex, amsJmeterProperties, amsTestPlanTree, amsTestPlan);
//Geneates the JMX file that you can use through the GUI mode.
//SaveService.saveTree(amsTestPlanTree, new FileOutputStream(JMETER_HOME + "/" + "amsTestPlan.jmx"));
//Summarizer output to get test progress in stdout like.
Summariser summariser = null;
String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary");
if (summariserName.length() > 0) {
summariser = new Summariser(summariserName);
}
//Store execution results into a .jtl file
String jmeterLogFile = tmpDir + "/amsJmeterTestResults.jtl";
ResultCollector resultCollector = new ResultCollector(summariser);
resultCollector.setFilename(jmeterLogFile);
amsTestPlanTree.add(amsTestPlanTree.getArray()[0], resultCollector);
jmeterEngine.configure(amsTestPlanTree);
jmeterEngine.run();
LOG.info("AMS Jmeter Test started up successfully");
} catch (Exception ioEx) {
amsTestPlanTree.clear();
jmeterEngine.askThreadsToStop();
jmeterEngine.stopTest();
JMeterContextService.endTest();
LOG.error("Error occurred while running AMS load test : " + ioEx.getMessage());
ioEx.printStackTrace();
}
}
private ConstantTimer createConstantTimer(int delay) {
ConstantTimer timer = new ConstantTimer();
timer.setDelay("" + delay);
return timer;
}
private Map<String, String> getAppSpecificParameters(String app, GetMetricRequestInfo request, Properties amsJmeterProperties) {
Map<String, String> parametersMap = new HashMap<String, String>();
String hostPrefix = amsJmeterProperties.getProperty("host-prefix");
String hostSuffix = amsJmeterProperties.getProperty("host-suffix");
int minHostIndex = Integer.valueOf(amsJmeterProperties.getProperty("min-host-index"));
int numHosts = Integer.valueOf(amsJmeterProperties.getProperty("num-hosts"));
parametersMap.put("appId", app);
if (request.needsTimestamps()) {
long currentTime = System.currentTimeMillis();
long oneHourBack = currentTime - 3600 * 1000;
parametersMap.put("startTime", String.valueOf(oneHourBack));
parametersMap.put("endTime", String.valueOf(currentTime));
}
if (request.needsHost()) {
if (ClientApp.AMS_HBASE.getId().equals(app)) {
parametersMap.put("hostname", amsJmeterProperties.getProperty("ams-host"));
} else if (ClientApp.HOST.getId().equals(app) || ClientApp.NODEMANAGER.getId().equals(app)) {
int randomHost = minHostIndex + new Random().nextInt(numHosts);
parametersMap.put("hostname", hostPrefix + randomHost + hostSuffix);
} else {
parametersMap.put("hostname", hostPrefix + amsJmeterProperties.getProperty(app + "-host") + hostSuffix);
}
}
parametersMap.put("metricNames", request.getMetricStringPayload());
return parametersMap;
}
private void createThreadGroupHashTree(int appIndex, Properties amsJmeterProperties, HashTree amsTestPlanTree, TestPlan amsTestPlan) {
AppGetMetric appGetMetric = appGetMetrics.get(appIndex);
String app = appGetMetric.getApp();
int interval = appGetMetric.getInterval();
//Read and validate AMS information.
String[] amsHostPort = amsJmeterProperties.getProperty("ams-host-port").split(":");
String amsHost = amsHostPort[0];
String amsPath = amsJmeterProperties.getProperty("ams-path");
int amsPort = Integer.valueOf(amsHostPort[1]);
int numLoops = Integer.valueOf(amsJmeterProperties.getProperty("num-get-calls-per-app"));
LoopController loopController = createLoopController(app + " GET loop controller", numLoops, false);
for (GetMetricRequestInfo request : appGetMetric.getMetricRequests()) {
ThreadGroup threadGroup = createThreadGroup(app + " GET threadGroup", 1, 0, loopController);
HashTree threadGroupHashTree = amsTestPlanTree.add(amsTestPlan, threadGroup);
Map<String, String> parametersMap = getAppSpecificParameters(app, request, amsJmeterProperties);
HTTPSampler sampler = createGetSampler("GET " + app + " metrics", amsHost, amsPort, amsPath, null, parametersMap);
if (numLoops > 1) {
threadGroupHashTree.add(createConstantTimer(interval));
}
threadGroupHashTree.add(sampler);
}
}
private HTTPSampler createGetSampler(String name, String domain, int port, String path, String encoding, Map<String, String> parameters) {
HTTPSampler sampler = new HTTPSampler();
sampler.setDomain(domain);
sampler.setPort(port);
sampler.setPath(path);
sampler.setMethod(HTTPConstants.GET);
if (encoding != null)
sampler.setContentEncoding(encoding);
for (Map.Entry<String, String> entry : parameters.entrySet()) {
sampler.addArgument(entry.getKey(), entry.getValue());
}
sampler.setName(name);
return sampler;
}
private LoopController createLoopController(String name, int numLoops, boolean continueForever) {
LoopController loopController = new LoopController();
loopController.setLoops(numLoops);
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
loopController.initialize();
loopController.setContinueForever(continueForever);
loopController.setName(name);
return loopController;
}
private ThreadGroup createThreadGroup(String name, int numThreads, int rampUp, LoopController loopController) {
ThreadGroup threadGroup = new ThreadGroup();
threadGroup.setName(name);
threadGroup.setNumThreads(numThreads);
threadGroup.setRampUp(rampUp);
threadGroup.setSamplerController(loopController);
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
return threadGroup;
}
}