Mercurial > hg > thermostat
changeset 2301:a2ac39aa092e
Initial version of byteman cli client.
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-May/018868.html
line wrap: on
line diff
--- a/distribution/config/examples/thermostat-roles-example.properties Wed May 18 12:13:13 2016 -0400 +++ b/distribution/config/examples/thermostat-roles-example.properties Mon May 09 09:39:38 2016 +0200 @@ -57,4 +57,5 @@ thermostat-cmdc-grant-killvm, \ thermostat-cmdc-grant-profile-vm, \ thermostat-cmdc-grant-ping, \ - thermostat-cmdc-grant-jmx-toggle-notifications + thermostat-cmdc-grant-jmx-toggle-notifications \ + thermostat-cmdc-grant-vm-byteman-instrument
--- a/distribution/config/thermostat-roles.properties Wed May 18 12:13:13 2016 -0400 +++ b/distribution/config/thermostat-roles.properties Mon May 09 09:39:38 2016 +0200 @@ -74,4 +74,5 @@ # thermostat-cmdc-grant-killvm, \ # thermostat-cmdc-grant-profile-vm, \ # thermostat-cmdc-grant-ping, \ -# thermostat-cmdc-grant-jmx-toggle-notifications +# thermostat-cmdc-grant-jmx-toggle-notifications \ +# thermostat-cmdc-grant-vm-byteman-instrument
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserRoles.java Wed May 18 12:13:13 2016 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserRoles.java Mon May 09 09:39:38 2016 +0200 @@ -69,7 +69,8 @@ final String GRANT_CMD_CHANNEL_KILLVM = "thermostat-cmdc-grant-killvm"; final String GRANT_CMD_CHANNEL_PING = "thermostat-cmdc-grant-ping"; final String GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION = "thermostat-cmdc-grant-jmx-toggle-notifications"; - final String GRANT_CMD_PROFILE_VM = "thermostat-cmdc-grant-profile-vm"; + final String GRANT_CMD_CHANNEL_PROFILE_VM = "thermostat-cmdc-grant-profile-vm"; + final String GRANT_CMD_CHANNEL_BYTEMAN_INSTRUMENT = "thermostat-cmdc-grant-vm-byteman-instrument"; final String PREPARE_STATEMENT = "thermostat-prepare-statement"; final String READ = "thermostat-query"; @@ -126,6 +127,7 @@ UserRoles.GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION, UserRoles.GRANT_CMD_CHANNEL_KILLVM, UserRoles.GRANT_CMD_CHANNEL_PING, - UserRoles.GRANT_CMD_PROFILE_VM + UserRoles.GRANT_CMD_CHANNEL_PROFILE_VM, + UserRoles.GRANT_CMD_CHANNEL_BYTEMAN_INSTRUMENT }; }
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanAgentInfo.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanAgentInfo.java Mon May 09 09:39:38 2016 +0200 @@ -42,12 +42,14 @@ private final int agentListenPort; private final String listenHost; private final String vmId; + private final String writerId; - BytemanAgentInfo(int vmPid, int agentListenPort, String listenHost, String vmId) { + BytemanAgentInfo(int vmPid, int agentListenPort, String listenHost, String vmId, String writerId) { this.agentListenPort = agentListenPort; this.listenHost = listenHost; this.vmPid = vmPid; this.vmId = vmId; + this.writerId = writerId; } int getVmPid() { @@ -65,4 +67,8 @@ String getVmId() { return vmId; } + + String getWriterId() { + return writerId; + } }
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanAttacher.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanAttacher.java Mon May 09 09:39:38 2016 +0200 @@ -91,7 +91,7 @@ // Port might have changed here if agent rebooted and targed jvm // stayed alive if (actualPort > 0) { - return new BytemanAgentInfo(pid, actualPort, null, vmId); + return new BytemanAgentInfo(pid, actualPort, null, vmId, agentId); } else { return null; }
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanMetricsReceiver.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanMetricsReceiver.java Mon May 09 09:39:38 2016 +0200 @@ -47,15 +47,15 @@ import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.byteman.agent.internal.typeadapter.BytemanMetricTypeAdapter; import com.redhat.thermostat.vm.byteman.common.BytemanMetric; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; class BytemanMetricsReceiver implements ThermostatIPCCallbacks { private static final Logger logger = LoggingUtils.getLogger(BytemanMetricsReceiver.class); - private final VmBytemanMetricDAO dao; + private final VmBytemanDAO dao; private final VmSocketIdentifier socketId; - BytemanMetricsReceiver(VmBytemanMetricDAO dao, VmSocketIdentifier socketId) { + BytemanMetricsReceiver(VmBytemanDAO dao, VmSocketIdentifier socketId) { this.dao = dao; this.socketId = socketId; } @@ -66,7 +66,7 @@ logger.fine("Received metrics from byteman for socketId: " + socketId.getName() + ". Metric was: " + jsonMetric); List<BytemanMetric> metrics = convertFromJson(jsonMetric); for (BytemanMetric metric: metrics) { - dao.putMetric(metric); + dao.addMetric(metric); } return null; // No response to send back to the IPC endpoint }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanRequestReceiver.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,227 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.agent.internal; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.jboss.byteman.agent.submit.ScriptText; +import org.jboss.byteman.agent.submit.Submit; + +import com.redhat.thermostat.agent.command.RequestReceiver; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.core.WriterID; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +/** + * Receiver class for Byteman action command channel requests. + * + */ +@Component +@Service(value = RequestReceiver.class) +@Property(name = "servicename", value = "com.redhat.thermostat.vm.byteman.agent.internal.BytemanRequestReceiver") +public class BytemanRequestReceiver implements RequestReceiver { + + private static final Logger logger = LoggingUtils.getLogger(BytemanRequestReceiver.class); + private static final Response ERROR_RESPONSE = new Response(ResponseType.ERROR); + private static final Response OK_RESPONSE = new Response(ResponseType.OK); + private static final String BYTEMAN_PLUGIN_DIR = System.getProperty("thermostat.plugin", "vm-byteman"); + private static final String BYTEMAN_PLUGIN_LIBS_DIR = BYTEMAN_PLUGIN_DIR + File.separator + "plugin-libs"; + private static final String BYTEMAN_HELPER_DIR = BYTEMAN_PLUGIN_LIBS_DIR + File.separator + "thermostat-helper"; + + // package-private for testing + static List<String> helperJars; + + @Reference + private CommonPaths paths; + + @Reference + private VmBytemanDAO vmBytemanDao; + + @Reference + private WriterID writerId; + + //////////////////////////////////////////////// + // methods used by DS + //////////////////////////////////////////////// + + protected void bindPaths(CommonPaths paths) { + File bytemanHelperDir = new File(paths.getSystemPluginRoot(), BYTEMAN_HELPER_DIR); + initListOfHelperJars(bytemanHelperDir); + } + + protected void unbindPaths(CommonPaths paths) { + synchronized (helperJars) { + helperJars = null; + } + } + + protected void bindWriterId(WriterID writerId) { + this.writerId = writerId; + } + + protected void unbindWriterId(WriterID writerId) { + this.writerId = null; + } + + protected void bindVmBytemanDao(VmBytemanDAO dao) { + this.vmBytemanDao = dao; + } + + protected void unbindVmBytemanDao(VmBytemanDAO dao) { + this.vmBytemanDao = null; + } + + //////////////////////////////////////////////// + // end methods used by DS + //////////////////////////////////////////////// + + @Override + public Response receive(Request request) { + String vmId = request.getParameter(BytemanRequest.VM_ID_PARAM_NAME); + String actionStr = request.getParameter(BytemanRequest.ACTION_PARAM_NAME); + String portStr = request.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME); + RequestAction action; + try { + action = RequestAction.fromIntString(actionStr); + } catch (IllegalArgumentException e) { + logger.log(Level.WARNING, "Illegal action received", e); + return ERROR_RESPONSE; + } + logger.fine("Processing request for vmId: " + vmId + ", Action: " + action + ", port: " + portStr); + int port = Integer.parseInt(portStr); + Response response = null; + switch(action) { + case LOAD_RULES: + String rule = request.getParameter(BytemanRequest.RULE_PARAM_NAME); + response = loadRules(port, new VmId(vmId), rule); + break; + case UNLOAD_RULES: + response = unloadRules(port, new VmId(vmId)); + break; + default: + logger.warning("Unknown action: " + action); + return ERROR_RESPONSE; + } + return response; + } + + private Response loadRules(int listenPort, VmId vmId, String bytemanRules) { + Submit submit = getSubmit(listenPort); + try { + // Add jar files for Thermostat byteman helper: + String addJarsResult = submit.addJarsToSystemClassloader(helperJars); + logger.fine("Added jars for byteman helper with result: " + addJarsResult); + List<ScriptText> existingScripts = submit.getAllScripts(); + if (existingScripts.size() > 0) { + String deleteResult = submit.deleteAllRules(); + logger.fine("Deleted rules with result: " + deleteResult); + } + List<InputStream> list = Arrays.<InputStream>asList(new ByteArrayInputStream(bytemanRules.getBytes(Charset.forName("UTF-8")))); + String addRulesResult = submit.addRulesFromResources(list); + logger.info("Added byteman rules for VM with id '" + vmId.get() + "' with result: " + addRulesResult); + VmBytemanStatus status = new VmBytemanStatus(writerId.getWriterID()); + status.setListenPort(listenPort); + status.setRule(bytemanRules); + status.setTimeStamp(System.currentTimeMillis()); + status.setVmId(vmId.get()); + vmBytemanDao.addOrReplaceBytemanStatus(status); + return OK_RESPONSE; + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to submit byteman rules", e); + return ERROR_RESPONSE; + } + } + + private Response unloadRules(int listenPort, VmId vmId) { + Submit submit = getSubmit(listenPort); + try { + List<ScriptText> list = submit.getAllScripts(); + // Avoid no scripts to delete errors + if (!list.isEmpty()) { + submit.deleteAllRules(); + // update the corresponding status in storage + VmBytemanStatus newStatus = new VmBytemanStatus(writerId.getWriterID()); + newStatus.setListenPort(listenPort); + newStatus.setRule(null); + newStatus.setTimeStamp(System.currentTimeMillis()); + newStatus.setVmId(vmId.get()); + vmBytemanDao.addOrReplaceBytemanStatus(newStatus); + } + return OK_RESPONSE; + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to delete rules", e); + return ERROR_RESPONSE; + } + } + + protected Submit getSubmit(int listenPort) { + return new Submit(null /* localhost */, listenPort); + } + + // package private for testing + static synchronized List<String> initListOfHelperJars(File helperDir) { + if (helperJars == null) { + List<String> jars = new ArrayList<>(); + for (File f: helperDir.listFiles()) { + jars.add(f.getAbsolutePath()); + } + helperJars = jars; + } + return helperJars; + } + +}
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/PeriodicBytemanPollingBackend.java Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.byteman.agent.internal; - -import java.util.concurrent.Executors; - -import com.redhat.thermostat.agent.VmStatusListenerRegistrar; -import com.redhat.thermostat.backend.VmPollingBackend; -import com.redhat.thermostat.common.Version; -import com.redhat.thermostat.shared.config.CommonPaths; - -public class PeriodicBytemanPollingBackend extends VmPollingBackend { - - public PeriodicBytemanPollingBackend(Version version, - VmStatusListenerRegistrar registrar, BytemanAgentInfo info, CommonPaths paths) { - super("VM Byteman backend (rule updater)", - "Updates a byteman rule periodically", - "Red Hat Inc.", - version, Executors.newSingleThreadScheduledExecutor(), registrar); - registerAction(new UpdateRulePollingAction(info, paths)); - } - - @Override - public int getOrderValue() { - return VmBytemanBackend.BACKEND_ORDER_VALUE + 1; - } - -}
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/UpdateRulePollingAction.java Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.byteman.agent.internal; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jboss.byteman.agent.submit.ScriptText; -import org.jboss.byteman.agent.submit.Submit; - -import com.redhat.thermostat.backend.VmPollingAction; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.common.utils.StreamUtils; -import com.redhat.thermostat.shared.config.CommonPaths; - -class UpdateRulePollingAction implements VmPollingAction { - - private static final String BYTEMAN_PLUGIN_DIR = System.getProperty("thermostat.plugin", "vm-byteman"); - private static final String BYTEMAN_PLUGIN_LIBS_DIR = BYTEMAN_PLUGIN_DIR + File.separator + "plugin-libs"; - private static final String BYTEMAN_HELPER_DIR = BYTEMAN_PLUGIN_LIBS_DIR + File.separator + "thermostat-helper"; - private static final String BYTEMAN_SCRIPT_NAME = "bytemanRule.btm"; - private static final Logger logger = LoggingUtils.getLogger(UpdateRulePollingAction.class); - static final List<ScriptText> bmScripts; - static { - bmScripts = getBmScripts(); - } - - private static List<String> helperJars = null; - private final BytemanAgentInfo agentInfo; - private final Map<String, Boolean> rulesSubmitted; - private boolean loggedRuleSubmission = false; - - UpdateRulePollingAction(BytemanAgentInfo agentInfo, CommonPaths paths) { - this.agentInfo = agentInfo; - this.rulesSubmitted = new HashMap<>(); - File bytemanHelperDir = new File(paths.getSystemPluginRoot(), BYTEMAN_HELPER_DIR); - initListOfHelperJars(bytemanHelperDir); - } - - - @Override - public void run(String vmId, int pid) { - // As of yet we submit the fixed rule once and be done with it. - // A future enhancement, for example, would be to put the rule - // into storage, retrieve it and if changed submit the changed - // rules. - Boolean value = rulesSubmitted.get(vmId); - boolean ruleSubmitted = value == null ? false : value; - if (!ruleSubmitted) { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - PrintStream bytemanOutputWriter = new PrintStream(bout); - String host = agentInfo.getListenHost(); - int port = agentInfo.getAgentListenPort(); - Submit bytemanSubmit = new Submit(host, port, bytemanOutputWriter); - try { - // Add jar files for Thermostat byteman helper: - String addJarsResult = bytemanSubmit.addJarsToSystemClassloader(helperJars); - logger.fine("Added jars for byteman helper with result: " + addJarsResult); - List<ScriptText> existingScripts = bytemanSubmit.getAllScripts(); - if (existingScripts.size() > 0) { - String deleteResult = bytemanSubmit.deleteScripts(bmScripts); - logger.fine("Deleted rule with result: " + deleteResult); - } - String deployResult = bytemanSubmit.addScripts(bmScripts); - logger.fine("Deployed rule with result: " + deployResult); - logger.finest("Byteman output for VM ID '" + vmId + "' was: " + new String(bout.toByteArray())); - } catch (Exception e) { - logger.log(Level.WARNING, "Failed to submit rules.", e); - } - rulesSubmitted.put(vmId, true); - } else { - if (!loggedRuleSubmission) { - logger.fine("Skipping byteman rule submission for VM '" + pid + "', since it's been submitted already."); - loggedRuleSubmission = true; - } - } - } - - private static List<ScriptText> getBmScripts() { - String scriptText = getScriptText(); - ScriptText text = new ScriptText(BYTEMAN_SCRIPT_NAME, scriptText); - List<ScriptText> bmlist = new ArrayList<>(); - bmlist.add(text); - return bmlist; - } - - private static String getScriptText() { - try { - byte[] bytes = StreamUtils.readAll(UpdateRulePollingAction.class.getResourceAsStream("/" + BYTEMAN_SCRIPT_NAME)); - String script = new String(bytes, Charset.forName("UTF-8")); - return script.trim(); // Byteman does not seem to like empty lines at the end of rule files - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to read byteman rule file", e); - return null; - } - } - - // package private for testing - static List<String> initListOfHelperJars(File helperDir) { - if (helperJars == null) { - List<String> jars = new ArrayList<>(); - for (File f: helperDir.listFiles()) { - jars.add(f.getAbsolutePath()); - } - helperJars = jars; - } - return helperJars; - } - -}
--- a/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/VmBytemanBackend.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/VmBytemanBackend.java Mon May 09 09:39:38 2016 +0200 @@ -40,7 +40,9 @@ import java.io.IOException; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -60,7 +62,8 @@ import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.storage.core.WriterID; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; @Component @Service(value = Backend.class) @@ -75,6 +78,7 @@ private static final Logger logger = LoggingUtils.getLogger(VmBytemanBackend.class); static final int BACKEND_ORDER_VALUE = ORDER_CODE_GROUP + 3; private final Set<String> sockets = Collections.synchronizedSet(new HashSet<String>()); + private final Map<String, BytemanAgentInfo> agentInfos = new ConcurrentHashMap<>(); private boolean started; private boolean observeNewJVMs; private BytemanAttacher attacher; @@ -84,7 +88,7 @@ // Services @Reference - private VmBytemanMetricDAO dao; + private VmBytemanDAO dao; @Reference private CommonPaths paths; @@ -113,8 +117,8 @@ protected void activate(ComponentContext context) { BundleContext ctx = context.getBundleContext(); Bundle thisBundle = ctx.getBundle(); - this.version = new Version(thisBundle); - this.registrar = new VmStatusListenerRegistrar(ctx); + version = new Version(thisBundle); + registrar = new VmStatusListenerRegistrar(ctx); } protected void deactivate(ComponentContext context) { @@ -181,26 +185,29 @@ } } - private void attachBytemanToVm(String vmId, int pid) { + private synchronized void attachBytemanToVm(String vmId, int pid) { if (!started) { logger.fine(getName() +" not active. Thus not attaching Byteman agent to VM '" + pid + "'"); return; } logger.fine("Attaching byteman agent to VM '" + pid + "'"); BytemanAgentInfo info = attacher.attach(vmId, pid, writerId.getWriterID()); + agentInfos.put(vmId, info); if (info == null) { logger.warning("Failed to attach byteman agent for VM '" + pid + "'. Skipping rule updater and IPC channel."); return; } + logger.fine("Attached byteman agent to VM '" + pid + "' at port: '" + info.getAgentListenPort()); logger.fine("Starting IPC socket for byteman helper"); VmSocketIdentifier socketId = new VmSocketIdentifier(vmId, pid, writerId.getWriterID()); final ThermostatIPCCallbacks callback = new BytemanMetricsReceiver(dao, socketId); startIPCEndpoint(socketId, callback); - // Finally activate the rule submitter backend. The metrics will be sent - // back to us via the byteman helper and received via BytemanMetricsReceiver. - PeriodicBytemanPollingBackend ruleBackend = new PeriodicBytemanPollingBackend(version, registrar, info, paths); - logger.fine("Activating byteman rule updater backend for VM '" + pid + "'"); - ruleBackend.activate(); + // Add a status record to storage + VmBytemanStatus status = new VmBytemanStatus(writerId.getWriterID()); + status.setListenPort(info.getAgentListenPort()); + status.setTimeStamp(System.currentTimeMillis()); + status.setVmId(vmId); + dao.addOrReplaceBytemanStatus(status); } private void startIPCEndpoint(VmSocketIdentifier identifier, ThermostatIPCCallbacks callback) {
--- a/vm-byteman/agent/src/main/resources/bytemanRule.btm Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -RULE count sleep method invocations -CLASS com.redhat.thermostat.byteman.test.InfiniteLoop -METHOD sleep -AT ENTRY -IF true -DO -incrementCounter("sleep-counter") -ENDRULE - -RULE send sleep method invocations to thermostat -CLASS com.redhat.thermostat.byteman.test.InfiniteLoop -METHOD sleep -HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper -AT ENTRY -BIND counterValue: int = readCounter("sleep-counter") -IF counterValue % 10 == 0 -DO -send("com.redhat.thermostat.byteman.test.InfiniteLoop", "method sleep() count", counterValue); -ENDRULE
--- a/vm-byteman/agent/src/test/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanMetricsReceiverTest.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/agent/src/test/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanMetricsReceiverTest.java Mon May 09 09:39:38 2016 +0200 @@ -49,7 +49,7 @@ import org.mockito.ArgumentCaptor; import com.redhat.thermostat.vm.byteman.common.BytemanMetric; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; public class BytemanMetricsReceiverTest { @@ -57,13 +57,13 @@ @Test public void canSendDataToStorage() { - VmBytemanMetricDAO dao = mock(VmBytemanMetricDAO.class); + VmBytemanDAO dao = mock(VmBytemanDAO.class); ArgumentCaptor<BytemanMetric> metricsCaptor = ArgumentCaptor.forClass(BytemanMetric.class); VmSocketIdentifier sockId = new VmSocketIdentifier("vm-id", SOME_PID, "agent-id"); BytemanMetricsReceiver receiver = new BytemanMetricsReceiver(dao, sockId); String jsonString = JsonHelper.buildJsonArray(3); receiver.dataReceived(jsonString.getBytes()); - verify(dao, times(3)).putMetric(metricsCaptor.capture()); + verify(dao, times(3)).addMetric(metricsCaptor.capture()); List<BytemanMetric> metrics = metricsCaptor.getAllValues(); assertEquals("vm-id", metrics.get(0).getVmId()); assertEquals("agent-id", metrics.get(2).getAgentId()); @@ -71,4 +71,5 @@ assertEquals("baz0", metrics.get(0).getMarker()); assertNotNull(metrics.get(2).getData()); } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/agent/src/test/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanRequestReceiverTest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,214 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.agent.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.net.InetSocketAddress; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.jboss.byteman.agent.submit.ScriptText; +import org.jboss.byteman.agent.submit.Submit; +import org.junit.After; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Request.RequestType; +import com.redhat.thermostat.common.command.Response.ResponseType; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.core.WriterID; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +public class BytemanRequestReceiverTest { + + @After + public void tearDown() { + BytemanRequestReceiver.helperJars = null; + } + + @Test + public void testLoadRules() throws Exception { + Submit submit = mock(Submit.class); + doLoadRulesTest(submit); + verify(submit, never()).deleteAllRules(); + } + + @Test + public void testLoadRulesWithRulesExisting() throws Exception { + Submit submit = mock(Submit.class); + when(submit.getAllScripts()).thenReturn(Arrays.asList(mock(ScriptText.class))); + doLoadRulesTest(submit); + verify(submit).deleteAllRules(); + } + + @Test + public void testUnLoadRulesWithNoExistingRules() throws Exception { + Submit submit = mock(Submit.class); + when(submit.getAllScripts()).thenReturn(Collections.<ScriptText>emptyList()); + CommonPaths paths = mock(CommonPaths.class); + File helperRootFile = getHelperRootFile(); + when(paths.getSystemPluginRoot()).thenReturn(helperRootFile); + BytemanRequestReceiver receiver = createReceiver(submit, null, paths, null); + Response response = receiver.receive(BytemanRequest.create(mock(InetSocketAddress.class), new VmId("ignored"), RequestAction.UNLOAD_RULES, -1)); + assertEquals(ResponseType.OK, response.getType()); + verify(submit, never()).deleteAllRules(); + } + + @Test + public void testUnLoadRulesWithExistingRules() throws Exception { + Submit submit = mock(Submit.class); + when(submit.getAllScripts()).thenReturn(Arrays.asList(mock(ScriptText.class))); + CommonPaths paths = mock(CommonPaths.class); + File helperRootFile = getHelperRootFile(); + when(paths.getSystemPluginRoot()).thenReturn(helperRootFile); + WriterID writerId = mock(WriterID.class); + String someAgentId = "some-agent-id"; + String someVmId = "some-vm-id"; + int someListenPort = 3333; + VmBytemanDAO bytemanDao = mock(VmBytemanDAO.class); + when(writerId.getWriterID()).thenReturn(someAgentId); + BytemanRequestReceiver receiver = createReceiver(submit, writerId, paths, bytemanDao); + ArgumentCaptor<VmBytemanStatus> statusCaptor = ArgumentCaptor.forClass(VmBytemanStatus.class); + Response response = receiver.receive(BytemanRequest.create(mock(InetSocketAddress.class), new VmId(someVmId), RequestAction.UNLOAD_RULES, someListenPort)); + assertEquals(ResponseType.OK, response.getType()); + verify(submit).deleteAllRules(); + verify(bytemanDao).addOrReplaceBytemanStatus(statusCaptor.capture()); + VmBytemanStatus status = statusCaptor.getValue(); + assertEquals(someAgentId, status.getAgentId()); + assertEquals(someVmId, status.getVmId()); + assertEquals(someListenPort, status.getListenPort()); + assertNull(status.getRule()); + } + + @Test + public void testReceiveWithBadAction() { + Request badRequest = new Request(RequestType.RESPONSE_EXPECTED, mock(InetSocketAddress.class)); + badRequest.setParameter(BytemanRequest.ACTION_PARAM_NAME, Integer.toString(-1)); + BytemanRequestReceiver receiver = new BytemanRequestReceiver(); + Response response = receiver.receive(badRequest); + assertEquals(ResponseType.ERROR, response.getType()); + } + + @SuppressWarnings("unchecked") + private void doLoadRulesTest(Submit submit) throws Exception { + File helperRootFile = getHelperRootFile(); + CommonPaths paths = mock(CommonPaths.class); + when(paths.getSystemPluginRoot()).thenReturn(helperRootFile); + VmBytemanDAO bytemanDao = mock(VmBytemanDAO.class); + WriterID wid = mock(WriterID.class); + String writerId = "some-writer-id"; + String someVmId = "some-id"; + int listenPort = 333; + when(wid.getWriterID()).thenReturn(writerId); + BytemanRequestReceiver receiver = createReceiver(submit, wid, paths, bytemanDao); + String rule = "some-rule"; + ArgumentCaptor<VmBytemanStatus> statusCaptor = ArgumentCaptor.forClass(VmBytemanStatus.class); + Response response = receiver.receive(BytemanRequest.create(mock(InetSocketAddress.class), new VmId(someVmId), RequestAction.LOAD_RULES, listenPort, rule)); + verify(bytemanDao).addOrReplaceBytemanStatus(statusCaptor.capture()); + VmBytemanStatus capturedStatus = statusCaptor.getValue(); + assertEquals(someVmId, capturedStatus.getVmId()); + assertEquals(writerId, capturedStatus.getAgentId()); + assertEquals(rule, capturedStatus.getRule()); + assertEquals(listenPort, capturedStatus.getListenPort()); + List<String> expectedList = new ArrayList<>(); + expectedList.add(helperRootFile.getAbsolutePath() + File.separator + "vm-byteman" + File.separator + "plugin-libs" + File.separator + "thermostat-helper" + File.separator + "not-really-a-jar-file.jar"); + // Verify thermostat helper jars get added + verify(submit).addJarsToSystemClassloader(eq(expectedList)); + verify(submit).addRulesFromResources(any(List.class)); + assertEquals(ResponseType.OK, response.getType()); + } + + private File getHelperRootFile() { + URL rootFile = getClass().getResource("/byteman-helper-root"); + File helperRootFile = new File(rootFile.getFile()); + return helperRootFile; + } + + private BytemanRequestReceiver createReceiver(final Submit submit, WriterID writerId, CommonPaths paths, VmBytemanDAO dao) { + BytemanRequestReceiver receiver = new BytemanRequestReceiver() { + @Override + protected Submit getSubmit(int port) { + return submit; + } + + }; + receiver.bindPaths(paths); + receiver.bindVmBytemanDao(dao); + receiver.bindWriterId(writerId); + return receiver; + } + + @Test + public void canGetListOfJarsForBytemanHelper() { + String parent = "/foo"; + File file = mock(File.class); + File[] mockFiles = new File[7]; + for (int i = 0; i < 7; i++) { + mockFiles[i] = getFileMockWithName(parent, "test-file" + i + ".jar"); + } + when(file.listFiles()).thenReturn(mockFiles); + List<String> jars = BytemanRequestReceiver.initListOfHelperJars(file); + assertEquals(7, jars.size()); + for (int i = 0; i < 7; i++) { + assertEquals("/foo/test-file" + i + ".jar", jars.get(i)); + } + } + + private File getFileMockWithName(String parent, String name) { + File f = mock(File.class); + when(f.getAbsolutePath()).thenReturn(parent + "/" + name); + return f; + } +}
--- a/vm-byteman/agent/src/test/java/com/redhat/thermostat/vm/byteman/agent/internal/UpdateRulePollingActionTest.java Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.byteman.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.util.List; - -import org.jboss.byteman.agent.submit.ScriptText; -import org.junit.Test; - -public class UpdateRulePollingActionTest { - - @Test - public void canGetScriptText() { - List<ScriptText> text = UpdateRulePollingAction.bmScripts; - assertEquals(1, text.size()); - ScriptText script = text.get(0); - String scriptText = script.getText(); - assertTrue(scriptText.contains("ThermostatHelper")); - } - - @Test - public void canGetListOfJarsForBytemanHelper() { - String parent = "/foo"; - File file = mock(File.class); - File[] mockFiles = new File[7]; - for (int i = 0; i < 7; i++) { - mockFiles[i] = getFileMockWithName(parent, "test-file" + i + ".jar"); - } - when(file.listFiles()).thenReturn(mockFiles); - List<String> jars = UpdateRulePollingAction.initListOfHelperJars(file); - assertEquals(7, jars.size()); - for (int i = 0; i < 7; i++) { - assertEquals("/foo/test-file" + i + ".jar", jars.get(i)); - } - } - - private File getFileMockWithName(String parent, String name) { - File f = mock(File.class); - when(f.getAbsolutePath()).thenReturn(parent + "/" + name); - return f; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/pom.xml Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2012-2016 Red Hat, Inc. + + This file is part of Thermostat. + + Thermostat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + Thermostat is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Thermostat; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. + + Linking this code with other modules is making a combined work + based on this code. Thus, the terms and conditions of the GNU + General Public License cover the whole combination. + + As a special exception, the copyright holders of this code give + you permission to link this code with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also + meet, for each linked independent module, the terms and conditions + of the license of that module. An independent module is a module + which is not derived from or based on this code. If you modify + this code, you may extend this exception to your version of the + library, but you are not obligated to do so. If you do not wish + to do so, delete this exception statement from your version. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>thermostat-vm-byteman</artifactId> + <groupId>com.redhat.thermostat</groupId> + <version>1.99.10-SNAPSHOT</version> + </parent> + <artifactId>thermostat-vm-byteman-client-cli</artifactId> + <packaging>bundle</packaging> + <name>Thermostat VM Byteman plugin: CLI client</name> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Activator>com.redhat.thermostat.vm.byteman.client.cli.internal.Activator</Bundle-Activator> + <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> + <Bundle-SymbolicName>com.redhat.thermostat.vm.byteman.client.cli</Bundle-SymbolicName> + <Export-Package /> + <Private-Package> + com.redhat.thermostat.vm.byteman.client.cli.internal + </Private-Package> + <!-- Do not autogenerate uses clauses in Manifests --> + <_nouses>true</_nouses> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + <executions> + <execution> + <id>generate-scr-scrdescriptor</id> + <goals> + <goal>scr</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-command</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-byteman-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-storage-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.jboss.byteman</groupId> + <artifactId>byteman-submit</artifactId> + <version>${byteman.version}</version> + </dependency> + <dependency> + <groupId>org.jboss.byteman</groupId> + <artifactId>byteman-install</artifactId> + <version>${byteman.version}</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>${gson.version}</version> + </dependency> + <!-- declarative services --> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + </dependencies> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/Activator.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import java.util.Hashtable; +import java.util.Map; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.MultipleServiceTracker; +import com.redhat.thermostat.common.MultipleServiceTracker.Action; +import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; + +public class Activator implements BundleActivator { + + private ServiceRegistration<Command> commandReg; + private MultipleServiceTracker depsTracker; + + @Override + public void start(BundleContext context) throws Exception { + Hashtable<String, String> properties = new Hashtable<>(); + properties.put(Command.NAME, BytemanControlCommand.COMMAND_NAME); + final BytemanControlCommand bytemanCommand = new BytemanControlCommand(); + commandReg = context.registerService(Command.class, bytemanCommand, properties); + Class<?>[] deps = new Class[] { + VmInfoDAO.class, + AgentInfoDAO.class, + VmBytemanDAO.class, + RequestQueue.class, + }; + depsTracker = new MultipleServiceTracker(context, deps, new Action() { + + @Override + public void dependenciesUnavailable() { + bytemanCommand.unsetAgentInfoDao(); + bytemanCommand.unsetRequestQueue(); + bytemanCommand.unsetVmBytemanDao(); + bytemanCommand.unsetVmInfoDao(); + } + + @Override + public void dependenciesAvailable(Map<String, Object> services) { + VmInfoDAO vmInfo = (VmInfoDAO)services.get(VmInfoDAO.class.getName()); + AgentInfoDAO agentInfo = (AgentInfoDAO)services.get(AgentInfoDAO.class.getName()); + VmBytemanDAO vmBytemanDao = (VmBytemanDAO)services.get(VmBytemanDAO.class.getName()); + RequestQueue queue = (RequestQueue)services.get(RequestQueue.class.getName()); + bytemanCommand.setAgentInfoDao(agentInfo); + bytemanCommand.setVmInfoDao(vmInfo); + bytemanCommand.setVmBytemanDao(vmBytemanDao); + bytemanCommand.setRequestQueue(queue); + } + }); + depsTracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + depsTracker.close(); + commandReg.unregister(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommand.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,270 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.client.cli.VmArgument; +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.cli.AbstractCommand; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.DependencyServices; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.common.utils.StreamUtils; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.VmInfo; +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +public class BytemanControlCommand extends AbstractCommand { + + public static final String COMMAND_NAME = "byteman"; + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + private static final String INJECT_RULE_ACTION = "load"; + private static final String UNLOAD_RULE_ACTION = "unload"; + private static final String STATUS_ACTION = "status"; + private static final String SHOW_ACTION = "show-metrics"; + private static final String RULES_FILE_OPTION = "rules"; + private static final String NO_RULES_LOADED = "<no-loaded-rules>"; + private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); + + private final DependencyServices depServices = new DependencyServices(); + + @Override + public void run(CommandContext ctx) throws CommandException { + VmArgument vmArgument = VmArgument.required(ctx.getArguments()); + VmId vmId = vmArgument.getVmId(); + + VmInfoDAO vmInfoDAO = depServices.getService(VmInfoDAO.class); + + requireNonNull(vmInfoDAO, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); + final VmInfo vmInfo = vmInfoDAO.getVmInfo(vmId); + requireNonNull(vmInfo, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); + + AgentInfoDAO agentInfoDAO = depServices.getService(AgentInfoDAO.class); + final AgentId agentId = new AgentId(vmInfo.getAgentId()); + requireNonNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE)); + + AgentInformation agentInfo = agentInfoDAO.getAgentInformation(agentId); + if (agentInfo == null) { + throw new CommandException(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId.get())); + } + if (!agentInfo.isAlive()) { + throw new CommandException(translator.localize(LocaleResources.AGENT_DEAD, agentId.get())); + } + + InetSocketAddress target = agentInfo.getRequestQueueAddress(); + + List<String> nonOptionargs = ctx.getArguments().getNonOptionArguments(); + if (nonOptionargs.size() != 1) { + throw new CommandException(translator.localize(LocaleResources.COMMAND_EXPECTED)); + } + VmBytemanDAO bytemanDao = depServices.getService(VmBytemanDAO.class); + requireNonNull(bytemanDao, translator.localize(LocaleResources.BYTEMAN_METRICS_SERVICE_UNAVAILABLE)); + + String command = nonOptionargs.get(0); + + switch (command) { + case INJECT_RULE_ACTION: + injectRules(target, vmId, ctx, bytemanDao); + break; + case UNLOAD_RULE_ACTION: + unloadRules(target, vmId, ctx, bytemanDao); + break; + case STATUS_ACTION: + showStatus(ctx, vmInfo, bytemanDao); + break; + case SHOW_ACTION: + showMetrics(ctx, vmId, agentId, bytemanDao); + break; + default: + throw new CommandException(translator.localize(LocaleResources.UNKNOWN_COMMAND, command)); + } + } + + /* Unloads byteman rules */ + private void unloadRules(InetSocketAddress target, VmId vmId, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { + RequestQueue requestQueue = getRequestQueue(); + VmBytemanStatus status = getVmBytemanStatus(vmId, bytemanDao); + int listenPort = status.getListenPort(); + Request unloadRequest = BytemanRequest.create(target, vmId, RequestAction.UNLOAD_RULES, listenPort); + submitRequest(ctx, requestQueue, unloadRequest); + } + + + /* Injects byteman rules */ + private void injectRules(InetSocketAddress target, VmId vmId, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { + Arguments args = ctx.getArguments(); + if (!args.hasArgument(RULES_FILE_OPTION)) { + throw new CommandException(translator.localize(LocaleResources.NO_RULE_OPTION)); + } + String ruleFile = args.getArgument(RULES_FILE_OPTION); + + byte[] rulesBytes; + try { + rulesBytes = StreamUtils.readAll(new FileInputStream(new File(ruleFile))); + } catch (FileNotFoundException e) { + throw new CommandException(translator.localize(LocaleResources.RULE_FILE_NOT_FOUND, ruleFile)); + } catch (IOException e) { + throw new CommandException(translator.localize(LocaleResources.ERROR_READING_RULE_FILE, ruleFile)); + } + String rulesContent = new String(rulesBytes, UTF_8_CHARSET); + VmBytemanStatus status = getVmBytemanStatus(vmId, bytemanDao); + int listenPort = status.getListenPort(); + RequestQueue requestQueue = getRequestQueue(); + Request request = BytemanRequest.create(target, vmId, RequestAction.LOAD_RULES, listenPort, rulesContent); + submitRequest(ctx, requestQueue, request); + } + + /* Show metrics retrieved via byteman rules */ + private void showMetrics(CommandContext ctx, VmId vmId, AgentId agentId, VmBytemanDAO bytemanDao) throws CommandException { + // TODO: Make this query configurable with arguments + long now = System.currentTimeMillis(); + long from = now - TimeUnit.MINUTES.toMillis(5); + long to = now; + Range<Long> timeRange = new Range<Long>(from, to); + List<BytemanMetric> metrics = bytemanDao.findBytemanMetrics(timeRange, vmId, agentId); + PrintStream output = ctx.getConsole().getOutput(); + PrintStream out = ctx.getConsole().getOutput(); + if (metrics.isEmpty()) { + out.println(translator.localize(LocaleResources.NO_METRICS_AVAILABLE, vmId.get()).getContents()); + } else { + for (BytemanMetric m: metrics) { + output.println(m.getData()); + } + } + } + + /* Show status of loaded byteman rules */ + private void showStatus(CommandContext ctx, VmInfo vmInfo, VmBytemanDAO bytemanDao) throws CommandException { + VmBytemanStatus status = getVmBytemanStatus(new VmId(vmInfo.getVmId()), bytemanDao); + PrintStream out = ctx.getConsole().getOutput(); + String rules = status.getRule(); + if (rules == null || rules.isEmpty()) { + rules = NO_RULES_LOADED; + } + out.println(translator.localize(LocaleResources.BYTEMAN_STATUS_MSG, + vmInfo.getMainClass(), + Integer.toString(status.getListenPort()), + rules).getContents()); + } + + private void submitRequest(CommandContext ctx, RequestQueue requestQueue, Request request) { + CountDownLatch latch = new CountDownLatch(1); + request.addListener(new BytemanRequestResponseListener(latch, ctx)); + requestQueue.putRequest(request); + waitWithTimeout(latch); + } + + // package-private for testing + void waitWithTimeout(CountDownLatch latch) { + try { + // wait for request to finish. The listener does the rest. + latch.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore + } + } + + private RequestQueue getRequestQueue() throws CommandException { + RequestQueue requestQueue = depServices.getService(RequestQueue.class); + requireNonNull(requestQueue, translator.localize(LocaleResources.QUEUE_SERVICE_UNAVAILABLE)); + return requestQueue; + } + + private VmBytemanStatus getVmBytemanStatus(VmId vmId, VmBytemanDAO bytemanDao) throws CommandException { + VmBytemanStatus status = bytemanDao.findBytemanStatus(vmId); + if (status == null) { + throw new CommandException(translator.localize(LocaleResources.ERROR_NO_STATUS, vmId.get())); + } + return status; + } + + void setAgentInfoDao(AgentInfoDAO agentDao) { + depServices.addService(AgentInfoDAO.class, agentDao); + } + + void unsetAgentInfoDao() { + depServices.removeService(AgentInfoDAO.class); + } + + void setVmInfoDao(VmInfoDAO vmDao) { + depServices.addService(VmInfoDAO.class, vmDao); + } + + void unsetVmInfoDao() { + depServices.removeService(VmInfoDAO.class); + } + + void setVmBytemanDao(VmBytemanDAO metricDao) { + depServices.addService(VmBytemanDAO.class, metricDao); + } + + void unsetVmBytemanDao() { + depServices.removeService(VmBytemanDAO.class); + } + + void setRequestQueue(RequestQueue queue) { + depServices.addService(RequestQueue.class, queue); + } + + void unsetRequestQueue() { + depServices.removeService(RequestQueue.class); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanRequestResponseListener.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import java.io.PrintStream; +import java.util.concurrent.CountDownLatch; + +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.shared.locale.Translate; + +class BytemanRequestResponseListener implements RequestResponseListener { + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + private final CountDownLatch latch; + private final CommandContext ctx; + + BytemanRequestResponseListener(CountDownLatch latch, CommandContext ctx) { + this.latch = latch; + this.ctx = ctx; + } + + @Override + public void fireComplete(Request request, Response response) { + boolean isError = false; + String errorMsg = ""; + switch(response.getType()) { + case AUTH_FAILED: + isError = true; + errorMsg = t.localize(LocaleResources.REQUEST_FAILED_AUTH_ISSUE).getContents(); + break; + case ERROR: + isError = true; + errorMsg = t.localize(LocaleResources.REQUEST_FAILED_UNKNOWN_ISSUE).getContents(); + break; + case OK: + break; + default: + isError = true; + errorMsg = t.localize(LocaleResources.ERROR_UNKNOWN_RESPONSE, response.getType().toString()).getContents(); + } + latch.countDown(); + if (isError) { + PrintStream err = ctx.getConsole().getError(); + err.println(errorMsg); + } else { + PrintStream out = ctx.getConsole().getOutput(); + out.println(t.localize(LocaleResources.REQUEST_SUCCESS).getContents()); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/LocaleResources.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import com.redhat.thermostat.shared.locale.Translate; + +public enum LocaleResources { + + VM_SERVICE_UNAVAILABLE, + AGENT_SERVICE_UNAVAILABLE, + QUEUE_SERVICE_UNAVAILABLE, + BYTEMAN_METRICS_SERVICE_UNAVAILABLE, + AGENT_NOT_FOUND, + AGENT_DEAD, + COMMAND_EXPECTED, + UNKNOWN_COMMAND, + NO_RULE_OPTION, + RULE_FILE_NOT_FOUND, + ERROR_READING_RULE_FILE, + NO_METRICS_AVAILABLE, + ERROR_NO_STATUS, + BYTEMAN_STATUS_MSG, + REQUEST_FAILED_AUTH_ISSUE, + REQUEST_FAILED_UNKNOWN_ISSUE, + REQUEST_SUCCESS, + ERROR_UNKNOWN_RESPONSE, + ; + + static final String RESOURCE_BUNDLE = LocaleResources.class.getPackage().getName() + ".strings"; + + public static Translate<LocaleResources> createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/resources/com/redhat/thermostat/vm/byteman/client/cli/internal/strings.properties Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,23 @@ +VM_SERVICE_UNAVAILABLE = VM information is not available +AGENT_SERVICE_UNAVAILABLE = Agent information is not available +QUEUE_SERVICE_UNAVAILABLE = Sending requests to the agent is not possible +BYTEMAN_METRICS_SERVICE_UNAVAILABLE = Byteman metrics DAO service is not available +COMMAND_EXPECTED = A valid subcommand is expected. +UNKNOWN_COMMAND = Unknown command: {0} +AGENT_NOT_FOUND = Agent with id {0} not found. +AGENT_DEAD = Agent with id {0} is not alive. +NO_RULE_OPTION = No rule option specified. +RULE_FILE_NOT_FOUND = Specified rules file ''{0}'' not found. +ERROR_READING_RULE_FILE = Error reading file ''{0}''. +NO_METRICS_AVAILABLE = No metrics available for VM {0}. +ERROR_NO_STATUS = No status info available for VM {0}. +BYTEMAN_STATUS_MSG = Byteman status for VM: {0} \n\ + Byteman agent listen port: {1} \n\ + Loaded rules: \n\ + --------------- \n\ + {2} \n\ + --------------- +REQUEST_FAILED_AUTH_ISSUE = Request failed due to authentication/authorization issues. +REQUEST_FAILED_UNKNOWN_ISSUE = Request failed for some unknown reason. +REQUEST_SUCCESS = Request submitted successfully. +ERROR_UNKNOWN_RESPONSE = Unknown response: {0}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommandTest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,383 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.internal.test.TestCommandContextFactory; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.common.utils.StreamUtils; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.VmInfo; +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +public class BytemanControlCommandTest { + + private static final String RULE_OPTION = "rules"; + private static final String STATUS_ACTION = "status"; + private static final String SHOW_METRICS_ACTION = "show-metrics"; + private static final String UNLOAD_ACTION = "unload"; + private static final String LOAD_ACTION = "load"; + private static final String SOME_VM_ID = "some-vm-id"; + private static final String SOME_AGENT_ID = "some-agent-id"; + private static final String EMPTY_STRING = ""; + private static final InetSocketAddress REQUEST_QUEUE_ADDRESS = mock(InetSocketAddress.class); + private BytemanControlCommand command; + private TestCommandContextFactory ctxFactory; + + @Before + public void setup() { + command = new BytemanControlCommand() { + @Override + void waitWithTimeout(CountDownLatch latch) { + // return immediately + } + }; + VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class); + VmInfo vmInfo = new VmInfo(); + vmInfo.setAgentId(SOME_AGENT_ID); + vmInfo.setVmId(SOME_VM_ID); + when(vmInfoDAO.getVmInfo(any(VmId.class))).thenReturn(vmInfo); + command.setVmInfoDao(vmInfoDAO); + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + AgentInformation agentInfo = mock(AgentInformation.class); + when(agentInfo.isAlive()).thenReturn(true); + when(agentInfo.getRequestQueueAddress()).thenReturn(REQUEST_QUEUE_ADDRESS); + when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); + command.setAgentInfoDao(agentInfoDAO); + command.setVmBytemanDao(mock(VmBytemanDAO.class)); + ctxFactory = new TestCommandContextFactory(); + } + + @Test + public void testUnknownAction() { + String unknownAction = "some-action-that-doesn't-exist"; + Arguments args = getBasicArgsWithAction(unknownAction); + CommandContext ctx = ctxFactory.createContext(args); + try { + command.run(ctx); + fail("Expected failure due to unknown action"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertTrue(msg.contains(unknownAction)); + assertTrue(msg.startsWith("Unknown command:")); + } + } + + @Test + public void testStatusActionNoRule() throws CommandException { + String rule = null; + String expectedLoadedRuleMsg = "<no-loaded-rules>"; + basicStatusActionTest(rule, expectedLoadedRuleMsg); + } + + private void basicStatusActionTest(String rule, String expectedRuleMsg) throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(rule); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(STATUS_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + command.run(ctx); + String stdErr = ctxFactory.getError(); + String stdOut = ctxFactory.getOutput(); + assertEquals(EMPTY_STRING, stdErr); + assertTrue(stdOut.contains("Byteman status for VM:")); + assertTrue(stdOut.contains("Byteman agent listen port: " + listenPort)); + assertTrue(stdOut.contains("Loaded rules:")); + assertTrue(stdOut.contains(expectedRuleMsg)); + } + + @Test + public void testStatusActionWithRule() throws CommandException { + String rule = "some-rule-string\nsome more rule lines\nfurther more"; + basicStatusActionTest(rule, rule); + } + + @Test + public void testStatusActionNoStatus() { + Arguments args = getBasicArgsWithAction(STATUS_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + try { + command.run(ctx); + fail("Expected command exception due to missing status"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("No status info available for VM " + SOME_VM_ID + ".", msg); + } + } + + @Test + public void testAgentNotAlive() throws CommandException { + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + AgentInformation agentInfo = mock(AgentInformation.class); + when(agentInfo.isAlive()).thenReturn(false); + when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); + command.unsetAgentInfoDao(); + command.setAgentInfoDao(agentInfoDAO); + Arguments args = getBasicArgsWithAction("no-matter"); + CommandContext ctx = ctxFactory.createContext(args); + try { + command.run(ctx); + fail("Command should have thrown exception"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("Agent with id " + SOME_AGENT_ID + " is not alive.", msg); + } + } + + @Test + public void testShowMetricsActionNoMetrics() throws CommandException { + String expectedStdOut = "No metrics available for VM " + SOME_VM_ID + ".\n"; + List<BytemanMetric> returnedList = Collections.emptyList(); + doShowMetricsTest(returnedList, expectedStdOut); + } + + @Test + public void testShowMetricsActionWithMetrics() throws CommandException { + String metricData1 = "{ \"foo\": \"bar\" }"; + String metricData2 = "{ \"foo2\": -300 }"; + String expectedStdOut = String.format("%s\n%s\n", metricData1, metricData2); + BytemanMetric metric1 = new BytemanMetric(); + metric1.setData(metricData1); + BytemanMetric metric2 = new BytemanMetric(); + metric2.setData(metricData2); + List<BytemanMetric> returnedList = Arrays.asList(metric1, metric2); + doShowMetricsTest(returnedList, expectedStdOut); + } + + @Test + public void testUnloadAction() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(UNLOAD_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); + command.setRequestQueue(rQueue); + command.run(ctx); + verify(rQueue).putRequest(requestCaptor.capture()); + Request submittedRequest = requestCaptor.getValue(); + assertEquals(1, submittedRequest.getListeners().size()); + RequestResponseListener respListener = null; + for (RequestResponseListener l: submittedRequest.getListeners()) { + respListener = l; + break; + } + assertTrue(respListener instanceof BytemanRequestResponseListener); + String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); + RequestAction actualAction = RequestAction.fromIntString(rawAction); + assertEquals(RequestAction.UNLOAD_RULES, actualAction); + assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); + assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); + assertNull(submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); + assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); + } + + @Test + public void testLoadActionNoRuleFile() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); // rule file arg missing + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.setRequestQueue(rQueue); + try { + command.run(ctx); + fail("Expected cmd exception due to missing rule argument"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("No rule option specified.", msg); + } + } + + @Test + public void testLoadActionRuleFileDoesNotExist() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); + when(args.hasArgument(RULE_OPTION)).thenReturn(true); + String file = "i-do-not-exist"; + when(args.getArgument(RULE_OPTION)).thenReturn(file); + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.setRequestQueue(rQueue); + try { + command.run(ctx); + fail("Expected cmd exception due to rule file not existing"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("Specified rules file '" + file + "' not found.", msg); + } + } + + @Test + public void testLoadActionSuccess() throws CommandException, FileNotFoundException, IOException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); + when(args.hasArgument(RULE_OPTION)).thenReturn(true); + String file = getClass().getResource("/testRule.btm").getFile(); + when(args.getArgument(RULE_OPTION)).thenReturn(file); + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.setRequestQueue(rQueue); + ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); + command.run(ctx); + verify(rQueue).putRequest(requestCaptor.capture()); + Request submittedRequest = requestCaptor.getValue(); + assertEquals(1, submittedRequest.getListeners().size()); + RequestResponseListener respListener = null; + for (RequestResponseListener l: submittedRequest.getListeners()) { + respListener = l; + break; + } + assertTrue(respListener instanceof BytemanRequestResponseListener); + String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); + RequestAction actualAction = RequestAction.fromIntString(rawAction); + assertEquals(RequestAction.LOAD_RULES, actualAction); + assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); + assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); + String expectedRule = new String(StreamUtils.readAll(new FileInputStream(new File(file)))); + assertEquals(expectedRule, submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); + assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); + } + + @SuppressWarnings("unchecked") + private void doShowMetricsTest(List<BytemanMetric> metricsToReturn, String stdOutExpected) throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + when(dao.findBytemanMetrics(any(Range.class), any(VmId.class), any(AgentId.class))).thenReturn(metricsToReturn); + Arguments args = getBasicArgsWithAction(SHOW_METRICS_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unsetVmBytemanDao(); + command.setVmBytemanDao(dao); + command.run(ctx); + String stdErr = ctxFactory.getError(); + String stdOut = ctxFactory.getOutput(); + assertEquals(EMPTY_STRING, stdErr); + assertEquals(stdOutExpected, stdOut); + } + + private Arguments getBasicArgsWithAction(String action) { + Arguments args = mock(Arguments.class); + when(args.getArgument("vmId")).thenReturn(SOME_VM_ID); + when(args.getNonOptionArguments()).thenReturn(Arrays.asList(action)); + return args; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanRequestResponseListenerTest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.CountDownLatch; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.Console; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; + +public class BytemanRequestResponseListenerTest { + + private static final String EMPTY_STRING = ""; + private BytemanRequestResponseListener listener; + private ByteArrayOutputStream bout; + private ByteArrayOutputStream berr; + private CountDownLatch latch; + + @Before + public void setup() { + bout = new ByteArrayOutputStream(); + berr = new ByteArrayOutputStream(); + CommandContext ctx = mock(CommandContext.class); + Console console = mock(Console.class); + when(ctx.getConsole()).thenReturn(console); + PrintStream outStream = new PrintStream(bout); + PrintStream errStream = new PrintStream(berr); + when(console.getError()).thenReturn(errStream); + when(console.getOutput()).thenReturn(outStream); + latch = mock(CountDownLatch.class); + listener = new BytemanRequestResponseListener(latch, ctx); + } + + @Test + public void testAuthIssue() { + listener.fireComplete(mock(Request.class), new Response(ResponseType.AUTH_FAILED)); + verify(latch).countDown(); + String stdOut = getOutAsString(); + String errOut = getErrAsString(); + assertEquals(EMPTY_STRING, stdOut); + assertTrue(errOut.contains("authentication")); + assertTrue(errOut.contains("issue")); + } + + private String getOutAsString() { + return new String(bout.toByteArray()); + } + + private String getErrAsString() { + return new String(berr.toByteArray()); + } + + @Test + public void testSuccess() { + listener.fireComplete(mock(Request.class), new Response(ResponseType.OK)); + verify(latch).countDown(); + String stdOut = getOutAsString(); + String errOut = getErrAsString(); + assertEquals("Request submitted successfully.\n", stdOut); + assertEquals(EMPTY_STRING, errOut); + } + + @Test + public void testUnknownError() { + listener.fireComplete(mock(Request.class), new Response(ResponseType.ERROR)); + verify(latch).countDown(); + String stdOut = getOutAsString(); + String errOut = getErrAsString(); + assertEquals(EMPTY_STRING, stdOut); + assertTrue(errOut.contains("unknown")); + assertTrue(errOut.contains("reason")); + } + + @Test + public void testUnknownType() { + listener.fireComplete(mock(Request.class), new Response(ResponseType.NOK)); + verify(latch).countDown(); + String stdOut = getOutAsString(); + String errOut = getErrAsString(); + assertEquals(EMPTY_STRING, stdOut); + assertEquals("Unknown response: NOK\n", errOut); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/LocaleResourcesTest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.client.cli.internal; + +import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest; + +public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> { + + @Override + protected Class<LocaleResources> getEnumClass() { + return LocaleResources.class; + } + + @Override + protected String getResourceBundle() { + return LocaleResources.RESOURCE_BUNDLE; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/test/resources/testRule.btm Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,19 @@ +RULE count sleep method invocations +CLASS com.redhat.thermostat.byteman.test.InfiniteLoop +METHOD sleep +AT ENTRY +IF true +DO +incrementCounter("sleep-counter") +ENDRULE + +RULE send sleep method invocations to thermostat +CLASS com.redhat.thermostat.byteman.test.InfiniteLoop +METHOD sleep +HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper +AT ENTRY +BIND counterValue: int = readCounter("sleep-counter") +IF counterValue % 10 == 0 +DO +send("com.redhat.thermostat.byteman.test.InfiniteLoop", "method sleep() count", counterValue); +ENDRULE \ No newline at end of file
--- a/vm-byteman/common/pom.xml Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/common/pom.xml Mon May 09 09:39:38 2016 +0200 @@ -58,6 +58,7 @@ <Bundle-SymbolicName>com.redhat.thermostat.vm.byteman.common</Bundle-SymbolicName> <Export-Package> com.redhat.thermostat.vm.byteman.common, + com.redhat.thermostat.vm.byteman.common.command, </Export-Package> <Private-Package> com.redhat.thermostat.vm.byteman.common.internal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/VmBytemanDAO.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.common; + +import java.util.List; + +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; + +public interface VmBytemanDAO { + + void addMetric(BytemanMetric metric); + + void addOrReplaceBytemanStatus(VmBytemanStatus status); + + VmBytemanStatus findBytemanStatus(VmId vmId); + + List<BytemanMetric> findBytemanMetrics(Range<Long> timeRange, VmId vmId, AgentId agentId); +}
--- a/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/VmBytemanMetricDAO.java Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.byteman.common; - -import java.util.List; - -import com.redhat.thermostat.storage.core.AgentId; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.VmId; - -public interface VmBytemanMetricDAO { - - public static final Key<String> MARKER = new Key<>("marker"); - public static final Key<String> DATA = new Key<>("data"); - - public static final Category<BytemanMetric> CATEGORY = new Category<>( - "vm-byteman-metrics", - BytemanMetric.class, - Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, - MARKER, DATA); - - void putMetric(BytemanMetric metric); - - List<BytemanMetric> findBytemanMetrics(long from, long to, VmId vmId, AgentId agentId); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/VmBytemanStatus.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.common; + +import com.redhat.thermostat.storage.core.Entity; +import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.model.BasePojo; +import com.redhat.thermostat.storage.model.TimeStampedPojo; + +@Entity +public class VmBytemanStatus extends BasePojo implements TimeStampedPojo { + + private String vmId; + private long timestamp; + private String rule; + private int listenPort; + + public VmBytemanStatus(String writerId) { + super(writerId); + } + + public VmBytemanStatus() { + super(null); + } + + @Persist + public String getVmId() { + return vmId; + } + + @Persist + public void setVmId(String vmId) { + this.vmId = vmId; + } + + @Persist + public void setTimeStamp(long timestamp) { + this.timestamp = timestamp; + } + + @Persist + @Override + public long getTimeStamp() { + return timestamp; + } + + @Persist + public void setRule(String rule) { + this.rule = rule; + } + + @Persist + public String getRule() { + return rule; + } + + @Persist + public void setListenPort(int port) { + this.listenPort = port; + } + + @Persist + public int getListenPort() { + return listenPort; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/command/BytemanRequest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.common.command; + +import java.net.InetSocketAddress; + +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Request.RequestType; +import com.redhat.thermostat.storage.core.VmId; + +public class BytemanRequest { + + public static enum RequestAction { + LOAD_RULES(0), + UNLOAD_RULES(1), + ; + + private int actionId; + private RequestAction(int id) { + this.actionId = id; + } + + public static RequestAction fromIntString(String id) { + int intId; + try { + intId = Integer.parseInt(id); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Unknown action: " + id); + } + switch(intId) { + case(0): return LOAD_RULES; + case(1): return UNLOAD_RULES; + default: + throw new IllegalArgumentException("Unknown action: " + id); + } + } + + private String toIntString() { + return Integer.toString(actionId); + } + } + + private static final String CMD_CHANN_ACTION_NAME = "vm-byteman-instrument"; + private static final String RECEIVER = "com.redhat.thermostat.vm.byteman.agent.internal.BytemanRequestReceiver"; + + public static final String VM_ID_PARAM_NAME = "vm-id"; + public static final String ACTION_PARAM_NAME = "byteman-action"; + public static final String LISTEN_PORT_PARAM_NAME = "listen-port"; + public static final String RULE_PARAM_NAME = "byteman-rule"; + + public static Request create(InetSocketAddress address, VmId vmId, RequestAction action, int listenPort) { + Request req = new Request(RequestType.RESPONSE_EXPECTED, address); + req.setReceiver(RECEIVER); + req.setParameter(Request.ACTION, CMD_CHANN_ACTION_NAME); + req.setParameter(VM_ID_PARAM_NAME, vmId.get()); + req.setParameter(ACTION_PARAM_NAME, action.toIntString()); + req.setParameter(LISTEN_PORT_PARAM_NAME, Integer.toString(listenPort)); + return req; + } + + public static Request create(InetSocketAddress address, VmId vmId, RequestAction action, int listenPort, String rule) { + Request req = create(address, vmId, action, listenPort); + req.setParameter(RULE_PARAM_NAME, rule); + return req; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanDAOImpl.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,191 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.common.internal; + +import java.util.List; +import java.util.logging.Logger; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; + +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.PreparedStatement; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter; +import com.redhat.thermostat.storage.dao.AbstractDao; +import com.redhat.thermostat.storage.dao.AbstractDaoQuery; +import com.redhat.thermostat.storage.dao.AbstractDaoStatement; +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; + +@Component +@Service(value = VmBytemanDAO.class) +public class VmBytemanDAOImpl extends AbstractDao implements VmBytemanDAO { + + static final Key<String> MARKER = new Key<>("marker"); + static final Key<String> DATA = new Key<>("data"); + static final Key<String> RULE = new Key<>("rule"); + static final Key<Integer> PORT = new Key<>("listenPort"); + + static final Category<BytemanMetric> VM_BYTEMAN_METRICS_CATEGORY = new Category<>( + "vm-byteman-metrics", + BytemanMetric.class, + Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, + MARKER, DATA); + + static final Category<VmBytemanStatus> VM_BYTEMAN_STATUS_CATEGORY = new Category<>( + "vm-byteman-status", + VmBytemanStatus.class, + Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, + RULE, PORT); + + static final String REPLACE_OR_ADD_STATUS_DESC = "REPLACE " + VM_BYTEMAN_STATUS_CATEGORY.getName() + + " SET '" + Key.AGENT_ID.getName() + "' = ?s , " + + "'" + Key.VM_ID.getName() + "' = ?s , " + + "'" + Key.TIMESTAMP.getName() + "' = ?l , " + + "'" + RULE.getName() + "' = ?s , " + + "'" + PORT.getName() + "' = ?i WHERE " + + "'" + Key.VM_ID.getName() + "' = ?s"; + + static final String ADD_METRIC_DESC = "ADD " + VM_BYTEMAN_METRICS_CATEGORY.getName() + + " SET '" + Key.AGENT_ID.getName() + "' = ?s , " + + "'" + Key.VM_ID.getName() + "' = ?s , " + + "'" + Key.TIMESTAMP.getName() + "' = ?l , " + + "'" + MARKER.getName() + "' = ?s , " + + "'" + DATA.getName() + "' = ?s"; + + static final String QUERY_VM_BYTEMAN_STATUS = "QUERY " + VM_BYTEMAN_STATUS_CATEGORY.getName() + + " WHERE '" + Key.VM_ID.getName() + "' = ?s LIMIT 1"; + + @Reference + private Storage storage; + private VmTimeIntervalPojoListGetter<BytemanMetric> intervalGetter; + + public VmBytemanDAOImpl() { + // Default constructor for DS + } + + VmBytemanDAOImpl(Storage storage) { + bindStorage(storage); + } + + protected void bindStorage(Storage storage) { + this.storage = storage; + } + + protected void unbindStorage(Storage storage) { + this.storage = null; + } + + @Activate + private void activate() { + storage.registerCategory(VM_BYTEMAN_METRICS_CATEGORY); + storage.registerCategory(VM_BYTEMAN_STATUS_CATEGORY); + intervalGetter = new VmTimeIntervalPojoListGetter<>(storage, VM_BYTEMAN_METRICS_CATEGORY); + } + + @Override + public void addMetric(final BytemanMetric metric) { + executeStatement(new AbstractDaoStatement<BytemanMetric>(storage, VM_BYTEMAN_METRICS_CATEGORY, ADD_METRIC_DESC) { + + @Override + public PreparedStatement<BytemanMetric> customize(PreparedStatement<BytemanMetric> preparedStatement) { + preparedStatement.setString(0, metric.getAgentId()); + preparedStatement.setString(1, metric.getVmId()); + preparedStatement.setLong(2, metric.getTimeStamp()); + preparedStatement.setString(3, metric.getMarker()); + preparedStatement.setString(4, metric.getData()); + return preparedStatement; + } + }); + + } + + @Override + public List<BytemanMetric> findBytemanMetrics(Range<Long> timeRange, + VmId vmId, AgentId agentId) { + return intervalGetter.getLatest(agentId, vmId, timeRange.getMin(), timeRange.getMax()); + } + + @Override + protected Logger getLogger() { + return LoggingUtils.getLogger(VmBytemanDAOImpl.class); + } + + @Override + public void addOrReplaceBytemanStatus(final VmBytemanStatus status) { + executeStatement(new AbstractDaoStatement<VmBytemanStatus>(storage, VM_BYTEMAN_STATUS_CATEGORY, REPLACE_OR_ADD_STATUS_DESC) { + + @Override + public PreparedStatement<VmBytemanStatus> customize(PreparedStatement<VmBytemanStatus> preparedStatement) { + preparedStatement.setString(0, status.getAgentId()); + preparedStatement.setString(1, status.getVmId()); + preparedStatement.setLong(2, status.getTimeStamp()); + preparedStatement.setString(3, status.getRule()); + preparedStatement.setInt(4, status.getListenPort()); + preparedStatement.setString(5, status.getVmId()); + return preparedStatement; + } + }); + + } + + @Override + public VmBytemanStatus findBytemanStatus(final VmId vmId) { + List<VmBytemanStatus> result = executeQuery(new AbstractDaoQuery<VmBytemanStatus>(storage, VM_BYTEMAN_STATUS_CATEGORY, QUERY_VM_BYTEMAN_STATUS) { + + @Override + public PreparedStatement<VmBytemanStatus> customize(PreparedStatement<VmBytemanStatus> preparedStatement) { + preparedStatement.setString(0, vmId.get()); + return preparedStatement; + } + }).asList(); + if (result.isEmpty()) { + return null; + } + return result.get(0); + } + +}
--- a/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanMetricDAOCategoryRegistration.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanMetricDAOCategoryRegistration.java Mon May 09 09:39:38 2016 +0200 @@ -40,7 +40,6 @@ import java.util.Set; import com.redhat.thermostat.storage.core.auth.CategoryRegistration; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; public class VmBytemanMetricDAOCategoryRegistration implements CategoryRegistration { @@ -48,7 +47,8 @@ @Override public Set<String> getCategoryNames() { Set<String> categories = new HashSet<>(1); - categories.add(VmBytemanMetricDAO.CATEGORY.getName()); + categories.add(VmBytemanDAOImpl.VM_BYTEMAN_METRICS_CATEGORY.getName()); + categories.add(VmBytemanDAOImpl.VM_BYTEMAN_STATUS_CATEGORY.getName()); return categories; }
--- a/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanMetricDAOImpl.java Wed May 18 12:13:13 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.byteman.common.internal; - -import java.util.List; -import java.util.logging.Logger; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.storage.core.AgentId; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.PreparedStatement; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.core.VmId; -import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter; -import com.redhat.thermostat.storage.dao.AbstractDao; -import com.redhat.thermostat.storage.dao.AbstractDaoStatement; -import com.redhat.thermostat.vm.byteman.common.BytemanMetric; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; - -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Reference; -import org.apache.felix.scr.annotations.Service; - -@Component -@Service(value = VmBytemanMetricDAO.class) -public class VmBytemanMetricDAOImpl extends AbstractDao implements VmBytemanMetricDAO { - - static final String STATEMENT_DESCRIPTOR = "ADD " + CATEGORY.getName() + - " SET '" + Key.AGENT_ID.getName() + "' = ?s , " + - "'" + Key.VM_ID.getName() + "' = ?s , " + - "'" + Key.TIMESTAMP.getName() + "' = ?l , " + - "'" + MARKER.getName() + "' = ?s , " + - "'" + DATA.getName() + "' = ?s"; - - @Reference - private Storage storage; - private VmTimeIntervalPojoListGetter<BytemanMetric> intervalGetter; - - public VmBytemanMetricDAOImpl() { - // Default constructor for DS - } - - VmBytemanMetricDAOImpl(Storage storage) { - bindStorage(storage); - } - - protected void bindStorage(Storage storage) { - this.storage = storage; - } - - protected void unbindStorage(Storage storage) { - this.storage = null; - } - - @Activate - private void activate() { - storage.registerCategory(CATEGORY); - intervalGetter = new VmTimeIntervalPojoListGetter<>(storage, CATEGORY); - } - - @Override - public void putMetric(final BytemanMetric metric) { - executeStatement(new AbstractDaoStatement<BytemanMetric>(storage, CATEGORY, STATEMENT_DESCRIPTOR) { - - @Override - public PreparedStatement<BytemanMetric> customize(PreparedStatement<BytemanMetric> preparedStatement) { - preparedStatement.setString(0, metric.getAgentId()); - preparedStatement.setString(1, metric.getVmId()); - preparedStatement.setLong(2, metric.getTimeStamp()); - preparedStatement.setString(3, metric.getMarker()); - preparedStatement.setString(4, metric.getData()); - return preparedStatement; - } - }); - - } - - @Override - public List<BytemanMetric> findBytemanMetrics(long from, long to, - VmId vmId, AgentId agentId) { - return intervalGetter.getLatest(agentId, vmId, from, to); - } - - @Override - protected Logger getLogger() { - return LoggingUtils.getLogger(VmBytemanMetricDAOImpl.class); - } - -}
--- a/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanMetricDAOImplStatementDescriptorRegistration.java Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/common/src/main/java/com/redhat/thermostat/vm/byteman/common/internal/VmBytemanMetricDAOImplStatementDescriptorRegistration.java Mon May 09 09:39:38 2016 +0200 @@ -41,20 +41,21 @@ import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter; import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration; -import com.redhat.thermostat.vm.byteman.common.VmBytemanMetricDAO; public class VmBytemanMetricDAOImplStatementDescriptorRegistration implements StatementDescriptorRegistration { static final String intervalDescriptor = String.format( VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT, - VmBytemanMetricDAO.CATEGORY.getName()); + VmBytemanDAOImpl.VM_BYTEMAN_METRICS_CATEGORY.getName()); @Override public Set<String> getStatementDescriptors() { Set<String> descs = new HashSet<>(7); - descs.add(VmBytemanMetricDAOImpl.STATEMENT_DESCRIPTOR); + descs.add(VmBytemanDAOImpl.ADD_METRIC_DESC); descs.add(intervalDescriptor); + descs.add(VmBytemanDAOImpl.REPLACE_OR_ADD_STATUS_DESC); + descs.add(VmBytemanDAOImpl.QUERY_VM_BYTEMAN_STATUS); return descs; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/common/src/test/java/com/redhat/thermostat/vm/byteman/common/command/BytemanRequestTest.java Mon May 09 09:39:38 2016 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.byteman.common.command; + +import static org.junit.Assert.assertEquals; + +import java.net.InetSocketAddress; + +import org.junit.Test; + +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Request.RequestType; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +public class BytemanRequestTest { + + @Test + public void testCreateRequestWithRule() { + Request req = BytemanRequest.create(new InetSocketAddress("127.0.0.1", 12000), new VmId("vmId"), RequestAction.LOAD_RULES, 3, "foo-bar"); + assertEquals(RequestType.RESPONSE_EXPECTED, req.getType()); + } +}
--- a/vm-byteman/distribution/pom.xml Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/distribution/pom.xml Mon May 09 09:39:38 2016 +0200 @@ -98,6 +98,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-byteman-client-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.jboss.byteman</groupId> <artifactId>byteman-submit</artifactId> <version>${byteman.version}</version>
--- a/vm-byteman/distribution/thermostat-plugin.xml Wed May 18 12:13:13 2016 -0400 +++ b/vm-byteman/distribution/thermostat-plugin.xml Mon May 09 09:39:38 2016 +0200 @@ -37,6 +37,67 @@ --> <plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 http://icedtea.classpath.org/thermostat/docs/1.0/thermostat-plugin.xsd"> + <commands> + <command> + <name>byteman</name> + <summary>instrument a target VM with Byteman rules</summary> + <description> + Instrument a target VM with Byteman rules. + This command takes a subcommand that describes what to do. + 'load' loads rule as specified by the command option -r + into a VM. 'unload' unloads all loaded Byteman rules from a VM. + 'status' shows the currently loaded rules for a VM. 'show-metrics' + displays raw metrics gathered via an injected Byteman rule. + </description> + <arguments> + <argument>action</argument> + </arguments> + <options> + <option> + <long>vmId</long> + <short>v</short> + <argument>vm</argument> + <required>true</required> + <description>the ID of the VM to instrument</description> + </option> + <option> + <long>rules</long> + <short>r</short> + <argument>file</argument> + <required>false</required> + <description>a file with Byteman rules to load into a VM</description> + </option> + </options> + <environments> + <environment>cli</environment> + <environment>shell</environment> + </environments> + <bundles> + <bundle><symbolic-name>com.redhat.thermostat.client.cli</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>io.netty.common</symbolic-name><version>${netty.version}</version></bundle> + <bundle><symbolic-name>io.netty.buffer</symbolic-name><version>${netty.version}</version></bundle> + <bundle><symbolic-name>io.netty.codec</symbolic-name><version>${netty.version}</version></bundle> + <bundle><symbolic-name>io.netty.transport</symbolic-name><version>${netty.version}</version></bundle> + <bundle><symbolic-name>io.netty.handler</symbolic-name><version>${netty.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.client.command</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.common.command</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.vm.byteman.common</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.vm.byteman.client.cli</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.google.gson</symbolic-name><version>${gson.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.storage.mongodb</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.web.common</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.web.client</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>org.mongodb.mongo-java-driver</symbolic-name><version>${mongo-driver.osgi-version}</version></bundle> + <bundle><symbolic-name>org.apache.commons.beanutils</symbolic-name><version>${commons-beanutils.version}</version></bundle> + <bundle><symbolic-name>org.apache.commons.codec</symbolic-name><version>${commons-codec.osgi-version}</version></bundle> + <bundle><symbolic-name>org.apache.commons.collections</symbolic-name><version>${commons-collections.version}</version></bundle> + <bundle><symbolic-name>org.apache.commons.logging</symbolic-name><version>${commons-logging.version}</version></bundle> + <bundle><symbolic-name>org.apache.httpcomponents.httpcore</symbolic-name><version>${httpcomponents.core.version}</version></bundle> + <bundle><symbolic-name>org.apache.httpcomponents.httpclient</symbolic-name><version>${httpcomponents.client.version}</version></bundle> + <bundle><symbolic-name>${osgi.compendium.bundle.symbolic-name}</symbolic-name><version>${osgi.compendium.osgi-version}</version></bundle> + </bundles> + </command> + </commands> <extensions> <extension> <name>agent</name>