/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id$
*
* Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.harness;
import static com.sun.faban.harness.RunContext.*;
import com.sun.faban.common.Command;
import com.sun.faban.common.CommandHandle;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Element;
/**
* The default benchmark class for use with benchmarks implemented with the
* Faban Driver Framework. This class is designed to be extended if additional
* features are desired. Do not use the DefaultFabanBenchmark if the actual
* driver is not implemented using the Faban Driver Framework. Implement the
* Benchmark interface directly in such cases.
*
* @author Akara Sucharitakul
* @deprecated Replaced by DefaultFabanBenchmark2, this class is provided for
* backward compatibility and is to be removed in the future.
*/
@Deprecated public class DefaultFabanBenchmark implements Benchmark {
private Logger logger = Logger.getLogger(getClass().getName());;
/** The param repository. */
protected ParamRepository params;
/** The agent list. */
protected List<String> agents;
/** The agent hosts. */
protected String[] agentHosts;
/** The map from host to agents. */
protected Map<String, List<String>> hostAgents;
/** Environment to pass to agents when starting. */
protected Map<String, List<String>> agentEnv;
/** Command handle to the master. */
protected CommandHandle masterHandle;
/**
* Allows benchmark to validate the configuration file. Note that no
* execution facility is available during validation.
*
* @throws Exception if any error occurred.
* @see RunContext#exec(com.sun.faban.common.Command)
*/
public void validate() throws Exception {
params = getParamRepository();
// Check and match the hosts and agents
// First, list the drivers in the config file.
agents = params.getAttributeValues(
"fa:runConfig/fd:driverConfig", "name");
// Second, obtain the systems to run the drivers.
agentHosts = params.getTokenizedValue(
"fa:runConfig/fa:hostConfig/fa:host");
hostAgents = new HashMap<String, List<String>>(agentHosts.length + 5);
agentEnv = new HashMap<String, List<String>>();
HashMap<String, Integer> anyHostAgents = new HashMap<String, Integer>();
for (String agentName : agents) {
String qb = "fa:runConfig/fd:driverConfig[@name=\"" + agentName +
"\"]/";
// Obtain the environment needed for the agent type...
List<String> env = params.getParameters(qb + "fd:environment");
if (env != null && env.size() > 0) {
agentEnv.put(agentName, env);
if (logger.isLoggable(Level.FINER)) {
StringBuilder b = new StringBuilder();
b.append("Env for ").append(agentName).append("Agents: ");
for (String envEntry : env) {
b.append('[').append(envEntry).append(']');
}
logger.finer(b.toString());
}
}
// Prepare the agent distribution...
String[] agentSpecs = params.getTokenizedValue(qb + "fd:agents");
switch (agentSpecs.length) {
case 0: // Default to 1 agent
String[] defaultAgentSpecs = { "1" };
agentSpecs = defaultAgentSpecs; // fall thru to case 1.
case 1: // Single value field, could be just count or host:count
if (agentSpecs[0].indexOf(':') < 0) { //Just count
int agentCnt = 0;
try {
agentCnt = Integer.parseInt(agentSpecs[0]);
} catch (NumberFormatException e) {
String msg = "Invalid agents spec " + agentSpecs[0];
ConfigurationException ce =
new ConfigurationException(msg);
logger.log(Level.SEVERE, msg, ce);
throw ce;
}
// Record the count for this agent.
anyHostAgents.put(agentName, agentCnt);
break;
}
default: // One or more host:count
for (String agentSpec : agentSpecs) {
int colIdx = agentSpec.indexOf(':');
// Check the spec for anything odd.
if (colIdx < 1) {
String msg = "Invalid agents spec " + agentSpec;
ConfigurationException ce =
new ConfigurationException(msg);
logger.log(Level.SEVERE, msg, ce);
throw ce;
}
String hostName = agentSpec.substring(0, colIdx);
int agentCnt;
try {
agentCnt = Integer.parseInt(
agentSpec.substring(colIdx + 1));
} catch (NumberFormatException e) {
String msg = "Invalid agents spec " + agentSpec;
ConfigurationException ce =
new ConfigurationException(msg);
logger.log(Level.SEVERE, msg, ce);
throw ce;
}
// Add one entry to the list for each agent.
List<String> agentList = null;
for (int i = 0; i < agentCnt; i++) {
if (agentList == null)
agentList = hostAgents.get(hostName);
if (agentList == null) {
agentList = new ArrayList<String>();
hostAgents.put(hostName, agentList);
}
agentList.add(agentName);
}
}
}
}
// After we got the host specifics done, we'll need to take care of
// distributing the anyhost agents fairly.
if (anyHostAgents.size() > 0) {
// Ensure all hosts are in hostAgents.
for (String hostName : agentHosts)
if (hostAgents.get(hostName) == null)
hostAgents.put(hostName, new ArrayList<String>());
String previousHost = null;
for (Map.Entry<String, Integer> anyHostEntry :
anyHostAgents.entrySet()) {
String agentName = anyHostEntry.getKey();
int cnt = anyHostEntry.getValue();
Set<Map.Entry<String, List<String>>> hostAgentsSet =
hostAgents.entrySet();
// Find the host with minimum agent for each anyHostAgent
for (int i = 0; i < cnt; i++) {
String leastBusyHost = null;
int minAgents = Integer.MAX_VALUE;
// Scan the hostAgents to find the least busy.
for (Map.Entry<String, List<String>> hostAgentsEntry :
hostAgentsSet) {
int agents = hostAgentsEntry.getValue().size();
if (agents < minAgents) {
leastBusyHost = hostAgentsEntry.getKey();
minAgents = agents;
} else if (agents == minAgents) {
// If there is more than one least busy, pick
// the first one that was not previously assigned.
if (leastBusyHost.equals(previousHost))
leastBusyHost = hostAgentsEntry.getKey();
}
}
hostAgents.get(leastBusyHost).add(agentName);
previousHost = leastBusyHost;
}
}
}
// In any case, there is a chance that some hosts are not used.
// we just remove those hosts from hostAgents.
Set<Map.Entry<String, List<String>>> hostAgentsSet =
hostAgents.entrySet();
for (Map.Entry<String, List<String>> hostAgentsEntry : hostAgentsSet)
if (hostAgentsEntry.getValue().size() == 0)
hostAgents.remove(hostAgentsEntry);
// Then we need to make sure our agentHosts strings are accurate.
// We want to keep the order the hosts were entered. So we use
// a LinkedHashSet and insert every host in here.
LinkedHashSet<String> agentHostSet = new LinkedHashSet<String>();
for (String agentHost : agentHosts)
agentHostSet.add(agentHost);
Set<String> hostAgentsKeySet = hostAgents.keySet();
// Now we remove all the hosts that are not used...
agentHostSet.retainAll(hostAgentsKeySet);
// and add what is used and not listed, if any.
agentHostSet.addAll(hostAgentsKeySet);
// Put it back into the array for later use.
agentHosts = agentHostSet.toArray(new String[agentHostSet.size()]);
// Recreate the string from the array.
StringBuilder agentHostsBldr = new StringBuilder();
for (int i = 0; i < agentHosts.length; i++) {
if (i > 0)
agentHostsBldr.append(' ');
agentHostsBldr.append(agentHosts[i]);
}
try {
// Save the new host back the list.
params.setParameter("fa:runConfig/fa:hostConfig/fa:host",
agentHostsBldr.toString());
// Update the output directory to the one assigned by the harness.
Element outputDirNode = (Element)
params.getNode("fa:runConfig/fd:outputDir");
if (outputDirNode == null)
outputDirNode = params.addParameter("fa:runConfig",
"http://faban.sunsource.net/ns/fabandriver", null,
"outputDir");
params.setParameter(outputDirNode, getOutDir());
params.save();
} catch(Exception e) {
logger.log(Level.SEVERE, "Exception updating " + getParamFile(), e);
throw e;
}
}
/**
* This method is called to configure the specific benchmark run
* Tasks done in this method include reading user parameters,
* logging them and initializing various local variables.
*
* @throws Exception if any error occurred.
*/
public void configure() throws Exception {
// No configuration needed.
}
/**
* This method is responsible for starting the benchmark run.
*
* @throws Exception if any error occurred.
*/
public void start() throws Exception {
// Spread the drivers to the systems.
// Start Agents. Other JVM Options like security policy and logging
// properties are added by CmdAgent when starting the java command.
Command c = null;
HashMap<String, Integer> agentIds =
new HashMap<String, Integer>(agents.size());
// Iterate through all the hosts before coming back for the next set
// of agents for the first host. Once we're though all agent sets
// for all hosts, the agentStarted flag will be false and we'll exit
// the loop.
boolean agentStarted = true;
for (int i = 0; agentStarted; i++) {
agentStarted = false;
for (String hostName : agentHosts) {
List<String> agentList = hostAgents.get(hostName);
if (i >= agentList.size())
continue;
String agentType = agentList.get(i);
Integer oldAgentId = agentIds.get(agentType);
int agentId;
if (oldAgentId == null) {
agentIds.put(agentType, 0);
agentId = 0;
} else {
agentId = oldAgentId.intValue() + 1;
agentIds.put(agentType, agentId);
}
logger.info("Starting " + agentType + "Agent[" + agentId +
"] on host " + hostName + '.');
String masterIP = getMasterIP(hostName);
if (masterIP == null) {
masterIP = getMasterIP();
}
Command agent = new Command("com.sun.faban.driver.engine." +
"AgentImpl", agentType, String.valueOf(agentId),
masterIP);
List<String> env = agentEnv.get(agentType);
if (env != null) {
String[] e = new String[env.size()];
e = env.toArray(e);
agent.setEnvironment(e);
}
agent.setSynchronous(false);
java(hostName, agent);
agentStarted = true;
//Wait for the Agents to register
try {
Thread.sleep(500);
} catch(InterruptedException e) {
logger.severe("Exception Sleeping : " + e);
logger.log(Level.FINE, "Exception", e);
}
}
}
//Wait for all the Agents to register
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
logger.severe("Exception Sleeping : " + e);
logger.log(Level.FINE, "Exception", e);
}
// Start the master
c = new Command("-Dbenchmark.config=" + getParamFile(),
"-Dfaban.outputdir.unique=true",
"com.sun.faban.driver.engine.MasterImpl");
c.setSynchronous(false);
masterHandle = java(c);
// Wait until the master gets to rampup before we give back control.
// This will ensure the tools start at correct times.
java(new Command("com.sun.faban.driver.engine.PingMaster", "RAMPUP"));
logger.info("Ramp up started");
}
/**
* This method is responsible for waiting for all commands started and
* run all postprocessing needed.
*
* @throws Exception if any error occurred.
*/
public void end() throws Exception {
// Wait for the master to complete the run.
masterHandle.waitFor();
int exitValue = masterHandle.exitValue();
if (exitValue != 0) {
logger.severe("Master terminated with exit value " + exitValue);
throw new Exception("Driver failed to complete benchmark run");
}
}
/**
* This method aborts the current benchmark run and is
* called when a user asks for a run to be killed.
*
* @throws Exception if any error occurred.
*/
public void kill() throws Exception {
// We don't need to kill off anything here. All processes managed
// by the run context are automatically terminated.
}
}