changeset 1506:6222ea3f5803

Kill-VM Command Addition reviewed-by: omajid review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-September/010859.html
author Jie Kang <jkang@redhat.com>
date Mon, 29 Sep 2014 15:14:32 -0400
parents 3aee8e9b54d9
children dd501e161176
files build-deps/pom.xml distribution/assembly/all-plugin-assembly.xml killvm/client-swing/pom.xml killvm/command/pom.xml killvm/command/src/main/java/com/redhat/thermostat/killvm/command/KillVMCommand.java killvm/command/src/main/java/com/redhat/thermostat/killvm/command/internal/Activator.java killvm/command/src/main/java/com/redhat/thermostat/killvm/command/internal/ShellVMKilledListener.java killvm/command/src/main/java/com/redhat/thermostat/killvm/command/locale/LocaleResources.java killvm/command/src/main/resources/com/redhat/thermostat/killvm/command/locale/strings.properties killvm/command/src/test/java/com/redhat/thermostat/killvm/command/KillVmCommandTest.java killvm/command/src/test/java/com/redhat/thermostat/killvm/command/internal/ActivatorTest.java killvm/command/src/test/java/com/redhat/thermostat/killvm/command/internal/ShellVMKilledListenerTest.java killvm/command/src/test/java/com/redhat/thermostat/killvm/command/locale/LocaleResourcesTest.java killvm/distribution/pom.xml killvm/distribution/thermostat-plugin.xml killvm/pom.xml
diffstat 16 files changed, 1185 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/build-deps/pom.xml	Fri Sep 26 15:19:39 2014 +0200
+++ b/build-deps/pom.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -277,5 +277,11 @@
       <version>${project.version}</version>
       <type>zip</type>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-killvm-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+    </dependency>
   </dependencies>
 </project>
--- a/distribution/assembly/all-plugin-assembly.xml	Fri Sep 26 15:19:39 2014 +0200
+++ b/distribution/assembly/all-plugin-assembly.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -66,6 +66,7 @@
         <include>com.redhat.thermostat:thermostat-vm-overview-distribution</include>
         <include>com.redhat.thermostat:thermostat-schema-info-distribution</include>
         <include>com.redhat.thermostat:thermostat-web-endpoint-distribution</include>
+        <include>com.redhat.thermostat:thermostat-killvm-distribution</include>
       </includes>
     </dependencySet>  
   </dependencySets>
--- a/killvm/client-swing/pom.xml	Fri Sep 26 15:19:39 2014 +0200
+++ b/killvm/client-swing/pom.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -24,9 +24,9 @@
               com.redhat.thermostat.killvm.client.internal,
               com.redhat.thermostat.killvm.client.locale,
             </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
           </instructions>
-          <!-- Do not autogenerate uses clauses in Manifests -->
-          <_nouses>true</_nouses>
         </configuration>
       </plugin>
     </plugins>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/pom.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2014 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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-killvm</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-killvm-command</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat Command Kill VM</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-Activator>com.redhat.thermostat.killvm.command.internal.Activator</Bundle-Activator>
+            <Bundle-SymbolicName>com.redhat.thermostat.killvm.command</Bundle-SymbolicName>
+            <Export-Package>
+              com.redhat.thermostat.killvm.command,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.killvm.command.internal,
+              com.redhat.thermostat.killvm.command.locale,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </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-command</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-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </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-shared-config</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/KillVMCommand.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2012-2014 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.killvm.command;
+
+import java.net.InetSocketAddress;
+
+import com.redhat.thermostat.client.cli.HostVMArguments;
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.cli.AbstractCommand;
+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.killvm.command.internal.ShellVMKilledListener;
+import com.redhat.thermostat.killvm.command.locale.LocaleResources;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+
+public class KillVMCommand extends AbstractCommand {
+
+    private static final String RECEIVER = "com.redhat.thermostat.killvm.agent.internal.KillVmReceiver";
+    private static final String CMD_CHANNEL_ACTION_NAME = "killvm";
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private final ShellVMKilledListener listener;
+
+    private HostInfoDAO hostInfoDAO;
+    private VmInfoDAO vmInfoDAO;
+    private AgentInfoDAO agentInfoDAO;
+    private RequestQueue requestQueue;
+
+    public KillVMCommand(ShellVMKilledListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        requireNotNull(vmInfoDAO, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE));
+        requireNotNull(hostInfoDAO, translator.localize(LocaleResources.HOST_SERVICE_UNAVAILABLE));
+        requireNotNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE));
+        requireNotNull(requestQueue, translator.localize(LocaleResources.QUEUE_SERVICE_UNAVAILABLE));
+
+        listener.setOut(ctx.getConsole().getOutput());
+        listener.setErr(ctx.getConsole().getError());
+
+        HostVMArguments args = new HostVMArguments(ctx.getArguments(), true, true);
+
+        attemptToKillVM(args.getVM());
+    }
+
+    private void requireNotNull(Object object, LocalizedString message) throws CommandException {
+        if (object == null) {
+            throw new CommandException(message);
+        }
+    }
+
+    private void attemptToKillVM(VmRef vmRef) throws CommandException {
+        VmInfo result = vmInfoDAO.getVmInfo(vmRef);
+
+        if (result == null) {
+            throw new CommandException(translator.localize(LocaleResources.VM_NOT_FOUND, vmRef.getVmId()));
+        } else {
+            sendKillRequest(vmRef.getHostRef(), result.getVmPid());
+        }
+    }
+
+    private void sendKillRequest(HostRef hostRef, int vmPid) throws CommandException {
+        InetSocketAddress target = getAddressFromHost(hostRef);
+
+        Request murderer = setupMurderer(target, vmPid);
+
+        requestQueue.putRequest(murderer);
+
+        waitForListenerResponse();
+    }
+
+    private InetSocketAddress getAddressFromHost(HostRef hostRef) throws CommandException {
+        String [] hostAndPort = getHostAndPort(hostRef);
+        return new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1]));
+    }
+
+    private Request setupMurderer(InetSocketAddress target, int vmPid) {
+        Request murderer = new Request(Request.RequestType.RESPONSE_EXPECTED, target);
+        murderer.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
+        murderer.setParameter("vm-pid", String.valueOf(vmPid));
+        murderer.setReceiver(RECEIVER);
+        murderer.addListener(listener);
+
+        return murderer;
+    }
+
+    private String[] getHostAndPort(HostRef hostRef) throws CommandException {
+        String address = agentInfoDAO.getAgentInformation(hostRef).getConfigListenAddress();
+        return address.split(":");
+    }
+
+    private void waitForListenerResponse() throws CommandException {
+        try {
+            listener.await(1000l);
+        } catch (InterruptedException e) {
+            throw new CommandException(translator.localize(LocaleResources.KILL_INTERRUPTED));
+        }
+    }
+
+    public void setHostInfoDAO(HostInfoDAO hostInfoDAO) {
+        this.hostInfoDAO = hostInfoDAO;
+    }
+
+    public void setVmInfoDAO(VmInfoDAO vmInfoDAO) {
+        this.vmInfoDAO = vmInfoDAO;
+    }
+
+    public void setAgentInfoDAO(AgentInfoDAO agentInfoDAO) {
+        this.agentInfoDAO = agentInfoDAO;
+    }
+
+    public void setRequestQueue(RequestQueue requestQueue) {
+        this.requestQueue = requestQueue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/internal/Activator.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.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.cli.Command;
+import com.redhat.thermostat.killvm.command.KillVMCommand;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+
+public class Activator implements BundleActivator {
+
+    private ServiceRegistration killCommandRegistration;
+    private MultipleServiceTracker serviceTracker;
+    private KillVMCommand command = new KillVMCommand(new ShellVMKilledListener());
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] serviceDeps = new Class<?>[] {
+                HostInfoDAO.class,
+                AgentInfoDAO.class,
+                VmInfoDAO.class,
+                RequestQueue.class,
+        };
+
+        serviceTracker = new MultipleServiceTracker(context, serviceDeps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                HostInfoDAO hostDAO = (HostInfoDAO) services.get(HostInfoDAO.class.getName());
+                AgentInfoDAO agentDao = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName());
+                VmInfoDAO vmDao = (VmInfoDAO) services.get(VmInfoDAO.class.getName());
+                RequestQueue requestQueue = (RequestQueue) services.get(RequestQueue.class.getName());
+
+                command.setAgentInfoDAO(agentDao);
+                command.setHostInfoDAO(hostDAO);
+                command.setVmInfoDAO(vmDao);
+                command.setRequestQueue(requestQueue);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                command.setAgentInfoDAO(null);
+                command.setHostInfoDAO(null);
+                command.setVmInfoDAO(null);
+                command.setRequestQueue(null);
+            }
+        });
+
+        serviceTracker.open();
+
+        Hashtable<String,String> properties = new Hashtable<>();
+        properties.put(Command.NAME, "kill-vm");
+        killCommandRegistration = context.registerService(Command.class.getName(), command, properties);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        serviceTracker.close();
+        killCommandRegistration.unregister();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/internal/ShellVMKilledListener.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.internal;
+
+import java.io.PrintStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+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.common.utils.LoggingUtils;
+
+public class ShellVMKilledListener implements RequestResponseListener {
+
+    private static final Logger logger = LoggingUtils
+            .getLogger(ShellVMKilledListener.class);
+    private boolean complete = false;
+
+    private PrintStream out;
+    private PrintStream err;
+
+    private CountDownLatch latch = new CountDownLatch(1);
+
+    @Override
+    public void fireComplete(Request request, Response response) {
+        String message;
+        switch (response.getType()) {
+            case ERROR:
+                String pid = request.getParameter("vm-pid");
+                message = "Kill request error for VM ID " + pid;
+                logger.log(Level.SEVERE, message);
+                if (err != null) {
+                    err.println(message);
+                }
+                break;
+            case OK:
+                message = "VM with id " + request.getParameter("vm-pid") + " killed.";
+                logger.log(Level.INFO, message);
+                if (out != null) {
+                    out.println(message);
+                }
+                break;
+            default:
+                message = "Unknown result from KILL VM command.";
+                logger.log(Level.WARNING, message);
+                if (out != null) {
+                    out.println(message);
+                }
+                break;
+        }
+        latch.countDown();
+    }
+
+    public void setOut(PrintStream out) {
+        this.out = out;
+    }
+
+    public void setErr(PrintStream err) {
+        this.err = err;
+    }
+
+    public synchronized void await(long timeout) throws InterruptedException {
+        this.latch.await(timeout, TimeUnit.MILLISECONDS);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/main/java/com/redhat/thermostat/killvm/command/locale/LocaleResources.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources {
+
+    HOST_SERVICE_UNAVAILABLE,
+    VM_SERVICE_UNAVAILABLE,
+    AGENT_SERVICE_UNAVAILABLE,
+    QUEUE_SERVICE_UNAVAILABLE,
+    KILL_INTERRUPTED,
+    VM_NOT_FOUND;
+
+    public static final String RESOURCE_BUNDLE =
+            "com.redhat.thermostat.killvm.command.locale.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/main/resources/com/redhat/thermostat/killvm/command/locale/strings.properties	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,6 @@
+HOST_SERVICE_UNAVAILABLE = Unable to get host information (HostInfoDAO is unavailable)
+VM_SERVICE_UNAVAILABLE = Unable to get vm information (VmInfoDAO is unavailable)
+AGENT_SERVICE_UNAVAILABLE = Unable to get agent information (AgentInfoDAO is unavailable)
+QUEUE_SERVICE_UNAVAILABLE = Unable to get queue information (RequestQueue is unavailable)
+KILL_INTERRUPTED = Command interrupted while waiting for response from murderer
+VM_NOT_FOUND = VM with ID {0} not found.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/test/java/com/redhat/thermostat/killvm/command/KillVmCommandTest.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2012-2014 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.killvm.command;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.SimpleArguments;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.killvm.command.internal.ShellVMKilledListener;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+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.test.TestCommandContextFactory;
+
+public class KillVmCommandTest {
+
+    private TestCommandContextFactory cmdCtxFactory;
+    private KillVMCommand cmd;
+
+    private HostInfoDAO hostInfoDAO;
+    private VmInfoDAO vmInfoDAO;
+    private AgentInfoDAO agentInfoDAO;
+
+    private ShellVMKilledListener listener;
+    private RequestQueue requestQueue;
+
+    private HostRef hostRef;
+    private VmRef vmRef;
+
+
+    @Before
+    public void setup() {
+        cmdCtxFactory = new TestCommandContextFactory();
+
+        hostInfoDAO = mock(HostInfoDAO.class);
+        vmInfoDAO = mock(VmInfoDAO.class);
+        agentInfoDAO = mock(AgentInfoDAO.class);
+
+        listener = mock(ShellVMKilledListener.class);
+        requestQueue = mock(RequestQueue.class);
+
+        cmd = new KillVMCommand(listener);
+        cmd.setVmInfoDAO(vmInfoDAO);
+        cmd.setAgentInfoDAO(agentInfoDAO);
+        cmd.setHostInfoDAO(hostInfoDAO);
+        cmd.setRequestQueue(requestQueue);
+
+        hostRef = new HostRef("10", "dummy");
+        vmRef = new VmRef(hostRef, "liveVM", -1, "dummy");
+
+        VmInfo vmInfo = mock(VmInfo.class);
+        when(vmInfo.getVmPid()).thenReturn(-1);
+
+        when(vmInfoDAO.getVmInfo(vmRef)).thenReturn(vmInfo);
+    }
+
+    @Test
+    public void testKillLiveVM() throws CommandException, InterruptedException {
+        String vmId = "liveVM";
+
+        AgentInformation agent = mock(AgentInformation.class);
+        when(agent.getConfigListenAddress()).thenReturn("addr:10");
+        when(agentInfoDAO.getAgentInformation(hostRef)).thenReturn(agent);
+
+        CommandContext ctx = createContext(vmId, hostRef.getAgentId());
+
+        final boolean[] complete = {false};
+
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                complete[0] = true;
+                return null;
+            }
+        }).when(requestQueue).putRequest(any(Request.class));
+
+        cmd.run(ctx);
+
+        assertTrue(complete[0]);
+    }
+
+    @Test(expected = CommandException.class)
+    public void testKillNonexistentVM() throws CommandException {
+        String vmId = "nonexistentVM";
+        CommandContext ctx = createContext(vmId, hostRef.getAgentId());
+        cmd.run(ctx);
+    }
+
+    @Test(expected = CommandException.class)
+    public void testKillNonexistentHost() throws  CommandException {
+        String vmId = "liveVM";
+        CommandContext ctx = createContext(vmId, "nonexistentHost");
+        cmd.run(ctx);
+    }
+
+    @Test(expected = CommandException.class)
+    public void testNoVMArgument() throws CommandException {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("hostId", "hostId");
+        CommandContext ctx = cmdCtxFactory.createContext(args);
+
+        cmd.run(ctx);
+    }
+
+    @Test(expected =  CommandException.class)
+    public void testNoHostArgument() throws CommandException {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", "vmId");
+        CommandContext ctx = cmdCtxFactory.createContext(args);
+
+        cmd.run(ctx);
+    }
+
+    public CommandContext createContext(String vmId, String hostId) {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", vmId);
+        args.addArgument("hostId", hostId);
+        return cmdCtxFactory.createContext(args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/test/java/com/redhat/thermostat/killvm/command/internal/ActivatorTest.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.internal;
+
+import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.killvm.command.KillVMCommand;
+import com.redhat.thermostat.testutils.StubBundleContext;
+
+public class ActivatorTest {
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertCommandIsRegistered(context, "kill-vm", KillVMCommand.class);
+
+        activator.stop(context);
+
+        assertEquals(0, context.getServiceListeners().size());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/test/java/com/redhat/thermostat/killvm/command/internal/ShellVMKilledListenerTest.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class ShellVMKilledListenerTest {
+
+    private final ShellVMKilledListener listener = new ShellVMKilledListener();
+    private static final Logger logger = LoggingUtils
+            .getLogger(ShellVMKilledListener.class);
+
+
+    @Test
+    public void testSuccessfulKillResponse() {
+        Request request = mock(Request.class);
+
+        Response resp = new Response(Response.ResponseType.OK);
+
+        Handler handler = mock(Handler.class);
+        logger.addHandler(handler);
+
+        final boolean[] complete = {false};
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                LogRecord log = (LogRecord)invocation.getArguments()[0];
+                if (log.getLevel().equals(Level.INFO)) {
+                    complete[0] = true;
+                }
+                return null;
+            }
+        }).when(handler).publish(any(LogRecord.class));
+
+        listener.fireComplete(request, resp);
+
+        assertTrue(complete[0]);
+    }
+
+    @Test
+    public void testErrorResponse() {
+        Request request = mock(Request.class);
+        Response resp = new Response(Response.ResponseType.ERROR);
+
+        Handler handler = mock(Handler.class);
+        logger.addHandler(handler);
+
+        final boolean[] complete = {false};
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                LogRecord log = (LogRecord)invocation.getArguments()[0];
+                if (log.getLevel().equals(Level.SEVERE)) {
+                    complete[0] = true;
+                }
+                return null;
+            }
+        }).when(handler).publish(any(LogRecord.class));
+
+        listener.fireComplete(request, resp);
+
+        assertTrue(complete[0]);
+    }
+
+    @Test
+    public void testDefaultResponse() {
+        Request request = mock(Request.class);
+        Response resp = new Response(Response.ResponseType.NOK);
+
+        Handler handler = mock(Handler.class);
+        logger.addHandler(handler);
+
+        final boolean[] complete = {false};
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                LogRecord log = (LogRecord)invocation.getArguments()[0];
+                if (log.getLevel().equals(Level.WARNING)) {
+                    complete[0] = true;
+                }
+                return null;
+            }
+        }).when(handler).publish(any(LogRecord.class));
+
+        listener.fireComplete(request, resp);
+
+        assertTrue(complete[0]);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/command/src/test/java/com/redhat/thermostat/killvm/command/locale/LocaleResourcesTest.java	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2014 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.killvm.command.locale;
+
+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/killvm/distribution/pom.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2014 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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-killvm</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-killvm-distribution</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat KillVM Distribution</name>
+
+  <properties>
+    <thermostat.plugin>killvm</thermostat.plugin>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-assembly</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>plugin-assembly</descriptorRef>
+          </descriptorRefs>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>assemble-plugin</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <!-- Explicitly list all plug-in artifacts, transitive dependencies
+       are not included in assembly. -->
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-killvm-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/killvm/distribution/thermostat-plugin.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright 2012-2014 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.
+
+-->
+<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 thermostat-plugin.xsd">
+  <commands>
+    <command>
+      <name>kill-vm</name>
+      <description>Attempts to kill the vm specified.</description>
+      <options>
+        <option>
+          <long>vmId</long>
+          <short>v</short>
+          <argument>vmId</argument>
+          <required>true</required>
+          <description>the ID of the VM</description>
+        </option>
+        <option>
+          <long>hostId</long>
+          <short>h</short>
+          <argument>host</argument>
+          <required>true</required>
+          <description>the ID of the Host</description>
+        </option>
+      </options>
+      <environments>
+        <environment>cli</environment>
+        <environment>shell</environment>
+      </environments>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.killvm.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.client.core</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.client.command</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.client.cli</symbolic-name><version>${project.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>
+        <bundle><symbolic-name>com.google.gson</symbolic-name><version>${gson.version}</version></bundle>
+        <bundle><symbolic-name>org.jboss.netty</symbolic-name><version>${netty.version}</version></bundle>
+      </bundles>
+    </command>
+  </commands>
+</plugin>
+
--- a/killvm/pom.xml	Fri Sep 26 15:19:39 2014 +0200
+++ b/killvm/pom.xml	Mon Sep 29 15:14:32 2014 -0400
@@ -61,6 +61,8 @@
   <modules>
     <module>client-swing</module>
     <module>agent</module>
+    <module>command</module>
+    <module>distribution</module>
   </modules>
 </project>