changeset 1961:2c970a73043a

Add find-vm command Added 'find-vm' command to cli and shell which allows a user to specify a set of criteria about active hosts and VMs, and print out a list or table of resulting VMs which match all of the given criteria. Reviewed-by: jerboaa, jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-July/014791.html Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019718.html PR3040
author Andrew Azores <aazores@redhat.com>
date Fri, 24 Jul 2015 13:06:09 -0400
parents 7e9e2f81b86c
children 08b645d025d9
files distribution/assembly/all-plugin-assembly.xml distribution/pom.xml pom.xml vm-find/command/pom.xml vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/AbstractMatcher.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/Activator.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/CriterionMatcher.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/FindVmCommand.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/HostCriterion.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/HostMatcher.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/Matcher.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/ResultsRenderer.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/VmCriterion.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/VmMatcher.java vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/locale/LocaleResources.java vm-find/command/src/main/resources/com/redhat/thermostat/vm/find/command/locale/strings.properties vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/AbstractMatcherTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/ActivatorTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/FindVmCommandTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/HostCriterionTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/HostMatcherTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/ResultsRendererTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/VmCriterionTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/VmMatcherTest.java vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/locale/LocaleResourcesTest.java vm-find/distribution/pom.xml vm-find/distribution/thermostat-plugin.xml vm-find/pom.xml
diffstat 28 files changed, 2871 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/distribution/assembly/all-plugin-assembly.xml	Tue Jul 21 16:10:45 2015 -0400
+++ b/distribution/assembly/all-plugin-assembly.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -61,6 +61,7 @@
         <include>com.redhat.thermostat:thermostat-local-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-classstat-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-cpu-distribution</include>
+        <include>com.redhat.thermostat:thermostat-vm-find-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-gc-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-heap-analysis-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-jmx-distribution</include>
--- a/distribution/pom.xml	Tue Jul 21 16:10:45 2015 -0400
+++ b/distribution/pom.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -514,6 +514,12 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-find-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-gc-distribution</artifactId>
       <version>${project.version}</version>
       <type>zip</type>
--- a/pom.xml	Tue Jul 21 16:10:45 2015 -0400
+++ b/pom.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -66,7 +66,7 @@
       <properties>
         <!-- define it empty so as to NOT exclude the PerformanceTests
              categorized tests. -->
-        <surefire-perftests-exclusion />
+        <surefire-perftests-exclusion/>
       </properties>
     </profile>
     <profile>
@@ -208,7 +208,7 @@
 
     <!-- see web/common/pom.xml for an example as to how this is used -->
     <surefire-perftests-exclusion>com.redhat.thermostat.testutils.PerformanceTest</surefire-perftests-exclusion>
-    <surefire-argline /> <!-- intentionally empty -->
+    <surefire-argline/> <!-- intentionally empty -->
     <mongodb.dev.username> mongodevuser </mongodb.dev.username>
     <mongodb.dev.password> mongodevpassword </mongodb.dev.password>
   </properties>
@@ -235,6 +235,7 @@
     <module>host-memory</module>
     <module>vm-overview</module>
     <module>vm-cpu</module>
+    <module>vm-find</module>
     <module>vm-gc</module>
     <module>vm-classstat</module>
     <module>vm-memory</module>
@@ -358,7 +359,7 @@
                     </goals>
                   </pluginExecutionFilter>
                   <action>
-                    <ignore></ignore>
+                    <ignore/>
                   </action>
                 </pluginExecution>
                 <pluginExecution>
@@ -375,7 +376,7 @@
                     </goals>
                   </pluginExecutionFilter>
                   <action>
-                    <ignore></ignore>
+                    <ignore/>
                   </action>
                 </pluginExecution>
               </pluginExecutions>
@@ -583,5 +584,4 @@
     <url>http://icedtea.classpath.org/hg/thermostat</url>
   </scm>
 
-</project>
-
+</project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/pom.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,132 @@
+<?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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-vm-find</artifactId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-find-command</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat vm-find plug-in</name>
+
+  <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>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </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-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </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-storage-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>
+  </dependencies>
+
+  <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-SymbolicName>com.redhat.thermostat.vm.find.command</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.find.command.internal.Activator</Bundle-Activator>
+            <Export-Package/>
+            <Private-Package>
+              com.redhat.thermostat.vm.find.command.internal,
+              com.redhat.thermostat.vm.find.command.locale
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/AbstractMatcher.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,59 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.Pojo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+abstract class AbstractMatcher<T extends Pojo> implements Matcher<T> {
+
+    protected final Map<CriterionMatcher<T, String>, String> criteriaMap = new HashMap<>();
+
+    @Override
+    public boolean match(T t) {
+        for (Map.Entry<? extends CriterionMatcher<T, String>, String> entry : criteriaMap.entrySet()) {
+            boolean match = entry.getKey().match(t, entry.getValue());
+            if (!match) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/Activator.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,99 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.cli.Command;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Registers the {@link FindVmCommand} with Thermostat.
+ */
+public class Activator implements BundleActivator {
+
+    private final FindVmCommand findVmCommand = new FindVmCommand();
+
+    private MultipleServiceTracker serviceTracker;
+    private ServiceRegistration serviceRegistration;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+            AgentInfoDAO.class,
+            HostInfoDAO.class,
+            VmInfoDAO.class,
+        };
+
+        serviceTracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                AgentInfoDAO agentInfoDAO = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName());
+                HostInfoDAO hostInfoDAO = (HostInfoDAO) services.get(HostInfoDAO.class.getName());
+                VmInfoDAO vmInfoDAO = (VmInfoDAO) services.get(VmInfoDAO.class.getName());
+
+                findVmCommand.setAgentInfoDAO(agentInfoDAO);
+                findVmCommand.setHostInfoDAO(hostInfoDAO);
+                findVmCommand.setVmInfoDAO(vmInfoDAO);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                findVmCommand.servicesUnavailable();
+            }
+        });
+
+        serviceTracker.open();
+
+        Hashtable<String,String> properties = new Hashtable<>();
+        properties.put(Command.NAME, FindVmCommand.REGISTER_NAME);
+        serviceRegistration = context.registerService(Command.class.getName(), findVmCommand, properties);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        serviceTracker.close();
+        serviceRegistration.unregister();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/CriterionMatcher.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.Pojo;
+
+interface CriterionMatcher<T extends Pojo, U> {
+
+    boolean match(T t, U u);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/FindVmCommand.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,195 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.common.Pair;
+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.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.AgentInformation;
+import com.redhat.thermostat.storage.model.HostInfo;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.vm.find.command.locale.LocaleResources;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class FindVmCommand extends AbstractCommand {
+
+    static final String REGISTER_NAME = "find-vm";
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createTranslator();
+
+    private AgentInfoDAO agentInfoDAO;
+    private HostInfoDAO hostInfoDAO;
+    private VmInfoDAO vmInfoDAO;
+    private CountDownLatch servicesLatch = new CountDownLatch(3);
+    
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        try {
+            servicesLatch.await(500, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new CommandException(translator.localize(LocaleResources.COMMAND_INTERRUPTED));
+        }
+
+        requireNonNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE));
+        requireNonNull(hostInfoDAO, translator.localize(LocaleResources.HOST_SERVICE_UNAVAILABLE));
+        requireNonNull(vmInfoDAO, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE));
+
+        List<AgentInformation> agentsToSearch = getAgentsToSearch(ctx.getArguments());
+
+        Map<String, String> hostCriteria = getHostCriteria(ctx.getArguments());
+        Map<String, String> vmCriteria = getVmCriteria(ctx.getArguments());
+
+        if (hostCriteria.isEmpty() && vmCriteria.isEmpty()) {
+            throw new CommandException(translator.localize(LocaleResources.NO_CRITERIA_GIVEN));
+        }
+
+        HostMatcher hostMatcher = new HostMatcher(hostCriteria);
+        VmMatcher vmMatcher = new VmMatcher(vmCriteria);
+
+        List<Pair<HostInfo, VmInfo>> results = performSearch(agentsToSearch, hostMatcher, vmMatcher);
+
+        ResultsRenderer resultsRenderer = new ResultsRenderer(ctx.getArguments());
+        resultsRenderer.print(ctx.getConsole().getOutput(), results);
+    }
+
+    List<AgentInformation> getAgentsToSearch(Arguments arguments) {
+        List<AgentInformation> aliveAgents;
+        if (arguments.hasArgument("agentId")) {
+            String agentId = arguments.getArgument("agentId");
+
+            aliveAgents = Collections.singletonList(agentInfoDAO.getAgentInformation(new HostRef(agentId, "dummy")));
+        } else {
+            aliveAgents = agentInfoDAO.getAliveAgents();
+        }
+        return aliveAgents;
+    }
+
+    static Map<String, String> getHostCriteria(Arguments arguments) {
+        Map<String, String> hostCriteria = new HashMap<>();
+        for (HostCriterion criterion : HostCriterion.values()) {
+            if (arguments.hasArgument(criterion.getCliSwitch())) {
+                hostCriteria.put(criterion.getCliSwitch(), arguments.getArgument(criterion.getCliSwitch()));
+            }
+        }
+        return hostCriteria;
+    }
+
+    static Map<String, String> getVmCriteria(Arguments arguments) {
+        Map<String, String> vmCriteria = new HashMap<>();
+        for (VmCriterion criterion : VmCriterion.values()) {
+            if (arguments.hasArgument(criterion.getCliSwitch())) {
+                vmCriteria.put(criterion.getCliSwitch(), arguments.getArgument(criterion.getCliSwitch()));
+            }
+        }
+        return vmCriteria;
+    }
+
+    List<Pair<HostInfo, VmInfo>> performSearch(Iterable<AgentInformation> agents, HostMatcher hostMatcher, VmMatcher vmMatcher) {
+        List<Pair<HostInfo, VmInfo>> pairs = new ArrayList<>();
+        for (AgentInformation agentInformation : filterAgents(agents, hostMatcher)) {
+            HostInfo hostInfo = getHostInfo(agentInformation);
+            List<VmInfo> matchingVms = getMatchingVms(agentInformation, vmMatcher);
+            for (VmInfo vm : matchingVms) {
+                pairs.add(new Pair<>(hostInfo, vm));
+            }
+        }
+        return pairs;
+    }
+
+    List<AgentInformation> filterAgents(Iterable<AgentInformation> agents, HostMatcher hostMatcher) {
+        List<AgentInformation> list = new ArrayList<>();
+        for (AgentInformation agent : agents) {
+            HostInfo hostInfo = hostInfoDAO.getHostInfo(new HostRef(agent.getAgentId(), "dummy"));
+            if (hostMatcher.match(hostInfo)) {
+                list.add(agent);
+            }
+        }
+        return list;
+    }
+
+    HostInfo getHostInfo(AgentInformation agentInformation) {
+        return hostInfoDAO.getHostInfo(new HostRef(agentInformation.getAgentId(), "dummy"));
+    }
+
+    List<VmInfo> getMatchingVms(AgentInformation agent, VmMatcher vmMatcher) {
+        List<VmInfo> list = new ArrayList<>();
+        for (VmRef vmRef : vmInfoDAO.getVMs(new HostRef(agent.getAgentId(), "dummy"))) {
+            VmInfo vmInfo = vmInfoDAO.getVmInfo(vmRef);
+            if (vmMatcher.match(vmInfo)) {
+                list.add(vmInfo);
+            }
+        }
+        return list;
+    }
+
+    public void setAgentInfoDAO(AgentInfoDAO agentInfoDAO) {
+        this.agentInfoDAO = agentInfoDAO;
+        servicesLatch.countDown();
+    }
+
+    public void setHostInfoDAO(HostInfoDAO hostInfoDAO) {
+        this.hostInfoDAO = hostInfoDAO;
+        servicesLatch.countDown();
+    }
+
+    public void setVmInfoDAO(VmInfoDAO vmInfoDAO) {
+        this.vmInfoDAO = vmInfoDAO;
+        servicesLatch.countDown();
+    }
+
+    public void servicesUnavailable() {
+        setAgentInfoDAO(null);
+        setHostInfoDAO(null);
+        setVmInfoDAO(null);
+        servicesLatch = new CountDownLatch(2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/HostCriterion.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,97 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.HostInfo;
+
+import java.util.NoSuchElementException;
+
+enum HostCriterion implements CriterionMatcher<HostInfo, String> {
+    HOSTNAME("hostname", new HostnameMatcher()),
+    OS_KERNEL("oskernel", new OsKernelMatcher()),
+    OS_NAME("osname", new OsNameMatcher()),
+    ;
+
+    private final String cliSwitch;
+    private final CriterionMatcher<HostInfo, String> criterionMatcher;
+
+    HostCriterion(String cliSwitch, CriterionMatcher<HostInfo, String> criterionMatcher) {
+        this.cliSwitch = cliSwitch;
+        this.criterionMatcher = criterionMatcher;
+    }
+
+    @Override
+    public boolean match(HostInfo hostInfo, String value) {
+        return this.criterionMatcher.match(hostInfo, value);
+    }
+
+    String getCliSwitch() {
+        return cliSwitch;
+    }
+
+    static HostCriterion fromString(String string) {
+        for (HostCriterion criterion : HostCriterion.values()) {
+            if (criterion.cliSwitch.equals(string)) {
+                return criterion;
+            }
+        }
+        throw new IllegalArgumentException(string + " is not a legal HostCriterion");
+    }
+
+    static class HostnameMatcher implements CriterionMatcher<HostInfo, String> {
+        @Override
+        public boolean match(HostInfo hostInfo, String s) {
+            return hostInfo.getHostname().equals(s);
+        }
+    }
+
+    static class OsKernelMatcher implements CriterionMatcher<HostInfo, String> {
+        @Override
+        public boolean match(HostInfo hostInfo, String s) {
+            return hostInfo.getOsKernel().equals(s) || hostInfo.getOsKernel().contains(s);
+        }
+    }
+
+    static class OsNameMatcher implements CriterionMatcher<HostInfo, String> {
+        @Override
+        public boolean match(HostInfo hostInfo, String s) {
+            return hostInfo.getOsName().equals(s) || hostInfo.getOsName().contains(s);
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/HostMatcher.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,52 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.HostInfo;
+
+import java.util.Map;
+
+class HostMatcher extends AbstractMatcher<HostInfo> {
+
+    HostMatcher(Map<String, String> criteriaMap) {
+        for (Map.Entry<String, String> entry : criteriaMap.entrySet()) {
+            HostCriterion hostCriterion = HostCriterion.fromString(entry.getKey());
+            this.criteriaMap.put(hostCriterion, entry.getValue());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/Matcher.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,43 @@
+/*
+ * 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.find.command.internal;
+
+interface Matcher<T> {
+
+    boolean match(T t);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/ResultsRenderer.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,220 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.SortedTableRenderer;
+import com.redhat.thermostat.storage.model.HostInfo;
+import com.redhat.thermostat.storage.model.VmInfo;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+class ResultsRenderer {
+
+    enum Field {
+        VM_ID("show-vm-ids", new VmIdFieldAdapter()),
+        MAINCLASS("show-mainclasses", new MainClassFieldAdapter()),
+        VMNAME("show-vmnames", new VmNameFieldAdapter()),
+        JAVAVERSION("show-javaversions", new JavaVersionFieldAdapter()),
+        VMVERSION("show-vmversions", new VmVersionFieldAdapter()),
+        PID("show-pids", new PidFieldAdapter()),
+        USERNAME("show-usernames", new UsernameFieldAdapter()),
+        HOSTNAME("show-hostnames", new HostnameFieldAdapter()),
+        OSNAME("show-osnames", new OsNameFieldAdapter()),
+        OSKERNEL("show-oskernels", new OsKernelFieldAdapter()),
+        ;
+
+        private final String cliSwitch;
+        private final FieldAdapter fieldAdapter;
+
+        Field(String cliSwitch, FieldAdapter fieldAdapter) {
+            this.cliSwitch = cliSwitch;
+            this.fieldAdapter = fieldAdapter;
+        }
+
+        String getCliSwitch() {
+            return cliSwitch;
+        }
+
+        String getAdaptedField(Pair<HostInfo, VmInfo> pair) {
+            return fieldAdapter.map(pair);
+        }
+    }
+
+    static final String ENABLE_ALL_FIELDS_FLAG = "show-all";
+    private static final Set<Field> DEFAULT_ENABLED_FIELDS = Collections.unmodifiableSet(EnumSet.of(Field.VM_ID));
+
+    private final Set<Field> enabledFields = EnumSet.noneOf(Field.class);
+
+    ResultsRenderer(Arguments arguments) {
+        if (arguments.hasArgument(ENABLE_ALL_FIELDS_FLAG)) {
+            enabledFields.addAll(Arrays.asList(Field.values()));
+        }
+        for (Field field : Field.values()) {
+            if (arguments.hasArgument(field.getCliSwitch())) {
+                enabledFields.add(field);
+            }
+        }
+        if (enabledFields.isEmpty()) {
+            enabledFields.addAll(DEFAULT_ENABLED_FIELDS);
+        }
+    }
+
+    void print(PrintStream printStream, Iterable<Pair<HostInfo, VmInfo>> pairs) {
+        SortedTableRenderer renderer = new SortedTableRenderer(enabledFields.size());
+        if (!isShortOutput()) {
+            renderer.printHeader(getHeaderFields().toArray(new String[enabledFields.size()]));
+        }
+        for (Pair<HostInfo, VmInfo> pair : pairs) {
+            List<String> info = getInfo(pair);
+            renderer.printLine(info.toArray(new String[enabledFields.size()]));
+        }
+        renderer.render(printStream);
+    }
+
+    boolean isShortOutput() {
+        return enabledFields.equals(DEFAULT_ENABLED_FIELDS) || enabledFields.size() == 1;
+    }
+
+    List<String> getHeaderFields() {
+        List<String> list = new ArrayList<>();
+        for (Field field : enabledFields) {
+            list.add(field.toString());
+        }
+        return list;
+    }
+
+    List<String> getInfo(Pair<HostInfo, VmInfo> pair) {
+        List<String> list = new ArrayList<>();
+        for (Field field : enabledFields) {
+            list.add(field.getAdaptedField(pair));
+        }
+        return list;
+    }
+
+    interface FieldAdapter {
+        String map(Pair<HostInfo, VmInfo> pair);
+    }
+
+    static class VmIdFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getVmId();
+        }
+    }
+
+    static class MainClassFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getMainClass();
+        }
+    }
+
+    static class VmNameFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getVmName();
+        }
+    }
+
+    static class JavaVersionFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getJavaVersion();
+        }
+    }
+
+    static class VmVersionFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getVmVersion();
+        }
+    }
+
+    static class PidFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return Integer.toString(vmInfo.getVmPid());
+        }
+    }
+
+    static class UsernameFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            VmInfo vmInfo = pair.getSecond();
+            return vmInfo.getUsername();
+        }
+    }
+
+    static class HostnameFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            HostInfo hostInfo = pair.getFirst();
+            return hostInfo.getHostname();
+        }
+    }
+
+    static class OsNameFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            HostInfo hostInfo = pair.getFirst();
+            return hostInfo.getOsName();
+        }
+    }
+
+    static class OsKernelFieldAdapter implements FieldAdapter {
+        @Override
+        public String map(Pair<HostInfo, VmInfo> pair) {
+            HostInfo hostInfo = pair.getFirst();
+            return hostInfo.getOsKernel();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/VmCriterion.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,154 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.VmInfo;
+
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.NoSuchElementException;
+import java.util.regex.PatternSyntaxException;
+
+enum VmCriterion implements CriterionMatcher<VmInfo, String> {
+    JAVA_VERSION("javaversion", new JavaVersionMatcher()),
+    MAINCLASS("mainclass", new MainclassMatcher()),
+    VM_NAME("vmname", new VmNameMatcher()),
+    VM_ARGS("vmargs", new VmArgsMatcher()),
+    VM_VERSION("vmversion", new VmVersionMatcher()),
+    USERNAME("username", new UsernameMatcher()),
+    JAVA_HOME("javahome", new JavaHomeMatcher()),
+    ;
+
+    private final String cliSwitch;
+    private final CriterionMatcher<VmInfo, String> criterionMatcher;
+
+    VmCriterion(String cliSwitch, CriterionMatcher<VmInfo, String> criterionMatcher) {
+        this.cliSwitch = cliSwitch;
+        this.criterionMatcher = criterionMatcher;
+    }
+
+    @Override
+    public boolean match(VmInfo vmInfo, String value) {
+        return this.criterionMatcher.match(vmInfo, value);
+    }
+
+    String getCliSwitch() {
+        return cliSwitch;
+    }
+
+    static VmCriterion fromString(String string) {
+        for (VmCriterion criterion : VmCriterion.values()) {
+            if (criterion.cliSwitch.equals(string)) {
+                return criterion;
+            }
+        }
+        throw new IllegalArgumentException(string + " is not a legal VmCriterion");
+    }
+
+    static class JavaVersionMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            return vmInfo.getJavaVersion().equals(s);
+        }
+    }
+
+    static class MainclassMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            try {
+                return vmInfo.getMainClass().equals(s) || vmInfo.getMainClass().contains(s) || vmInfo.getMainClass().matches(s);
+            } catch (PatternSyntaxException e) {
+                return false;
+            }
+        }
+    }
+
+    static class VmNameMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            return vmInfo.getVmName().equals(s);
+        }
+    }
+
+    static class VmArgsMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            if (!s.contains(",")) {
+                return vmInfo.getVmArguments().contains(s);
+            }
+            boolean allPresent = true;
+            String[] args = s.split(",");
+            for (String arg : args) {
+                allPresent = allPresent && vmInfo.getVmArguments().contains(arg);
+            }
+            return allPresent;
+        }
+    }
+
+    static class VmVersionMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            return vmInfo.getVmVersion().equals(s);
+        }
+    }
+
+    static class UsernameMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            return vmInfo.getUsername().equals(s);
+        }
+    }
+
+    static class JavaHomeMatcher implements CriterionMatcher<VmInfo, String> {
+        @Override
+        public boolean match(VmInfo vmInfo, String s) {
+            if (vmInfo.getJavaHome().equals(s)) {
+                return true;
+            }
+            try {
+                Path vmPath = Paths.get(vmInfo.getJavaHome());
+                Path givenPath = Paths.get(s);
+                return vmPath.equals(givenPath);
+            } catch (InvalidPathException e) {
+                return false;
+            }
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/internal/VmMatcher.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,52 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.VmInfo;
+
+import java.util.Map;
+
+class VmMatcher extends AbstractMatcher<VmInfo> {
+
+    VmMatcher(Map<String, String> criteriaMap) {
+        for (Map.Entry<String, String> entry : criteriaMap.entrySet()) {
+            VmCriterion vmCriterion = VmCriterion.fromString(entry.getKey());
+            this.criteriaMap.put(vmCriterion, entry.getValue());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/java/com/redhat/thermostat/vm/find/command/locale/LocaleResources.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,56 @@
+/*
+ * 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.find.command.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources {
+
+    COMMAND_INTERRUPTED,
+    AGENT_SERVICE_UNAVAILABLE,
+    HOST_SERVICE_UNAVAILABLE,
+    VM_SERVICE_UNAVAILABLE,
+    NO_CRITERIA_GIVEN,
+    ;
+
+    static final String RESOURCE_BUNDLE =
+            "com.redhat.thermostat.vm.find.command.locale.strings";
+
+    public static Translate<LocaleResources> createTranslator() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/main/resources/com/redhat/thermostat/vm/find/command/locale/strings.properties	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,5 @@
+COMMAND_INTERRUPTED = Command interrupted while waiting for services.
+AGENT_SERVICE_UNAVAILABLE = Unable to get agent information (AgentInfoDAO is unavailable)
+HOST_SERVICE_UNAVAILABLE = Unable to get host information (HostInfoDAO is unavailable)
+VM_SERVICE_UNAVAILABLE = Unable to get vm information (VmInfoDAO is unavailable)
+NO_CRITERIA_GIVEN = No filtering criteria were specified
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/AbstractMatcherTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.BasePojo;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class AbstractMatcherTest {
+
+    static class TestMatcher extends AbstractMatcher<BasePojo> {
+        TestMatcher() {
+            CriterionMatcher<BasePojo, String> matcher = new CriterionMatcher<BasePojo, String>() {
+                @Override
+                public boolean match(BasePojo pojo, String s) {
+                    return pojo.getAgentId().equals(s);
+                }
+            };
+            criteriaMap.put(matcher, "foo");
+        }
+    }
+
+    @Test
+    public void testMatch() {
+        AbstractMatcher<BasePojo> matcher = new TestMatcher();
+        BasePojo foo = new BasePojo("foo");
+        BasePojo bar = new BasePojo("bar");
+        assertThat(matcher.match(foo), is(true));
+        assertThat(matcher.match(bar), is(false));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/ActivatorTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,63 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.testutils.StubBundleContext;
+import org.junit.Test;
+
+import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class ActivatorTest {
+
+    @Test
+    public void verifyCommandRegistered() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertCommandIsRegistered(context, FindVmCommand.REGISTER_NAME, FindVmCommand.class);
+
+        activator.stop(context);
+
+        assertThat(context.getAllServices().size(), is(0));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/FindVmCommandTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,490 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmId;
+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.HostInfo;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FindVmCommandTest {
+
+    private Arguments args;
+
+    @Before
+    public void setup() {
+        args = mock(Arguments.class);
+    }
+
+    @Test
+    public void testCommandFailsWhenDaosUnavailable() {
+        FindVmCommand command = new FindVmCommand();
+        try {
+            command.run(new TestCommandContextFactory().createContext(args));
+            fail("should have received CommandException");
+        } catch (CommandException e) {
+            assertTrue(e.getMessage().contains("AgentInfoDAO is unavailable")
+                || e.getMessage().contains("HostInfoDAO is unavailable")
+                || e.getMessage().contains("VmInfoDAO is unavailable"));
+        }
+    }
+
+    @Test
+    public void testCommandFailsWhenAgentInfoDaoUnavailable() {
+        FindVmCommand command = new FindVmCommand();
+        command.setVmInfoDAO(mock(VmInfoDAO.class));
+        command.setHostInfoDAO(mock(HostInfoDAO.class));
+        try {
+            command.run(new TestCommandContextFactory().createContext(args));
+            fail("should have received CommandException");
+        } catch (CommandException e) {
+            assertThat(e.getMessage(), containsString("AgentInfoDAO is unavailable"));
+        }
+    }
+
+    @Test
+    public void testCommandFailsWhenHostInfoDaoUnavailable() {
+        FindVmCommand command = new FindVmCommand();
+        command.setAgentInfoDAO(mock(AgentInfoDAO.class));
+        command.setVmInfoDAO(mock(VmInfoDAO.class));
+        try {
+            command.run(new TestCommandContextFactory().createContext(args));
+            fail("should have received CommandException");
+        } catch (CommandException e) {
+            assertThat(e.getMessage(), containsString("HostInfoDAO is unavailable"));
+        }
+    }
+
+    @Test
+    public void testCommandFailsWhenVmInfoDaoUnavailable() {
+        FindVmCommand command = new FindVmCommand();
+        command.setAgentInfoDAO(mock(AgentInfoDAO.class));
+        command.setHostInfoDAO(mock(HostInfoDAO.class));
+        try {
+            command.run(new TestCommandContextFactory().createContext(args));
+            fail("should have received CommandException");
+        } catch (CommandException e) {
+            assertThat(e.getMessage(), containsString("VmInfoDAO is unavailable"));
+        }
+    }
+
+    @Test
+    public void testCommandFailsWhenNoCriteriaSupplied() {
+        FindVmCommand command = new FindVmCommand();
+        command.setAgentInfoDAO(mock(AgentInfoDAO.class));
+        command.setHostInfoDAO(mock(HostInfoDAO.class));
+        command.setVmInfoDAO(mock(VmInfoDAO.class));
+        try {
+            command.run(new TestCommandContextFactory().createContext(args));
+            fail("should have received CommandException");
+        } catch (CommandException e) {
+            assertThat(e.getMessage(), containsString("No filtering criteria were specified"));
+        }
+    }
+
+    @Test
+    public void testCommandSucceedsWithSingleHostCriterion() throws CommandException {
+        when(args.hasArgument("hostname")).thenReturn(true);
+        when(args.getArgument("hostname")).thenReturn("foo-host");
+
+        AgentInformation agent = new AgentInformation("agent");
+
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setVmId("vm-id");
+        vmInfo.setAgentId(agent.getAgentId());
+
+        HostInfo hostInfo = new HostInfo();
+        hostInfo.setHostname("foo-host");
+
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        when(vmInfoDAO.getVmInfo(any(VmRef.class))).thenReturn(vmInfo);
+        when(vmInfoDAO.getVMs(any(HostRef.class))).thenReturn(Collections.singleton(new VmRef(new HostRef(agent.getAgentId(), "dummy"), vmInfo)));
+
+        HostInfoDAO hostInfoDAO = mock(HostInfoDAO.class);
+        when(hostInfoDAO.getHostInfo(any(HostRef.class))).thenReturn(hostInfo);
+
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        when(agentInfoDAO.getAliveAgents()).thenReturn(Collections.singletonList(agent));
+        when(agentInfoDAO.getAgentInformation(any(HostRef.class))).thenReturn(agent);
+
+        TestCommandContextFactory testCommandContextFactory = new TestCommandContextFactory();
+        FindVmCommand command = new FindVmCommand();
+        command.setVmInfoDAO(vmInfoDAO);
+        command.setAgentInfoDAO(agentInfoDAO);
+        command.setHostInfoDAO(hostInfoDAO);
+        command.run(testCommandContextFactory.createContext(args));
+
+        String output = testCommandContextFactory.getOutput();
+        assertThat(output, containsString(vmInfo.getVmId()));
+    }
+
+    @Test
+    public void testCommandSucceedsWithSingleVmCriterion() throws CommandException {
+        when(args.hasArgument("username")).thenReturn(true);
+        when(args.getArgument("username")).thenReturn("foo-user");
+
+        AgentInformation agent = new AgentInformation("agent");
+
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setVmId("vm-id");
+        vmInfo.setAgentId(agent.getAgentId());
+        vmInfo.setUsername("foo-user");
+
+        HostInfo hostInfo = new HostInfo();
+
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        when(vmInfoDAO.getVmInfo(any(VmRef.class))).thenReturn(vmInfo);
+        when(vmInfoDAO.getVMs(any(HostRef.class))).thenReturn(Collections.singleton(new VmRef(new HostRef(agent.getAgentId(), "dummy"), vmInfo)));
+
+        HostInfoDAO hostInfoDAO = mock(HostInfoDAO.class);
+        when(hostInfoDAO.getHostInfo(any(HostRef.class))).thenReturn(hostInfo);
+
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        when(agentInfoDAO.getAliveAgents()).thenReturn(Collections.singletonList(agent));
+        when(agentInfoDAO.getAgentInformation(any(HostRef.class))).thenReturn(agent);
+
+        TestCommandContextFactory testCommandContextFactory = new TestCommandContextFactory();
+        FindVmCommand command = new FindVmCommand();
+        command.setVmInfoDAO(vmInfoDAO);
+        command.setAgentInfoDAO(agentInfoDAO);
+        command.setHostInfoDAO(hostInfoDAO);
+        command.run(testCommandContextFactory.createContext(args));
+
+        String output = testCommandContextFactory.getOutput();
+        assertThat(output, containsString(vmInfo.getVmId()));
+    }
+
+    @Test
+    public void testGetAgentsToSearchWithoutAgentIdArg() {
+        List<AgentInformation> list = Arrays.asList(mock(AgentInformation.class), mock(AgentInformation.class));
+        FindVmCommand command = new FindVmCommand();
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        when(agentInfoDAO.getAliveAgents()).thenReturn(list);
+        command.setAgentInfoDAO(agentInfoDAO);
+        List<AgentInformation> agentsToSearch = command.getAgentsToSearch(args);
+        assertThat(agentsToSearch, is(equalTo(list)));
+    }
+
+    @Test
+    public void testGetAgentsToSearchWithAgentIdArg() {
+        AgentInformation foo = new AgentInformation("foo");
+        AgentInformation bar = new AgentInformation("bar");
+        List<AgentInformation> list = Arrays.asList(foo, bar);
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        when(agentInfoDAO.getAliveAgents()).thenReturn(list);
+        when(agentInfoDAO.getAgentInformation(any(HostRef.class))).thenReturn(foo);
+        FindVmCommand command = new FindVmCommand();
+        command.setAgentInfoDAO(agentInfoDAO);
+        when(args.hasArgument("agentId")).thenReturn(true);
+        when(args.getArgument("agentId")).thenReturn(foo.getAgentId());
+        List<AgentInformation> agentsToSearch = command.getAgentsToSearch(args);
+        assertThat(agentsToSearch, is(equalTo(Collections.singletonList(foo))));
+    }
+
+    @Test
+    public void testGetHostCriteria() {
+        Map<String, String> empty = FindVmCommand.getHostCriteria(args);
+        assertThat(empty, is(equalTo(Collections.<String, String>emptyMap())));
+
+        when(args.hasArgument(HostCriterion.HOSTNAME.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(HostCriterion.HOSTNAME.getCliSwitch())).thenReturn("foo");
+        when(args.hasArgument(HostCriterion.OS_KERNEL.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(HostCriterion.OS_KERNEL.getCliSwitch())).thenReturn("lin");
+        when(args.hasArgument(VmCriterion.JAVA_HOME.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(VmCriterion.JAVA_HOME.getCliSwitch())).thenReturn("bar");
+        Map<String, String> map1 = FindVmCommand.getHostCriteria(args);
+        Map<String, String> expected1 = new HashMap<>();
+        expected1.put(HostCriterion.HOSTNAME.getCliSwitch(), "foo");
+        expected1.put(HostCriterion.OS_KERNEL.getCliSwitch(), "lin");
+        assertThat(map1, is(equalTo(expected1)));
+    }
+
+    @Test
+    public void testGetVmCriteria() {
+        Map<String, String> empty = FindVmCommand.getVmCriteria(args);
+        assertThat(empty, is(equalTo(Collections.<String, String>emptyMap())));
+
+        when(args.hasArgument(HostCriterion.HOSTNAME.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(HostCriterion.HOSTNAME.getCliSwitch())).thenReturn("foo");
+        when(args.hasArgument(VmCriterion.JAVA_HOME.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(VmCriterion.JAVA_HOME.getCliSwitch())).thenReturn("bar");
+        when(args.hasArgument(VmCriterion.USERNAME.getCliSwitch())).thenReturn(true);
+        when(args.getArgument(VmCriterion.USERNAME.getCliSwitch())).thenReturn("baz");
+        Map<String, String> map1 = FindVmCommand.getVmCriteria(args);
+        Map<String, String> expected1 = new HashMap<>();
+        expected1.put(VmCriterion.JAVA_HOME.getCliSwitch(), "bar");
+        expected1.put(VmCriterion.USERNAME.getCliSwitch(), "baz");
+        assertThat(map1, is(equalTo(expected1)));
+    }
+
+    @Test
+    public void testPerformSearch() {
+        AgentInformation agentInfo1 = new AgentInformation("agentInfo1");
+        HostRef hostRef1 = new HostRef(agentInfo1.getAgentId(), "dummy");
+        AgentInformation agentInfo2 = new AgentInformation("agentInfo2");
+        HostRef hostRef2 = new HostRef(agentInfo2.getAgentId(), "dummy");
+        AgentInformation agentInfo3 = new AgentInformation("agentInfo3");
+        HostRef hostRef3 = new HostRef(agentInfo3.getAgentId(), "dummy");
+        List<AgentInformation> agents = Arrays.asList(agentInfo1, agentInfo2, agentInfo3);
+
+        HostInfo host1 = mock(HostInfo.class);
+        when(host1.getHostname()).thenReturn("foo-host");
+        HostInfo host2 = host1;
+        HostInfo host3 = mock(HostInfo.class);
+        when(host3.getHostname()).thenReturn("bar-host");
+
+        VmInfo vm1 = mock(VmInfo.class);
+        when(vm1.getVmId()).thenReturn("vm1");
+        when(vm1.getJavaVersion()).thenReturn("1.8");
+        when(vm1.getMainClass()).thenReturn("foo-class");
+        VmInfo vm2 = mock(VmInfo.class);
+        when(vm2.getVmId()).thenReturn("vm2");
+        when(vm2.getJavaVersion()).thenReturn("1.8");
+        when(vm2.getMainClass()).thenReturn("bar-class");
+        VmInfo vm3 = mock(VmInfo.class);
+        when(vm3.getVmId()).thenReturn("vm3");
+        when(vm3.getJavaVersion()).thenReturn("1.7");
+        when(vm3.getMainClass()).thenReturn("baz-class");
+        VmInfo vm4 = mock(VmInfo.class);
+        when(vm4.getVmId()).thenReturn("vm3");
+        when(vm4.getJavaVersion()).thenReturn("1.6");
+        when(vm4.getMainClass()).thenReturn("boz-class");
+
+        VmRef vmRef1 = new VmRef(hostRef1, vm1);
+        VmRef vmRef2 = new VmRef(hostRef1, vm2);
+        VmRef vmRef3 = new VmRef(hostRef2, vm3);
+        VmRef vmRef4 = new VmRef(hostRef3, vm4);
+        Set<VmRef> vmRefs1 = new HashSet<>(Arrays.asList(vmRef1, vmRef2));
+        Set<VmRef> vmRefs2 = Collections.singleton(vmRef3);
+        Set<VmRef> vmRefs3 = Collections.singleton(vmRef4);
+
+        Pair<HostInfo, VmInfo> pair1 = new Pair<>(host1, vm1);
+        Pair<HostInfo, VmInfo> pair2 = new Pair<>(host1, vm2);
+        Pair<HostInfo, VmInfo> pair3 = new Pair<>(host2, vm3);
+        Pair<HostInfo, VmInfo> pair4 = new Pair<>(host3, vm4);
+
+        FindVmCommand command = new FindVmCommand();
+
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        when(agentInfoDAO.getAliveAgents()).thenReturn(agents);
+        when(agentInfoDAO.getAgentInformation(hostRef1)).thenReturn(agentInfo1);
+        when(agentInfoDAO.getAgentInformation(hostRef2)).thenReturn(agentInfo2);
+        when(agentInfoDAO.getAgentInformation(hostRef3)).thenReturn(agentInfo3);
+        command.setAgentInfoDAO(agentInfoDAO);
+
+        HostInfoDAO hostInfoDAO = mock(HostInfoDAO.class);
+        when(hostInfoDAO.getHostInfo(hostRef1)).thenReturn(host1);
+        when(hostInfoDAO.getHostInfo(hostRef2)).thenReturn(host2);
+        when(hostInfoDAO.getHostInfo(hostRef3)).thenReturn(host3);
+        command.setHostInfoDAO(hostInfoDAO);
+
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        when(vmInfoDAO.getVMs(hostRef1)).thenReturn(vmRefs1);
+        when(vmInfoDAO.getVMs(hostRef2)).thenReturn(vmRefs2);
+        when(vmInfoDAO.getVMs(hostRef3)).thenReturn(vmRefs3);
+        when(vmInfoDAO.getVmInfo(vmRef1)).thenReturn(vm1);
+        when(vmInfoDAO.getVmInfo(vmRef2)).thenReturn(vm2);
+        when(vmInfoDAO.getVmInfo(vmRef3)).thenReturn(vm3);
+        when(vmInfoDAO.getVmInfo(vmRef4)).thenReturn(vm4);
+        command.setVmInfoDAO(vmInfoDAO);
+
+        Map<String, String> hostMap1 = new HashMap<>();
+        HostMatcher hostMatcher1 = new HostMatcher(hostMap1);
+        Map<String, String> vmMap1 = new HashMap<>();
+        VmMatcher vmMatcher1 = new VmMatcher(vmMap1);
+        List<Pair<HostInfo, VmInfo>> result1 = command.performSearch(agents, hostMatcher1, vmMatcher1);
+        assertThat(result1.size(), is(4));
+        for (Pair<HostInfo, VmInfo> pair : Arrays.asList(pair1, pair2, pair3, pair4)) {
+            assertThat(result1.contains(pair), is(true));
+        }
+
+        Map<String, String> hostMap2 = new HashMap<>();
+        hostMap2.put("hostname", "nosuchhost");
+        HostMatcher hostMatcher2 = new HostMatcher(hostMap2);
+        Map<String, String> vmMap2 = new HashMap<>();
+        vmMap2.put("javaversion", "1.8");
+        VmMatcher vmMatcher2 = new VmMatcher(vmMap2);
+        List<Pair<HostInfo, VmInfo>> result2 = command.performSearch(agents, hostMatcher2, vmMatcher2);
+        assertThat(result2.size(), is(0));
+
+        Map<String, String> hostMap3 = new HashMap<>();
+        hostMap3.put("hostname", "foo-host");
+        HostMatcher hostMatcher3 = new HostMatcher(hostMap3);
+        Map<String, String> vmMap3 = new HashMap<>();
+        VmMatcher vmMatcher3 = new VmMatcher(vmMap3);
+        List<Pair<HostInfo, VmInfo>> result3 = command.performSearch(agents, hostMatcher3, vmMatcher3);
+        assertThat(result3.size(), is(3));
+        for (Pair<HostInfo, VmInfo> pair : Arrays.asList(pair1, pair2, pair3)) {
+            assertThat(result3.contains(pair), is(true));
+        }
+
+        Map<String, String> hostMap4 = new HashMap<>();
+        hostMap3.put("hostname", "foo-host");
+        HostMatcher hostMatcher4 = new HostMatcher(hostMap4);
+        Map<String, String> vmMap4 = new HashMap<>();
+        vmMap4.put("javaversion", "1.8");
+        VmMatcher vmMatcher4 = new VmMatcher(vmMap4);
+        List<Pair<HostInfo, VmInfo>> result4 = command.performSearch(agents, hostMatcher4, vmMatcher4);
+        assertThat(result4.size(), is(2));
+        for (Pair<HostInfo, VmInfo> pair : Arrays.asList(pair1, pair2)) {
+            assertThat(result4.contains(pair), is(true));
+        }
+    }
+
+    @Test
+    public void testFilterAgents() {
+        AgentInformation foo = new AgentInformation("foo");
+        HostRef fooRef = new HostRef(foo.getAgentId(), "dummy");
+        AgentInformation bar = new AgentInformation("bar");
+        HostRef barRef = new HostRef(bar.getAgentId(), "dummy");
+        HostInfo hostInfo1 = mock(HostInfo.class);
+        when(hostInfo1.getHostname()).thenReturn("foo-host");
+        HostInfo hostInfo2 = mock(HostInfo.class);
+        when(hostInfo2.getHostname()).thenReturn("bar-host");
+        HostInfoDAO hostInfoDAO = mock(HostInfoDAO.class);
+        when(hostInfoDAO.getHostInfo(fooRef)).thenReturn(hostInfo1);
+        when(hostInfoDAO.getHostInfo(barRef)).thenReturn(hostInfo2);
+        List<AgentInformation> agents = Arrays.asList(foo, bar);
+
+        FindVmCommand command = new FindVmCommand();
+        command.setHostInfoDAO(hostInfoDAO);
+
+        HostMatcher allMatcher = new HostMatcher(Collections.<String, String>emptyMap());
+        List<AgentInformation> all = command.filterAgents(agents, allMatcher);
+        assertThat(all, is(equalTo(agents)));
+
+        Map<String, String> noneMap = new HashMap<>();
+        noneMap.put("hostname", "none-host");
+        HostMatcher noneMatcher = new HostMatcher(noneMap);
+        List<AgentInformation> none = command.filterAgents(agents, noneMatcher);
+        assertThat(none, is(equalTo(Collections.<AgentInformation>emptyList())));
+
+        Map<String, String> fooMap = new HashMap<>();
+        fooMap.put("hostname", "foo-host");
+        HostMatcher fooMatcher = new HostMatcher(fooMap);
+        List<AgentInformation> fooList = command.filterAgents(agents, fooMatcher);
+        assertThat(fooList, is(equalTo(Collections.singletonList(foo))));
+    }
+
+    @Test
+    public void testGetMatchingVms() {
+        AgentInformation agent = new AgentInformation("agent");
+        HostRef hostRef = new HostRef(agent.getAgentId(), "dummy");
+
+        VmInfo vmInfo1 = new VmInfo();
+        vmInfo1.setAgentId(agent.getAgentId());
+        vmInfo1.setVmId("vm1");
+        vmInfo1.setUsername("foo-user");
+        vmInfo1.setJavaVersion("1.8");
+        VmRef vmRef1 = new VmRef(hostRef, vmInfo1);
+
+        VmInfo vmInfo2 = new VmInfo();
+        vmInfo2.setAgentId(agent.getAgentId());
+        vmInfo2.setVmId("vm2");
+        vmInfo2.setUsername("bar-user");
+        vmInfo2.setJavaVersion("1.8");
+        VmRef vmRef2 = new VmRef(hostRef, vmInfo2);
+
+        VmInfo vmInfo3 = new VmInfo();
+        vmInfo3.setAgentId(agent.getAgentId());
+        vmInfo3.setVmId("vm3");
+        vmInfo3.setUsername("baz-user");
+        vmInfo3.setJavaVersion("1.7");
+        VmRef vmRef3 = new VmRef(hostRef, vmInfo3);
+
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        when(vmInfoDAO.getVMs(hostRef)).thenReturn(new HashSet<>(Arrays.asList(vmRef1, vmRef2, vmRef3)));
+        when(vmInfoDAO.getVmInfo(vmRef1)).thenReturn(vmInfo1);
+        when(vmInfoDAO.getVmInfo(vmRef2)).thenReturn(vmInfo2);
+        when(vmInfoDAO.getVmInfo(vmRef3)).thenReturn(vmInfo3);
+
+        FindVmCommand command = new FindVmCommand();
+        command.setVmInfoDAO(vmInfoDAO);
+
+        VmMatcher allMatcher = new VmMatcher(Collections.<String, String>emptyMap());
+        List<VmInfo> all = command.getMatchingVms(agent, allMatcher);
+        assertThat(all, is(equalTo(Arrays.asList(vmInfo1, vmInfo2, vmInfo3))));
+
+        Map<String, String> userMap = new HashMap<>();
+        userMap.put("username", "foo-user");
+        VmMatcher userMatcher = new VmMatcher(userMap);
+        List<VmInfo> users = command.getMatchingVms(agent, userMatcher);
+        assertThat(users, is(equalTo(Collections.singletonList(vmInfo1))));
+
+        Map<String, String> versionMap = new HashMap<>();
+        versionMap.put("javaversion", "1.8");
+        VmMatcher versionMatcher = new VmMatcher(versionMap);
+        List<VmInfo> versions = command.getMatchingVms(agent, versionMatcher);
+        assertThat(versions, is(equalTo(Arrays.asList(vmInfo1, vmInfo2))));
+
+        Map<String, String> noneMap = new HashMap<>();
+        noneMap.put("javaversion", "1.0");
+        VmMatcher noneMatcher = new VmMatcher(noneMap);
+        List<VmInfo> none = command.getMatchingVms(agent, noneMatcher);
+        assertThat(none, is(equalTo(Collections.<VmInfo>emptyList())));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/HostCriterionTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,93 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.HostInfo;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class HostCriterionTest {
+
+    @Test
+    public void testHostname() {
+        HostCriterion hostCriterion = HostCriterion.HOSTNAME;
+        HostInfo hostInfo = new HostInfo();
+        hostInfo.setHostname("foo");
+        assertThat(hostCriterion.match(hostInfo, "foo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "fo"), is(false));
+        assertThat(hostCriterion.match(hostInfo, "oo"), is(false));
+        assertThat(hostCriterion.match(hostInfo, "bar"), is(false));
+        assertThat(hostCriterion.match(hostInfo, "fooo"), is(false));
+    }
+
+    @Test
+    public void testOsKernel() {
+        HostCriterion hostCriterion = HostCriterion.OS_KERNEL;
+        HostInfo hostInfo = new HostInfo();
+        hostInfo.setOsKernel("foo");
+        assertThat(hostCriterion.match(hostInfo, "foo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "fo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "oo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "bar"), is(false));
+        assertThat(hostCriterion.match(hostInfo, "fooo"), is(false));
+    }
+
+    @Test
+    public void testOsName() {
+        HostCriterion hostCriterion = HostCriterion.OS_NAME;
+        HostInfo hostInfo = new HostInfo();
+        hostInfo.setOsName("foo");
+        assertThat(hostCriterion.match(hostInfo, "foo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "fo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "oo"), is(true));
+        assertThat(hostCriterion.match(hostInfo, "bar"), is(false));
+        assertThat(hostCriterion.match(hostInfo, "fooo"), is(false));
+    }
+
+    @Test
+    public void testFromStringWithValidArgument() {
+        HostCriterion criterion = HostCriterion.fromString("osname");
+        assertThat(criterion, is(HostCriterion.OS_NAME));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringWithInvalidArgument() {
+        HostCriterion.fromString("not a real criterion");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/HostMatcherTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.HostInfo;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class HostMatcherTest {
+
+    @Test
+    public void testTestMatch() {
+        Map<String, String> map = new HashMap<>();
+        map.put("hostname", "foo");
+        map.put("osname", "bar");
+        HostMatcher matcher = new HostMatcher(map);
+
+        HostInfo host1 = new HostInfo();
+        host1.setHostname("foo");
+        host1.setOsName("bar");
+
+        HostInfo host2 = new HostInfo();
+        host2.setHostname("foo");
+        host2.setOsName("baz");
+
+        assertThat(matcher.match(host1), is(true));
+        assertThat(matcher.match(host2), is(false));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/ResultsRendererTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,226 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.storage.model.HostInfo;
+import com.redhat.thermostat.storage.model.VmInfo;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ResultsRendererTest {
+
+    private static final HostInfo HOST_INFO = new HostInfo();
+    private static final VmInfo VM_INFO = new VmInfo();
+    private static final Pair<HostInfo, VmInfo> PAIR = new Pair<>(HOST_INFO, VM_INFO);
+
+    static {
+        HOST_INFO.setOsName("foo-os");
+        HOST_INFO.setHostname("foo-host");
+        HOST_INFO.setOsKernel("foo-kern");
+
+        VM_INFO.setJavaHome("/some/path/to/jdk");
+        VM_INFO.setUsername("foo-user");
+        VM_INFO.setVmVersion("20.5.6");
+        VM_INFO.setVmArguments("-Xmx2014M -Xms1024M");
+        VM_INFO.setJavaVersion("1.8");
+        VM_INFO.setMainClass("com.example.java.ExampleApplet");
+        VM_INFO.setVmId("foo-id");
+        VM_INFO.setVmName("Example JVM Implementation");
+        VM_INFO.setVmPid(2);
+    }
+
+    @Test
+    public void testVmId() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.VM_ID;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("foo-id"));
+    }
+
+    @Test
+    public void testMainclass() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.MAINCLASS;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("com.example.java.ExampleApplet"));
+    }
+
+    @Test
+    public void testVmName() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.VMNAME;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("Example JVM Implementation"));
+    }
+
+    @Test
+    public void testJavaVersion() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.JAVAVERSION;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("1.8"));
+    }
+
+    @Test
+    public void testVmVersion() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.VMVERSION;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("20.5.6"));
+    }
+
+    @Test
+    public void testPid() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.PID;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("2"));
+    }
+
+    @Test
+    public void testUsername() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.USERNAME;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("foo-user"));
+    }
+
+    @Test
+    public void testHostname() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.HOSTNAME;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("foo-host"));
+    }
+
+    @Test
+    public void testOsName() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.OSNAME;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("foo-os"));
+    }
+
+    @Test
+    public void testOsKernel() {
+        ResultsRenderer.Field field = ResultsRenderer.Field.OSKERNEL;
+        String result = field.getAdaptedField(PAIR);
+        assertThat(result, containsString("foo-kern"));
+    }
+
+    @Test
+    public void testIsShortOutputDefault() {
+        ResultsRenderer renderer = new ResultsRenderer(mock(Arguments.class));
+        assertThat(renderer.isShortOutput(), is(true));
+    }
+
+    @Test
+    public void testIsShortOutputNotOverriddenByVmIdAlone() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.VM_ID.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        assertThat(renderer.isShortOutput(), is(true));
+    }
+
+    @Test
+    public void testIsShortOutputOverriddenByVmIdWithOthers() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.VM_ID.getCliSwitch())).thenReturn(true);
+        when(arguments.hasArgument(ResultsRenderer.Field.HOSTNAME.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        assertThat(renderer.isShortOutput(), is(false));
+    }
+
+    @Test
+    public void testIsShortOutputOverriddenByOthersWithoutVmId() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.PID.getCliSwitch())).thenReturn(true);
+        when(arguments.hasArgument(ResultsRenderer.Field.HOSTNAME.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        assertThat(renderer.isShortOutput(), is(false));
+    }
+
+    @Test
+    public void testIsShortOutputNotOverridenBySingleOtherSwitch() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.PID.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        assertThat(renderer.isShortOutput(), is(true));
+    }
+
+    @Test
+    public void testGetHeaderFields() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.HOSTNAME.getCliSwitch())).thenReturn(true);
+        when(arguments.hasArgument(ResultsRenderer.Field.PID.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        List<String> headers = renderer.getHeaderFields();
+        assertThat(headers, is(equalTo(Arrays.asList(ResultsRenderer.Field.PID.name(), ResultsRenderer.Field.HOSTNAME.name()))));
+    }
+
+    @Test
+    public void testGetInfo() {
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.HOSTNAME.getCliSwitch())).thenReturn(true);
+        when(arguments.hasArgument(ResultsRenderer.Field.PID.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        List<String> info = renderer.getInfo(PAIR);
+        assertThat(info, is(equalTo(Arrays.asList("2", "foo-host"))));
+    }
+
+    @Test
+    public void testPrint() throws UnsupportedEncodingException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        Arguments arguments = mock(Arguments.class);
+        when(arguments.hasArgument(ResultsRenderer.Field.HOSTNAME.getCliSwitch())).thenReturn(true);
+        when(arguments.hasArgument(ResultsRenderer.Field.PID.getCliSwitch())).thenReturn(true);
+        ResultsRenderer renderer = new ResultsRenderer(arguments);
+        renderer.print(ps, Collections.singleton(PAIR));
+        String output = baos.toString();
+        assertThat(output, containsString("HOSTNAME"));
+        assertThat(output, containsString("PID"));
+        assertThat(output, containsString("foo-host"));
+        assertThat(output, containsString("2"));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/VmCriterionTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,158 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.VmInfo;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class VmCriterionTest {
+
+    @Test
+    public void testJavaVersion() {
+        VmCriterion criterion = VmCriterion.JAVA_VERSION;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setJavaVersion("1.8");
+        assertThat(criterion.match(vmInfo, "1.8"), is(true));
+        assertThat(criterion.match(vmInfo, "1.80"), is(false));
+        assertThat(criterion.match(vmInfo, "1.8_u45"), is(false));
+        assertThat(criterion.match(vmInfo, "1.7"), is(false));
+        assertThat(criterion.match(vmInfo, "1."), is(false));
+        assertThat(criterion.match(vmInfo, "1"), is(false));
+        assertThat(criterion.match(vmInfo, "8"), is(false));
+    }
+
+    @Test
+    public void testMainClass() {
+        VmCriterion criterion = VmCriterion.MAINCLASS;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setMainClass("com.example.java.ExampleApplet");
+        assertThat(criterion.match(vmInfo, "com.example.java.ExampleApplet"), is(true));
+        assertThat(criterion.match(vmInfo, "com.example.java.ExampleApplet.*"), is(true));
+        assertThat(criterion.match(vmInfo, "com.example.java.ExampleApplet.+"), is(false));
+        assertThat(criterion.match(vmInfo, "com.example.java.Example.+"), is(true));
+        assertThat(criterion.match(vmInfo, "ExampleApplet"), is(true));
+        assertThat(criterion.match(vmInfo, ".*Applet"), is(true));
+        assertThat(criterion.match(vmInfo, ".*Applet.+"), is(false));
+        assertThat(criterion.match(vmInfo, ".*"), is(true));
+        assertThat(criterion.match(vmInfo, "*"), is(false));
+        assertThat(criterion.match(vmInfo, "com.example.java"), is(true));
+        assertThat(criterion.match(vmInfo, "com.example.java..*"), is(true));
+        assertThat(criterion.match(vmInfo, "com.example.java.ExampleApplet2"), is(false));
+    }
+
+    @Test
+    public void testVmName() {
+        VmCriterion criterion = VmCriterion.VM_NAME;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setVmName("Example JVM Implementation Name");
+        assertThat(criterion.match(vmInfo, "Example JVM Implementation Name"), is(true));
+        assertThat(criterion.match(vmInfo, "Example JVM Implementation Name 2"), is(false));
+        assertThat(criterion.match(vmInfo, "JVM Implementation"), is(false));
+        assertThat(criterion.match(vmInfo, "Example JVM"), is(false));
+        assertThat(criterion.match(vmInfo, "*"), is(false));
+    }
+
+    @Test
+    public void testVmArgs() {
+        VmCriterion criterion = VmCriterion.VM_ARGS;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setVmArguments("-Xmx1024M -Xms1024M");
+        assertThat(criterion.match(vmInfo, "-Xmx1024M -Xms1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "-Xmx1024M,-Xms1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "-Xms1024M,-Xmx1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "-Xmx1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "-Xms1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "Xmx1024M"), is(true));
+        assertThat(criterion.match(vmInfo, "Xmx"), is(true));
+        assertThat(criterion.match(vmInfo, "Xms,Xmx"), is(true));
+        assertThat(criterion.match(vmInfo, "-Xmx2048M"), is(false));
+    }
+
+    @Test
+    public void testVmVersion() {
+        VmCriterion criterion = VmCriterion.VM_VERSION;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setVmVersion("20.5.6");
+        assertThat(criterion.match(vmInfo, "20.5.6"), is(true));
+        assertThat(criterion.match(vmInfo, "20.5,6"), is(false));
+        assertThat(criterion.match(vmInfo, "20.5.8"), is(false));
+        assertThat(criterion.match(vmInfo, "21.5.6"), is(false));
+        assertThat(criterion.match(vmInfo, "20"), is(false));
+        assertThat(criterion.match(vmInfo, "20+"), is(false));
+        assertThat(criterion.match(vmInfo, "<=21"), is(false));
+    }
+
+    @Test
+    public void testUsername() {
+        VmCriterion criterion = VmCriterion.USERNAME;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setUsername("foo-user");
+        assertThat(criterion.match(vmInfo, "foo-user"), is(true));
+        assertThat(criterion.match(vmInfo, "foo"), is(false));
+        assertThat(criterion.match(vmInfo, "user"), is(false));
+        assertThat(criterion.match(vmInfo, "*"), is(false));
+        assertThat(criterion.match(vmInfo, ".*"), is(false));
+    }
+
+    @Test
+    public void testJavahome() {
+        VmCriterion criterion = VmCriterion.JAVA_HOME;
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setJavaHome("/some/filesystem/path/to/jdk");
+        assertThat(criterion.match(vmInfo, "/some/filesystem/path/to/jdk"), is(true));
+        assertThat(criterion.match(vmInfo, "/some/filesystem/path"), is(false));
+        assertThat(criterion.match(vmInfo, "/some/filesystem/path/to/"), is(false));
+        assertThat(criterion.match(vmInfo, "/some/filesystem/path/to/jdk/"), is(true));
+        assertThat(criterion.match(vmInfo, "some/filesystem/path/to/jdk/"), is(false));
+        assertThat(criterion.match(vmInfo, "/other/some/filesystem/path/to/jdk/"), is(false));
+        assertThat(criterion.match(vmInfo, "/some/filesystem/path"), is(false));
+    }
+
+    @Test
+    public void testFromStringWithValidArgument() {
+        VmCriterion criterion = VmCriterion.fromString("javahome");
+        assertThat(criterion, is(VmCriterion.JAVA_HOME));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringWithInvalidArgument() {
+        VmCriterion.fromString("not a valid criterion");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/internal/VmMatcherTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,65 @@
+/*
+ * 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.find.command.internal;
+
+import com.redhat.thermostat.storage.model.VmInfo;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class VmMatcherTest {
+
+    @Test
+    public void testTestMatch() {
+        Map<String, String> map = new HashMap<>();
+        map.put("username", "foo-user");
+        VmMatcher matcher = new VmMatcher(map);
+
+        VmInfo vmInfo1 = new VmInfo();
+        vmInfo1.setUsername("foo-user");
+
+        VmInfo vmInfo2 = new VmInfo();
+        vmInfo2.setUsername("bar-user");
+
+        assertThat(matcher.match(vmInfo1), is(true));
+        assertThat(matcher.match(vmInfo2), is(false));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/command/src/test/java/com/redhat/thermostat/vm/find/command/locale/LocaleResourcesTest.java	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,52 @@
+/*
+ * 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.find.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/vm-find/distribution/pom.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,97 @@
+<?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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-vm-find</artifactId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-find-distribution</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat vm-find plug-in - Distribution</name>
+  
+  <properties>
+    <thermostat.plugin>vm-find</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-vm-find-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/distribution/thermostat-plugin.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,213 @@
+<?xml version="1.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.
+
+-->
+<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>find-vm</name>
+      <summary>find a VM or set of VMs given a list of criteria</summary>
+      <description>
+        Search for VMs matching the specified criteria and print information about them.
+        By default, only the VMs' VmIds are printed, as these are globally unique identifiers, and so are useful
+        for passing to other commands ex. kill-vm. In this case, no table header is printed, and the list of VmIds
+        is printed with a newline separating each ID. If a non-default set of print options is specified then a
+        table header will be printed labelling each column of the output.
+
+        At least one filtering criteria option must be specified. These are any of the options switches listed
+        which do not begin with "--show" and which take an argument.
+      </description>
+      <options>
+        <option>
+          <long>agentId</long>
+          <short>a</short>
+          <argument>id</argument>
+          <required>false</required>
+          <description>specify the agent to query. If none specified, then all living agents are queried</description>
+        </option>
+
+        <!-- Display Options -->
+        <option>
+          <long>show-vm-ids</long>
+          <description>enable display of VM ID (globally unique)</description>
+        </option>
+        <option>
+          <long>show-mainclasses</long>
+          <description>enable display of mainclass</description>
+        </option>
+        <option>
+          <long>show-vmnames</long>
+          <description>enable display of VM names</description>
+        </option>
+        <option>
+          <long>show-javaversions</long>
+          <description>enable display of Java versions</description>
+        </option>
+        <option>
+          <long>show-vmversions</long>
+          <description>enable display of VM versions</description>
+        </option>
+        <option>
+          <long>show-pids</long>
+          <description>enable display of PID (local to each host)</description>
+        </option>
+        <option>
+          <long>show-usernames</long>
+          <description>enable display of username which VM runs as</description>
+        </option>
+        <option>
+          <long>show-hostnames</long>
+          <description>enable display of hostname</description>
+        </option>
+        <option>
+          <long>show-osnames</long>
+          <description>enable display of host OS name</description>
+        </option>
+        <option>
+          <long>show-oskernels</long>
+          <description>enable display of kernel name</description>
+        </option>
+        <option>
+          <long>show-all</long>
+          <description>Enable display of all fields in output table</description>
+        </option>
+
+        <!-- VM Criteria -->
+        <option>
+          <long>javaversion</long>
+          <short>v</short>
+          <argument>version</argument>
+          <required>false</required>
+          <description>the Java version of the VM. Must be exact match</description>
+        </option>
+        <option>
+          <long>mainclass</long>
+          <short>m</short>
+          <argument>nameOrRegex</argument>
+          <required>false</required>
+          <description>the main class the VM is running. Can be fully qualified name, substring, or regular expression</description>
+        </option>
+        <option>
+          <long>vmname</long>
+          <short>n</short>
+          <argument>name</argument>
+          <required>false</required>
+          <description>the name of the VM. Must be exact match</description>
+        </option>
+        <option>
+          <long>vmargs</long>
+          <short>g</short>
+          <argument>vmArg</argument>
+          <required>false</required>
+          <description>the arguments passed to the VM when it was started. Can be exact match, substring, or comma-separated list of substrings</description>
+        </option>
+        <option>
+          <long>vmversion</long>
+          <short>V</short>
+          <argument>version</argument>
+          <required>false</required>
+          <description>the version of the VM. Must be exact match</description>
+        </option>
+        <option>
+          <long>username</long>
+          <short>u</short>
+          <argument>username</argument>
+          <required>false</required>
+          <description>the username the VM runs as. Must be exact match</description>
+        </option>
+        <option>
+          <long>javahome</long>
+          <short>H</short>
+          <argument>homeDirectoryPatch</argument>
+          <required>false</required>
+          <description>the home directory path of the VM. Must be exact match when treated as paths (ie trailing directory separators will match)</description>
+        </option>
+
+        <!-- Host Criteria -->
+        <option>
+          <long>hostname</long>
+          <short>h</short>
+          <argument>hostname</argument>
+          <required>false</required>
+          <description>the name of the host on which the VM is running. Must be exact match</description>
+        </option>
+        <option>
+          <long>oskernel</long>
+          <short>k</short>
+          <argument>oskernel</argument>
+          <required>false</required>
+          <description>the name of the kernel being run by the host on which the VM is running. Must be exact match or substring</description>
+        </option>
+        <option>
+          <long>osname</long>
+          <short>o</short>
+          <argument>osname</argument>
+          <required>false</required>
+          <description>the name of the OS being run by the host on which the VM is running. Must be exact match or substring</description>
+        </option>
+      </options>
+      <environments>
+        <environment>cli</environment>
+        <environment>shell</environment>
+      </environments>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.find.command</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.storage.core</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>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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-find/pom.xml	Fri Jul 24 13:06:09 2015 -0400
@@ -0,0 +1,56 @@
+<?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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-find</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat Find VM plug-in</name>
+
+  <modules>
+    <module>command</module>
+    <module>distribution</module>
+  </modules>
+</project>
\ No newline at end of file