diff --git a/dev/cosbench-config/bin/com/intel/cosbench/config/castor/stage-mapping.xml b/dev/cosbench-config/bin/com/intel/cosbench/config/castor/stage-mapping.xml index 12024363..805df753 100644 --- a/dev/cosbench-config/bin/com/intel/cosbench/config/castor/stage-mapping.xml +++ b/dev/cosbench-config/bin/com/intel/cosbench/config/castor/stage-mapping.xml @@ -13,6 +13,10 @@ + + + + diff --git a/dev/cosbench-config/bin/com/intel/cosbench/config/castor/workload-mapping.xml b/dev/cosbench-config/bin/com/intel/cosbench/config/castor/workload-mapping.xml index 0c1c84a4..78873a42 100644 --- a/dev/cosbench-config/bin/com/intel/cosbench/config/castor/workload-mapping.xml +++ b/dev/cosbench-config/bin/com/intel/cosbench/config/castor/workload-mapping.xml @@ -12,6 +12,10 @@ + + + + diff --git a/dev/cosbench-config/src/com/intel/cosbench/config/Stage.java b/dev/cosbench-config/src/com/intel/cosbench/config/Stage.java index c6ab71ec..18ddcab1 100644 --- a/dev/cosbench-config/src/com/intel/cosbench/config/Stage.java +++ b/dev/cosbench-config/src/com/intel/cosbench/config/Stage.java @@ -32,7 +32,8 @@ public class Stage implements Iterable { private String name; private int closuredelay; - private Auth auth; + private String trigger=null; + private Auth auth; private Storage storage; private List works; @@ -59,6 +60,14 @@ public void setName(String name) { public int getClosuredelay() { return closuredelay; } + + public String getTrigger() { + return trigger; + } + + public void setTrigger(String trigger) { + this.trigger = trigger; + } public void setClosuredelay(int closuredelay) { if (closuredelay < 0) diff --git a/dev/cosbench-config/src/com/intel/cosbench/config/Work.java b/dev/cosbench-config/src/com/intel/cosbench/config/Work.java index 5b254abf..919e2448 100644 --- a/dev/cosbench-config/src/com/intel/cosbench/config/Work.java +++ b/dev/cosbench-config/src/com/intel/cosbench/config/Work.java @@ -39,7 +39,7 @@ public class Work implements Iterable { private int runtime = 0; private int rampup = 0; private int rampdown = 0; - private int afr = 200000; /* acceptable failure ratio, the unit is samples per one million, + private int afr = -1; /* acceptable failure ratio, the unit is samples per one million, * default is 200000 for normal work, and 0 for init/prepare/cleanup/dispose/delay work */ private int totalOps = 0; private long totalBytes = 0; @@ -254,7 +254,7 @@ private void toPrepareWork() { name = "prepare"; setDivision("object"); setRuntime(0); - setAfr(0); + setDefaultAfr(0); setTotalBytes(0); setTotalOps(getWorkers()); Operation op = new Operation(); @@ -274,7 +274,7 @@ private void toCleanupWork() { name = "cleanup"; setDivision("object"); setRuntime(0); - setAfr(0); + setDefaultAfr(0); setTotalBytes(0); setTotalOps(getWorkers()); Operation op = new Operation(); @@ -294,7 +294,7 @@ private void toInitWork() { name = "init"; setDivision("container"); setRuntime(0); - setAfr(0); + setDefaultAfr(0); setTotalBytes(0); setTotalOps(getWorkers()); Operation op = new Operation(); @@ -310,7 +310,7 @@ private void toDisposeWork() { name = "dispose"; setDivision("container"); setRuntime(0); - setAfr(0); + setDefaultAfr(0); setTotalBytes(0); setTotalOps(getWorkers()); Operation op = new Operation(); @@ -326,7 +326,7 @@ public void toDelayWork() { name = "delay"; setDivision("none"); setRuntime(0); - setAfr(0); + setDefaultAfr(0); setTotalBytes(0); setWorkers(1); setTotalOps(getWorkers()); @@ -336,6 +336,11 @@ public void toDelayWork() { op.setConfig(""); setOperations(Collections.singletonList(op)); } + + private void setDefaultAfr(int def) { + if (afr < 0) + setAfr(def); + } public void validate() { if (type.equals("prepare")) @@ -348,6 +353,8 @@ else if (type.equals("dispose")) toDisposeWork(); else if (type.equals("delay")) toDelayWork(); + else + setDefaultAfr(200000); setName(getName()); setWorkers(getWorkers()); if (runtime == 0 && totalOps == 0 && totalBytes == 0) diff --git a/dev/cosbench-config/src/com/intel/cosbench/config/Workload.java b/dev/cosbench-config/src/com/intel/cosbench/config/Workload.java index 3b387440..1872e2f6 100644 --- a/dev/cosbench-config/src/com/intel/cosbench/config/Workload.java +++ b/dev/cosbench-config/src/com/intel/cosbench/config/Workload.java @@ -33,6 +33,7 @@ public class Workload { private String name; private String description; + private String trigger=null; private Auth auth = DEFAULT_AUTH; private Storage storage = DEFAULT_STORAGE; private Workflow workflow; @@ -59,6 +60,14 @@ public void setDescription(String description) { /* description might be empty */ this.description = description; } + + public String getTrigger() { + return trigger; + } + + public void setTrigger(String trigger) { + this.trigger = trigger; + } public Auth getAuth() { return auth; diff --git a/dev/cosbench-config/src/com/intel/cosbench/config/castor/stage-mapping.xml b/dev/cosbench-config/src/com/intel/cosbench/config/castor/stage-mapping.xml index 12024363..805df753 100644 --- a/dev/cosbench-config/src/com/intel/cosbench/config/castor/stage-mapping.xml +++ b/dev/cosbench-config/src/com/intel/cosbench/config/castor/stage-mapping.xml @@ -13,6 +13,10 @@ + + + + diff --git a/dev/cosbench-config/src/com/intel/cosbench/config/castor/workload-mapping.xml b/dev/cosbench-config/src/com/intel/cosbench/config/castor/workload-mapping.xml index 0c1c84a4..78873a42 100644 --- a/dev/cosbench-config/src/com/intel/cosbench/config/castor/workload-mapping.xml +++ b/dev/cosbench-config/src/com/intel/cosbench/config/castor/workload-mapping.xml @@ -12,6 +12,10 @@ + + + + diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/model/DriverContext.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/model/DriverContext.java index e518a325..381f6921 100644 --- a/dev/cosbench-controller/src/com/intel/cosbench/controller/model/DriverContext.java +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/model/DriverContext.java @@ -17,6 +17,9 @@ package com.intel.cosbench.controller.model; +import java.util.HashMap; +import java.util.Map; + import com.intel.cosbench.model.DriverInfo; import com.intel.cosbench.utils.MapRegistry; @@ -31,8 +34,9 @@ public class DriverContext implements DriverInfo, MapRegistry.Item { private String name; private String url; private boolean aliveState; + private Map pIDMap = new HashMap(); - public DriverContext() { + public DriverContext() { /* empty */ } @@ -64,4 +68,16 @@ public boolean getAliveState(){ return aliveState; } + + public String getPIDMap(String scriptName) { + String pid = pIDMap.remove(scriptName); + return (pid == null) ? "0" : pid; + } + + public void putPIDMap(String scriptName, String pid) { + if (pid == null) + pIDMap.put(scriptName, "0"); + pIDMap.put(scriptName, pid); + } + } diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/service/TriggerRunner.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/service/TriggerRunner.java new file mode 100644 index 00000000..95bbaa8b --- /dev/null +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/service/TriggerRunner.java @@ -0,0 +1,53 @@ +package com.intel.cosbench.controller.service; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.intel.cosbench.controller.model.DriverRegistry; +import com.intel.cosbench.controller.tasklet.Tasklet; +import com.intel.cosbench.controller.tasklet.Tasklets; +import com.intel.cosbench.log.LogFactory; +import com.intel.cosbench.log.Logger; + +public class TriggerRunner { + DriverRegistry registry; + private ExecutorService executor; + private static final Logger LOGGER = LogFactory.getSystemLogger(); + + public TriggerRunner(DriverRegistry registry) { + this.registry = registry; + createExecutor(); + } + + public void runTrigger(boolean option, String trigger, String wid) { + List tasklets = Tasklets.newTriggers(trigger, registry, option, wid); + executeTasklets(tasklets, option); + dispose();//shutdown too quick? + } + + private void createExecutor() { + int taskCount = registry.getSize(); + executor = Executors.newFixedThreadPool(taskCount); + } + + public void dispose() { + if (executor != null) + executor.shutdown(); + executor = null; + } + + private void executeTasklets(List tasklets, boolean option) { + int num = tasklets.size(); + LOGGER.debug("begin to execute {}-trigger tasklets, {} in total", + option ? "enable" : "kill", num); + try { + executor.invokeAll(tasklets); + } catch (InterruptedException e) { + LOGGER.debug("{}-trigger tasklets have interrupted", + option ? "enable" : "kill", num); + return; //no return is ok? + } + LOGGER.debug("all {} trigger tasklets have finished execution", num); + } +} diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/service/WorkloadProcessor.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/service/WorkloadProcessor.java index fbf70c51..26f54538 100644 --- a/dev/cosbench-controller/src/com/intel/cosbench/controller/service/WorkloadProcessor.java +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/service/WorkloadProcessor.java @@ -149,11 +149,14 @@ private void processWorkload() throws InterruptedException { workloadContext.setState(PROCESSING); workloadContext.setStartDate(new Date()); Iterator iter = queue.iterator(); + String trigger = workloadContext.getWorkload().getTrigger(); + executeTrigger(trigger, true); while (iter.hasNext()) { StageContext stageContext = iter.next(); iter.remove(); runStage(stageContext); } + executeTrigger(trigger, false); workloadContext.setStopDate(new Date()); workloadContext.setCurrentStage(null); for (StageContext stageContext : workloadContext.getStageRegistry() @@ -204,7 +207,6 @@ private void runStage(StageContext stageContext) throws InterruptedException { if(closuredelay > 0) executeDelay(stageContext, closuredelay); } - LOGGER.info("successfully ran stage {}", id); } @@ -222,12 +224,16 @@ private void executeStage(StageContext stageContext) { StageRunner runner = createStageRunner(stageContext); StageChecker checker = createStageChecker(stageContext); StageCallable[] callables = new StageCallable[] { runner, checker }; + String trigger = stageContext.getStage().getTrigger(); + executeTrigger(trigger, true); try { executor.invokeAll(Arrays.asList(callables)); } catch (InterruptedException ie) { + executeTrigger(trigger, false); throw new CancelledException(); // workload cancelled } runner.dispose(); // early dispose runner + executeTrigger(trigger, false); if (!stageContext.getState().equals(StageState.TERMINATED)) return; String id = stageContext.getId(); @@ -254,10 +260,18 @@ private void terminateWorkload() { LOGGER.info("begin to terminate workload {}", id); for (StageContext stageContext : queue) stageContext.setState(StageState.ABORTED); + executeTrigger(workloadContext.getWorkload().getTrigger(), false); workloadContext.setStopDate(new Date()); workloadContext.setState(TERMINATED); LOGGER.info("successfully terminated workload {}", id); } + + private void executeTrigger(String trigger, boolean isEnable) { + if (trigger == null || trigger.isEmpty()) + return; + TriggerRunner runner = new TriggerRunner(controllerContext.getDriverRegistry()); + runner.runTrigger(isEnable, trigger, workloadContext.getId()); + } public void cancel() { String id = workloadContext.getId(); @@ -310,6 +324,7 @@ private void cancelWorkload() { */ for (StageContext stageContext : queue) stageContext.setState(StageState.CANCELLED); + executeTrigger(workloadContext.getWorkload().getTrigger(), false); workloadContext.setStopDate(new Date()); workloadContext.setState(CANCELLED); LOGGER.info("successfully cancelled workload {}", id); diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Tasklets.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Tasklets.java index 97215780..6de0e18f 100644 --- a/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Tasklets.java +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Tasklets.java @@ -78,5 +78,13 @@ public static List newAborters(TaskRegistry tasks) { } return result; } + + public static List newTriggers(String trigger, DriverRegistry registry, boolean option, String wid) { + List result = new ArrayList(); + for (DriverContext driver : registry) { + result.add(new Trigger(driver, trigger, option, wid)); + } + return result; + } } diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Trigger.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Trigger.java new file mode 100644 index 00000000..b24a50c8 --- /dev/null +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/Trigger.java @@ -0,0 +1,52 @@ +package com.intel.cosbench.controller.tasklet; + +import org.apache.commons.lang.StringUtils; + +import com.intel.cosbench.controller.model.DriverContext; +import com.intel.cosbench.protocol.TriggerResponse; + + +public class Trigger extends TriggerHttpTasklet { + public Trigger(DriverContext driver, String trigger, boolean option, String wid) { + super(driver, trigger, option, wid); + } + + @Override + public void execute() { + initHttpClient(); + initObjectMapper(); + String content = getCmdLine(); + if (content == null || content.isEmpty()) + return; + issueCommand("trigger", content); + try { + closeHttpClient(); + } catch (Exception e) { + LOGGER.error("unexpected exception", e); + } + } + + private String getCmdLine() { + trigger.replace(" ", ""); + int idxLeft = StringUtils.indexOf(trigger, '('); + int idxRight = StringUtils.indexOf(trigger, ')'); + if (idxLeft < 3 || ( idxRight != trigger.length()-1)){ + LOGGER.error("can't enable trigger, the format is illegal!"); + return null; + } + scriptName = StringUtils.left(trigger, idxLeft); + String argStr = StringUtils.substring(trigger, idxLeft+1, idxRight); + return isEnable ? ("enableTrigger," + scriptName + "," + argStr + "," + wID) + : ("killTrigger," + driver.getPIDMap(scriptName) + "," + scriptName + "," + wID); + } + + @Override + protected void handleResponse(TriggerResponse response) { + if (!isEnable) { + driver.putPIDMap(scriptName, "0"); + return; + } + driver.putPIDMap(scriptName, response.getPID()); + } + +} diff --git a/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/TriggerHttpTasklet.java b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/TriggerHttpTasklet.java new file mode 100644 index 00000000..1a7480cb --- /dev/null +++ b/dev/cosbench-controller/src/com/intel/cosbench/controller/tasklet/TriggerHttpTasklet.java @@ -0,0 +1,150 @@ +package com.intel.cosbench.controller.tasklet; + +import java.io.IOException; +import java.net.SocketTimeoutException; + +import org.apache.commons.lang.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.DeserializationConfig.Feature; + +import com.intel.cosbench.client.http.HttpClientUtil; +import com.intel.cosbench.controller.model.DriverContext; +import com.intel.cosbench.log.LogFactory; +import com.intel.cosbench.log.Logger; +import com.intel.cosbench.protocol.TriggerResponse; +import com.intel.cosbench.service.UnexpectedException; + +abstract class TriggerHttpTasklet implements Tasklet{ + private transient HttpClient httpClient; + private transient ObjectMapper mapper; + protected DriverContext driver; + protected String trigger = null; + protected boolean isEnable; //true=enableTrigger; false=killTrigger + protected String wID = null; + protected String scriptName = null; + + Class clazz = TriggerResponse.class; + + private static final int TIMEOUT = 300 * 1000; + protected static final Logger LOGGER = LogFactory.getSystemLogger(); + + protected abstract void execute(); + protected abstract void handleResponse(TriggerResponse response); + + public TriggerHttpTasklet(DriverContext driver, String trigger, boolean option, String wid) { + this.driver = driver; + this.trigger = trigger; + this.isEnable = option; + this.wID = wid; + } + + protected void initObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + DeserializationConfig config = mapper.copyDeserializationConfig(); + config.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES); + mapper.setDeserializationConfig(config); + this.mapper = mapper; + } + + protected void initHttpClient() { + HttpClient client = HttpClientUtil.createHttpClient(TIMEOUT); + this.httpClient = client; + } + + protected synchronized void closeHttpClient() { + HttpClient client = this.httpClient; + HttpClientUtil.disposeHttpClient(client); + } + + private String issueHttpRequest(String command, String content) { + String url = driver.getUrl() + "/i/" + command + ".command"; + HttpClient client = this.httpClient; + HttpPost request = prepareRequest(content, url); + String body = null; + try { + HttpResponse response = client.execute(request); + body = fetchResponseBody(response); + } catch (SocketTimeoutException ste) { + LOGGER.error("fail to POST driver while execute trigger", ste); + } catch (ConnectTimeoutException cte) { + LOGGER.error("fail to POST driver while execute trigger", cte); + } catch (Exception e) { + LOGGER.error("fail to POST driver while execute trigger", e); + } + return body; // HTTP response body retrieved + } + + private static HttpPost prepareRequest(String content, String url) { + HttpPost POST = new HttpPost(url); + try { + if (StringUtils.isNotEmpty(content)) + POST.setEntity(new StringEntity(content)); + } catch (Exception e) { + throw new UnexpectedException(e); // will not happen + } + if (content != null && content.length() > 0) + if (!content.startsWith("> ] - {} -> {}", content, url); + else + LOGGER.debug("[ >> ] - [xml-content] -> {}", url); + else + LOGGER.debug("[ >> ] - [empty-body] -> {}", url); + return POST; // HTTP request prepared + } + + private static String fetchResponseBody(HttpResponse response) + throws IOException { + String body = null; + HttpEntity entity = response.getEntity(); + StatusLine status = response.getStatusLine(); + try { + body = EntityUtils.toString(entity); + } finally { + EntityUtils.consume(entity); + } + if (body.length() < 2048) + LOGGER.debug("[ << ] - {} {}", status, body); + else + LOGGER.debug("[ << ] - {} [body-omitted]", status); + return body; // the response body + } + + protected void issueCommand(String command, String content) { + TriggerResponse response = null; + String body = issueHttpRequest(command, content); + try { + response = this.mapper.readValue(body, clazz); + } catch (Exception e) { + LOGGER.error("cannot parse TriggerResponse body", e); + return; + } + if (!response.isSucc()) { + String msg = "driver report error: HTTP {} - {}"; + LOGGER.error(msg, response.getCode(), response.getError()); + return; + } + handleResponse(response); + } + + @Override + public Tasklet call() { + try { + execute(); + LOGGER.debug("{}-trigger executes normally", isEnable ? "enable" : "kill"); + } catch (Exception e) { + LOGGER.error("unexpected exception of trigger", e); + } + return this; /* okay -- done */ + } + +} + diff --git a/dev/cosbench-core/src/com/intel/cosbench/protocol/TriggerResponse.java b/dev/cosbench-core/src/com/intel/cosbench/protocol/TriggerResponse.java new file mode 100644 index 00000000..729f3ced --- /dev/null +++ b/dev/cosbench-core/src/com/intel/cosbench/protocol/TriggerResponse.java @@ -0,0 +1,37 @@ +/** + +Copyright 2013 Intel Corporation, 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. +*/ + + +package com.intel.cosbench.protocol; + +public class TriggerResponse extends Response{ + private String PID;//int(check)/string default + + public TriggerResponse() { + /*empty*/ + } + + public String getPID() { + return PID; + } + + public void setPID(String pID) { + //check + PID = pID; + } + +} diff --git a/dev/cosbench-driver-web/WEB-INF/spring/driver-handler-context.xml b/dev/cosbench-driver-web/WEB-INF/spring/driver-handler-context.xml index 1ffc0a09..0dbd05eb 100644 --- a/dev/cosbench-driver-web/WEB-INF/spring/driver-handler-context.xml +++ b/dev/cosbench-driver-web/WEB-INF/spring/driver-handler-context.xml @@ -40,6 +40,11 @@ class="com.intel.cosbench.driver.handler.AbortHandler"> + + + + diff --git a/dev/cosbench-driver-web/WEB-INF/spring/driver-web-context.xml b/dev/cosbench-driver-web/WEB-INF/spring/driver-web-context.xml index eaf081f5..77f1b1ac 100644 --- a/dev/cosbench-driver-web/WEB-INF/spring/driver-web-context.xml +++ b/dev/cosbench-driver-web/WEB-INF/spring/driver-web-context.xml @@ -50,6 +50,11 @@ class="com.intel.cosbench.driver.web.AbortMissionController"> + + + + diff --git a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/MissionHandler.java b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/MissionHandler.java index 9f7bc69d..28295754 100644 --- a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/MissionHandler.java +++ b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/MissionHandler.java @@ -21,6 +21,8 @@ import javax.servlet.http.*; +import com.intel.cosbench.log.LogFactory; +import com.intel.cosbench.log.Logger; import com.intel.cosbench.model.MissionInfo; import com.intel.cosbench.protocol.Response; import com.intel.cosbench.service.DriverService; @@ -29,6 +31,7 @@ abstract class MissionHandler extends AbstractCommandHandler { protected DriverService driver; + protected static final Logger LOGGER = LogFactory.getSystemLogger(); public void setDriver(DriverService driver) { this.driver = driver; diff --git a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/PingHandler.java b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/PingHandler.java index 306c7a37..db9680f3 100644 --- a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/PingHandler.java +++ b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/PingHandler.java @@ -63,9 +63,11 @@ private void setSysTime(long ctrTime) throws IOException { String[] cmd = {"date", "-s", dateTime.format(new Date(ctrTime))}; String osType = System.getProperty("os.name").toLowerCase(); if (osType.contains("linux")) { + LOGGER.debug("setting system time {} on driver {}", ctrTime, driver.getDriverInfo().getName()); Runtime.getRuntime().exec(cmd); } else { - /* skip for non linux system */ + LOGGER.warn("os type on driver {} is {}!", + driver.getDriverInfo().getName(), osType); } } diff --git a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/TriggerHandler.java b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/TriggerHandler.java new file mode 100644 index 00000000..38f05576 --- /dev/null +++ b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/handler/TriggerHandler.java @@ -0,0 +1,244 @@ +/** + +Copyright 2013 Intel Corporation, 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. +*/ + +package com.intel.cosbench.driver.handler; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; +import com.intel.cosbench.protocol.Response; +import com.intel.cosbench.protocol.TriggerResponse; +import com.intel.cosbench.service.DriverService; + +public class TriggerHandler extends AbstractCommandHandler { + + private int currPID = 0; + private int xferPID = 0; + private boolean isEnable = false; + protected static String scriptsDir = "scripts/"; + protected DriverService driver; + private String wID = null; + + public void setDriver(DriverService driver) { + this.driver = driver; + } + + @Override + protected Response process(HttpServletRequest req, HttpServletResponse res) + throws Exception { + Scanner scanner = new Scanner(req.getInputStream()); + String trigger = getTrigger(scanner); + runTrigger(trigger); + + return createResponse(); + } + + private String getTrigger(Scanner scanner) { + if (!scanner.hasNext()) + LOGGER.error("bad request exception"); + String trigger = scanner.next(); + if (trigger == null) + LOGGER.error("no found exception"); + return trigger; + } + + private void runTrigger(String trigger) { + String cmdLine = getCmdLine(trigger); + if (cmdLine == null || cmdLine.isEmpty()) { + LOGGER.error("trigger command line is empty!"); + return; + } + LOGGER.debug("executing trigger command line : {}", cmdLine); + String osType = System.getProperty("os.name").toLowerCase(); + if (osType.contains("linux")) { + try { + Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmdLine}); + getPID(process); + } catch (Exception e) { + LOGGER.error("execute trigger command failed!"); + return; + } + } else { + LOGGER.warn("can not execute trigger, the OS type({}) isn't linux!", osType); + return; + } + if (!isEnable) { + if (!isTriggerKilled(currPID)) { + killByPID(currPID); + } + if (!isTriggerKilled(xferPID)) + killByPID(xferPID); + } + } + + private String getCmdLine(String trigger) { + String cmdLine = null; + String fileName = null; + + trigger.replace(" ", ""); + String[] triggerArr = StringUtils.split(trigger, ','); + if (triggerArr == null) { + LOGGER.warn("trigger command array is empty!"); + return null; + } + if (triggerArr[0].equals("killTrigger")) { + this.isEnable = false; + if (triggerArr.length != 4) { + LOGGER.error("kill-trigger command line is illegal!"); + return null; + } + xferPID = parsePID(triggerArr[1]); + fileName = triggerArr[2]; + this.wID = triggerArr[3]; + cmdLine = "/bin/sh " + scriptsDir + fileName + " -k"; + } else if (triggerArr[0].equals("enableTrigger")) { + this.isEnable = true; + fileName = triggerArr[1]; + cmdLine = "/bin/sh " + scriptsDir + fileName; + for (int i = 2; i < triggerArr.length - 1; i++) + cmdLine += " " + triggerArr[i]; + this.wID = triggerArr[triggerArr.length-1]; + } else { + LOGGER.error("trigger command line is illegal!"); + return null; + } + String filePath = scriptsDir + fileName; + File tempPath = new File(filePath); + if (!tempPath.exists() || !tempPath.isFile()) { + LOGGER.error("trigger file {} dosen't exist in {}!", filePath); + return null; + } + cmdLine += " >> " + scriptsDir + "log/" + wID + "_" + fileName + ".log"; + return cmdLine; + } + + private int parsePID(String str) { + int num = 0; + try { + num = Integer.parseInt(str); + } catch (NumberFormatException e) { + LOGGER.error("can not parse PID from String to Int {}!", str); + } + return num; + } + + private void getPID(Process process) { + Field field = null; + if (!process.getClass().getName().equals("java.lang.UNIXProcess")) { + LOGGER.error("failed to get PID by {}", process.getClass().getName()); + return; + } + try { + field = process.getClass().getDeclaredField("pid"); + field.setAccessible(true); + currPID = (Integer) field.get(process); + } catch (Exception e) { + LOGGER.error("get PID failed!"); + } + if (isEnable) + xferPID = currPID; + LOGGER.debug("current PID is: {}", currPID); + } + + private boolean isTriggerKilled(int pid) { + List pids = getAllPID(pid); + if (pids != null && pids.size() >= 1) + return false; + return true; + } + + private void killByPID(int pid) { + List pidList = getAllPID(pid); + if (pidList == null || pidList.isEmpty()) { + //LOGGER.debug("pid {} have been killed", pid); + return; + } + if (pidList.size() == 1) { + //LOGGER.debug("pid {} have no child process", pid); + } else { + //LOGGER.debug("pid {} have child process {}", pid, pidList); + for (int i = 1; i < pidList.size(); i++) { + killByPID(pidList.get(i)); + } + } + runKill9(pid); + } + + private List getAllPID(int pid) { + if (pid <= 0) { + LOGGER.warn("pid is illegal: {}", pid); + return null; + } + List pidList = new ArrayList(); + String cmdLine = "ps -ef | grep " + pid + " | awk '{print $2}'"; + try { + Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmdLine}); + InputStream is = process.getInputStream(); + BufferedReader buff = new BufferedReader(new InputStreamReader(is)); + + String line = null; + while ((line = buff.readLine()) != null) { + int tmp = 0; + if ((tmp = parsePID(line.trim())) <= 0){ + LOGGER.error("buffer line is illegal: {}", line); + continue; + } + //LOGGER.debug("child pid of {} : {}", pid, tmp); + pidList.add(new Integer(tmp)); + } + process.waitFor(); + } catch (Exception e) { + LOGGER.error("get all PID failed!"); + return null; + } + if (pidList == null || pidList.isEmpty()) + return null; + pidList.remove(pidList.size()-1); + pidList.remove(pidList.size()-1); + return pidList; + } + + private void runKill9(int pid) { + String osType = System.getProperty("os.name").toLowerCase(); + if (osType.contains("linux")) { + try { + Runtime.getRuntime().exec(new String[]{"kill","-9",Integer.toString(pid)}); + LOGGER.debug("pid {} have been killed directly", pid); + } catch (Exception e) { + LOGGER.error("run failed!", pid); + } + } else { + LOGGER.warn("can not run kill command, the OS type({}) isn't linux!", osType); + } + } + + private Response createResponse() { + TriggerResponse response = new TriggerResponse(); + response.setPID(isEnable ? Integer.toString(xferPID) : "0"); + return response; + } + +} diff --git a/dev/cosbench-driver-web/src/com/intel/cosbench/driver/web/TriggerController.java b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/web/TriggerController.java new file mode 100644 index 00000000..23db135c --- /dev/null +++ b/dev/cosbench-driver-web/src/com/intel/cosbench/driver/web/TriggerController.java @@ -0,0 +1,23 @@ +package com.intel.cosbench.driver.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.servlet.ModelAndView; + +import com.intel.cosbench.service.DriverService; +import com.intel.cosbench.web.AbstractController; + +public class TriggerController extends AbstractController { + protected DriverService driver; + + public void setDriver(DriverService driver) { + this.driver = driver; + } + @Override + protected ModelAndView process(HttpServletRequest req, + HttpServletResponse res) throws Exception { + return new ModelAndView(); + } + +}