changeset 232:581d71ba9125

Implement list-vms command. Reviewed-by: vanaltj, omajid, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-April/000767.html
author Roman Kennke <rkennke@redhat.com>
date Mon, 16 Apr 2012 14:23:46 +0200
parents c87e8cb1f864
children 023cdedfdb8c
files common/src/main/java/com/redhat/thermostat/cli/AppContextSetup.java common/src/main/java/com/redhat/thermostat/cli/AppContextSetupImpl.java common/src/main/java/com/redhat/thermostat/cli/CommandContext.java common/src/main/java/com/redhat/thermostat/cli/CommandContextFactory.java common/src/main/java/com/redhat/thermostat/cli/CommandContextImpl.java common/src/main/java/com/redhat/thermostat/cli/CommandRegistry.java common/src/main/java/com/redhat/thermostat/cli/ConnectionConfiguration.java common/src/main/java/com/redhat/thermostat/test/TestCommandContextFactory.java common/src/test/java/com/redhat/thermostat/cli/AppContextSetupImplTest.java common/src/test/java/com/redhat/thermostat/cli/CommandRegistryTest.java common/src/test/java/com/redhat/thermostat/cli/HelpCommandTest.java common/src/test/java/com/redhat/thermostat/cli/LauncherTest.java common/src/test/java/com/redhat/thermostat/cli/TestCommandContextFactory.java tools/pom.xml tools/src/main/java/com/redhat/thermostat/tools/cli/ListVMsCommand.java tools/src/main/java/com/redhat/thermostat/tools/cli/VMListFormatter.java tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command tools/src/test/java/com/redhat/thermostat/tools/cli/ListVMsCommandTest.java
diffstat 18 files changed, 847 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/cli/AppContextSetup.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 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.cli;
+
+public interface AppContextSetup {
+
+    void setupAppContext(String dbUrl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/cli/AppContextSetupImpl.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 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.cli;
+
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.dao.Connection;
+import com.redhat.thermostat.common.dao.ConnectionProvider;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.MongoConnectionProvider;
+import com.redhat.thermostat.common.dao.MongoDAOFactory;
+
+class AppContextSetupImpl implements AppContextSetup {
+
+    @Override
+    public void setupAppContext(String dbUrl) {
+        StartupConfiguration config = new ConnectionConfiguration(dbUrl);
+        
+        ConnectionProvider connProv = new MongoConnectionProvider(config);
+        DAOFactory daoFactory = new MongoDAOFactory(connProv);
+        Connection connection = daoFactory.getConnection();
+        connection.connect();
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+
+    }
+
+}
--- a/common/src/main/java/com/redhat/thermostat/cli/CommandContext.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/CommandContext.java	Mon Apr 16 14:23:46 2012 +0200
@@ -44,4 +44,5 @@
 
     CommandRegistry getCommandRegistry();
 
+    AppContextSetup getAppContextSetup();
 }
--- a/common/src/main/java/com/redhat/thermostat/cli/CommandContextFactory.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/CommandContextFactory.java	Mon Apr 16 14:23:46 2012 +0200
@@ -44,17 +44,21 @@
         return instance;
     }
 
-    static void setInstance(CommandContextFactory ctxFactory) {
+    public static void setInstance(CommandContextFactory ctxFactory) {
         instance = ctxFactory;
     }
 
     private CommandRegistry commandRegistry = new CommandRegistry();
 
     public CommandContext createContext(final String[] args) {
-        return new CommandContextImpl(args, commandRegistry);
+        return new CommandContextImpl(args, commandRegistry, getAppContextSetup());
     }
 
-    CommandRegistry getCommandRegistry() {
+    protected AppContextSetup getAppContextSetup() {
+        return new AppContextSetupImpl();
+    }
+
+    protected CommandRegistry getCommandRegistry() {
         return commandRegistry;
     }
 
--- a/common/src/main/java/com/redhat/thermostat/cli/CommandContextImpl.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/CommandContextImpl.java	Mon Apr 16 14:23:46 2012 +0200
@@ -40,10 +40,12 @@
 
     private String[] arguments;
     private CommandRegistry commandRegistry;
+    private AppContextSetup appContextSetup;
 
-    CommandContextImpl(String[] args, CommandRegistry cmdReg) {
+    CommandContextImpl(String[] args, CommandRegistry cmdReg, AppContextSetup appContextSetup) {
         arguments = args;
         commandRegistry = cmdReg;
+        this.appContextSetup = appContextSetup;
     }
 
     @Override
@@ -61,4 +63,9 @@
         return commandRegistry;
     }
 
+    @Override
+    public AppContextSetup getAppContextSetup() {
+        return appContextSetup;
+    }
+
 }
--- a/common/src/main/java/com/redhat/thermostat/cli/CommandRegistry.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/CommandRegistry.java	Mon Apr 16 14:23:46 2012 +0200
@@ -45,7 +45,7 @@
 
     private Map<String,Command> commands;
 
-    CommandRegistry() {
+    public CommandRegistry() {
         commands = new HashMap<>();
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/cli/ConnectionConfiguration.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 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.cli;
+
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+class ConnectionConfiguration implements StartupConfiguration {
+
+    private String dbUrl;
+
+    ConnectionConfiguration(String dbUrl) {
+        this.dbUrl = dbUrl;
+    }
+
+    @Override
+    public String getDBConnectionString() {
+        return dbUrl;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/test/TestCommandContextFactory.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012 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.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import com.redhat.thermostat.cli.AppContextSetup;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandContextFactory;
+import com.redhat.thermostat.cli.CommandRegistry;
+import com.redhat.thermostat.cli.Console;
+
+public class TestCommandContextFactory extends CommandContextFactory {
+
+    private ByteArrayOutputStream out;
+    private ByteArrayOutputStream err;
+    private ByteArrayInputStream in;
+
+    public TestCommandContextFactory() {
+        reset();
+    }
+
+    private CommandRegistry commandRegistry = new CommandRegistry();
+
+    private AppContextSetup appContextSetup = new AppContextSetup() {
+
+        @Override
+        public void setupAppContext(String dbUrl) {
+            // We do nothing for now.
+        }
+    };
+
+    private class TestConsole implements Console {
+
+        @Override
+        public PrintStream getOutput() {
+            return new PrintStream(out);
+        }
+
+        @Override
+        public PrintStream getError() {
+            return new PrintStream(err);
+        }
+
+        @Override
+        public InputStream getInput() {
+            return in;
+        }
+        
+    }
+
+    @Override
+    public CommandContext createContext(final String[] args) {
+        return new CommandContext() {
+
+            @Override
+            public Console getConsole() {
+                return new TestConsole();
+            }
+
+            @Override
+            public String[] getArguments() {
+                return args;
+            }
+
+            @Override
+            public CommandRegistry getCommandRegistry() {
+                return commandRegistry;
+            }
+
+            @Override
+            public AppContextSetup getAppContextSetup() {
+                return TestCommandContextFactory.this.getAppContextSetup();
+            }
+            
+        };
+    }
+
+    @Override
+    public CommandRegistry getCommandRegistry() {
+        return commandRegistry;
+    }
+
+    @Override
+    protected AppContextSetup getAppContextSetup() {
+        return appContextSetup;
+    }
+
+    public String getOutput() {
+        return new String(out.toByteArray());
+    }
+
+    void setInput(String input) {
+        in = new ByteArrayInputStream(input.getBytes());
+    }
+
+    public Object getError() {
+        return new String(err.toByteArray());
+    }
+
+    public void reset() {
+        out = new ByteArrayOutputStream();
+        err = new ByteArrayOutputStream();
+        in = new ByteArrayInputStream(new byte[0]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/cli/AppContextSetupImplTest.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2012 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.cli;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.redhat.thermostat.cli.AppContextSetupImpl;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.dao.MongoConnection;
+import com.redhat.thermostat.common.dao.MongoConnectionProvider;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(MongoConnectionProvider.class)
+public class AppContextSetupImplTest {
+
+    @Test
+    public void testSetup() throws Exception {
+        // TODO: Figure out how to test this class without using PowerMock.
+        final MongoConnection conn = mock(MongoConnection.class);
+        PowerMockito.whenNew(MongoConnection.class).withArguments(Mockito.any(StartupConfiguration.class)).thenAnswer(new Answer<MongoConnection>() {
+
+            @Override
+            public MongoConnection answer(InvocationOnMock invocation)
+                    throws Throwable {
+                StartupConfiguration conf = (StartupConfiguration) invocation.getArguments()[0];
+                assertEquals("mongodb://fluff:27518", conf.getDBConnectionString());
+                return conn;
+            }
+            
+        });
+        AppContextSetupImpl setup = new AppContextSetupImpl();
+
+        setup.setupAppContext("mongodb://fluff:27518");
+
+        assertNotNull(ApplicationContext.getInstance().getDAOFactory());
+    }
+
+}
--- a/common/src/test/java/com/redhat/thermostat/cli/CommandRegistryTest.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/cli/CommandRegistryTest.java	Mon Apr 16 14:23:46 2012 +0200
@@ -49,6 +49,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
 public class CommandRegistryTest {
 
     private CommandRegistry registry;
--- a/common/src/test/java/com/redhat/thermostat/cli/HelpCommandTest.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/cli/HelpCommandTest.java	Mon Apr 16 14:23:46 2012 +0200
@@ -44,6 +44,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
 public class HelpCommandTest {
 
     private TestCommandContextFactory  ctxFactory;
--- a/common/src/test/java/com/redhat/thermostat/cli/LauncherTest.java	Wed Apr 11 16:31:42 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/cli/LauncherTest.java	Mon Apr 16 14:23:46 2012 +0200
@@ -44,6 +44,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
 public class LauncherTest {
 
     private static class TestCmd1 implements TestCommand.Handle {
--- a/common/src/test/java/com/redhat/thermostat/cli/TestCommandContextFactory.java	Wed Apr 11 16:31:42 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012 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.cli;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.PrintStream;
-
-class TestCommandContextFactory extends CommandContextFactory {
-
-    private ByteArrayOutputStream out;
-    private ByteArrayOutputStream err;
-    private ByteArrayInputStream in;
-
-    TestCommandContextFactory() {
-        reset();
-    }
-
-    private CommandRegistry commandRegistry = new CommandRegistry();
-
-    private class TestConsole implements Console {
-
-        @Override
-        public PrintStream getOutput() {
-            return new PrintStream(out);
-        }
-
-        @Override
-        public PrintStream getError() {
-            return new PrintStream(err);
-        }
-
-        @Override
-        public InputStream getInput() {
-            return in;
-        }
-        
-    }
-
-    @Override
-    public CommandContext createContext(final String[] args) {
-        return new CommandContext() {
-
-            @Override
-            public Console getConsole() {
-                return new TestConsole();
-            }
-
-            @Override
-            public String[] getArguments() {
-                return args;
-            }
-
-            @Override
-            public CommandRegistry getCommandRegistry() {
-                return commandRegistry;
-            }
-            
-        };
-    }
-
-    @Override
-    CommandRegistry getCommandRegistry() {
-        return commandRegistry;
-    }
-
-    String getOutput() {
-        return new String(out.toByteArray());
-    }
-
-    void setInput(String input) {
-        in = new ByteArrayInputStream(input.getBytes());
-    }
-
-    Object getError() {
-        return new String(err.toByteArray());
-    }
-
-    void reset() {
-        out = new ByteArrayOutputStream();
-        err = new ByteArrayOutputStream();
-        in = new ByteArrayInputStream(new byte[0]);
-    }
-}
--- a/tools/pom.xml	Wed Apr 11 16:31:42 2012 +0200
+++ b/tools/pom.xml	Mon Apr 16 14:23:46 2012 +0200
@@ -64,6 +64,16 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-api-mockito</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-module-junit4</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-common</artifactId>
       <version>${project.version}</version>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/ListVMsCommand.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2012 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.tools.cli;
+
+import java.util.Collection;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+
+import com.redhat.thermostat.cli.Command;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmInfoDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+
+public class ListVMsCommand implements Command {
+
+    private static final String NAME = "list-vms";
+
+    // TODO: Localize.
+    private static final String DESCRIPTION = "lists all currently monitored VMs";
+
+    private static final String USAGE = "list-vms --dbUrl URL\n\n"
+                                        + DESCRIPTION + "\n\n\t"
+                                        + "Options:\n\n"
+                                        + "--dbUrl URL  the URL of the storage to connect to.\n";
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+
+        OptionSet options = parseArguments(ctx);
+        String dbUrl = getDBURL(options);
+
+        ctx.getAppContextSetup().setupAppContext(dbUrl);
+
+        DAOFactory daoFactory = ApplicationContext.getInstance().getDAOFactory();
+        HostInfoDAO hostsDAO = daoFactory.getHostInfoDAO();
+        Collection<HostRef> hosts = hostsDAO.getHosts();
+        VmInfoDAO vmsDAO = daoFactory.getVmInfoDAO();
+        VMListFormatter formatter = new VMListFormatter();
+        for (HostRef host : hosts) {
+            Collection<VmRef> vms = vmsDAO.getVMs(host);
+            for (VmRef vm : vms) {
+                formatter.addVM(vm);
+            }
+        }
+        formatter.format(ctx.getConsole().getOutput());
+    }
+
+    private OptionSet parseArguments(CommandContext ctx) throws CommandException {
+        OptionParser parser = new OptionParser();
+        parser.accepts("dbUrl").withRequiredArg();
+        try {
+            OptionSet options = parser.parse(ctx.getArguments());
+            if (! options.nonOptionArguments().isEmpty()) {
+                throw new CommandException("Unknown arguments: " + options.nonOptionArguments());
+            }
+            return options;
+        } catch (OptionException ex) {
+            throw new CommandException(ex);
+        }
+    }
+
+    private String getDBURL(OptionSet options) {
+        String dbUrl = (String) options.valueOf("dbUrl");
+        return dbUrl;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getDescription() {
+        return DESCRIPTION;
+    }
+
+    @Override
+    public String getUsage() {
+        return USAGE;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMListFormatter.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 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.tools.cli;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmRef;
+
+class VMListFormatter {
+
+    // TODO: Localize.
+    private static final String HOST = "HOST";
+
+    private static final String VM_ID = "VM_ID";
+
+    private static final String VM_NAME = "VM_NAME";
+
+    private List<VmRef> vms = new ArrayList<>();
+
+    private int longestHost = HOST.length();
+    private int longestVmId = VM_ID.length();
+
+    void addVM(VmRef vm) {
+        vms.add(vm);
+        longestHost = Math.max(longestHost, vm.getAgent().getHostName().length());
+        longestVmId = Math.max(longestVmId, vm.getIdString().length());
+    }
+
+    void format(PrintStream output) {
+        printHeader(output);
+        for (VmRef vm : vms) {
+            printVM(output, vm);
+        }
+    }
+
+    private void printHeader(PrintStream output) {
+        printLine(output, HOST, VM_ID, VM_NAME);
+    }
+
+    private void printVM(PrintStream output, VmRef vm) {
+        printLine(output, vm.getAgent().getHostName(), vm.getId().toString(), vm.getName());
+    }
+
+    private void printLine(PrintStream output, String host, String vmId, String vmName) {
+        output.print(host);
+        output.print(fillSpace(longestHost - host.length() + 1));
+        output.print(vmId);
+        output.print(fillSpace(longestVmId - vmId.length() + 1));
+        output.print(vmName);
+        output.print('\n');
+    }
+
+    private String fillSpace(int n) {
+        StringBuilder s = new StringBuilder();
+        for (int i = 0; i < n; i++) {
+            s.append(' ');
+        }
+        return s.toString();
+    }
+
+}
--- a/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Wed Apr 11 16:31:42 2012 +0200
+++ b/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Mon Apr 16 14:23:46 2012 +0200
@@ -2,3 +2,4 @@
 com.redhat.thermostat.tools.ThermostatService
 com.redhat.thermostat.tools.db.DBService
 com.redhat.thermostat.agent.AgentApplication
+com.redhat.thermostat.tools.cli.ListVMsCommand
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/ListVMsCommandTest.java	Mon Apr 16 14:23:46 2012 +0200
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2012 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.tools.cli;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.cli.AppContextSetup;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandContextFactory;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmInfoDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
+public class ListVMsCommandTest {
+
+    private ListVMsCommand cmd;
+    private AppContextSetup appContextSetup;
+    private TestCommandContextFactory cmdCtxFactory;
+    private HostInfoDAO hostsDAO;
+    private VmInfoDAO vmsDAO;
+
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+        setupCommandContextFactory();
+
+        cmd = new ListVMsCommand();
+
+        setupDAOs();
+
+    }
+
+    private void setupCommandContextFactory() {
+        appContextSetup = mock(AppContextSetup.class);
+        cmdCtxFactory = new TestCommandContextFactory() {
+            @Override
+            protected AppContextSetup getAppContextSetup() {
+                return appContextSetup;
+            }
+        };
+        CommandContextFactory.setInstance(cmdCtxFactory);
+    }
+
+    private void setupDAOs() {
+        hostsDAO = mock(HostInfoDAO.class);
+        vmsDAO = mock(VmInfoDAO.class);
+        DAOFactory daoFactory = mock(DAOFactory.class);
+        when(daoFactory.getHostInfoDAO()).thenReturn(hostsDAO);
+        when(daoFactory.getVmInfoDAO()).thenReturn(vmsDAO);
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+    }
+
+    @After
+    public void tearDown() {
+        vmsDAO = null;
+        hostsDAO = null;
+        cmdCtxFactory = null;
+        cmd = null;
+        appContextSetup = null;
+        CommandContextFactory.setInstance(new CommandContextFactory());
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @Test
+    public void testRunCreatesConnectionFromArgumentURL() throws CommandException {
+
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl", "mongo://fluff:12345" });
+
+        cmd.run(ctx);
+
+        verify(appContextSetup).setupAppContext("mongo://fluff:12345");
+
+    }
+
+    @Test(expected=CommandException.class)
+    public void testUnknownOptionThrowsCommandException() throws CommandException {
+
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--fluff", "mongo://fluff:12345" });
+
+        cmd.run(ctx);
+    }
+
+    @Test(expected=CommandException.class)
+    public void testMissingURLThrowsCommandException() throws CommandException {
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl" });
+
+        cmd.run(ctx);
+    }
+
+    @Test(expected=CommandException.class)
+    public void testUnknownOptions() throws CommandException {
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl", "fluff", "fluff"});
+
+        cmd.run(ctx);
+    }
+
+    @Test(expected=CommandException.class)
+    public void testUnknownOptions2() throws CommandException {
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl"});
+
+        cmd.run(ctx);
+    }
+
+    @Test
+    public void verifyOutputFormatOneLine() throws CommandException {
+
+        HostRef host1 = new HostRef("123", "h1");
+        when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1));
+        when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(new VmRef(host1, 1, "n")));
+
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl", "fluff" });
+
+        cmd.run(ctx);
+
+        String output = cmdCtxFactory.getOutput();
+        assertEquals("HOST VM_ID VM_NAME\n" +
+                     "h1   1     n\n", output);
+    }
+
+    @Test
+    public void verifyOutputFormatMultiLines() throws CommandException {
+
+        HostRef host1 = new HostRef("123", "h1");
+        HostRef host2 = new HostRef("456", "longhostname");
+        when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1, host2));
+
+        when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(new VmRef(host1, 1, "n"), new VmRef(host1, 2, "n1")));
+        when(vmsDAO.getVMs(host2)).thenReturn(Arrays.asList(new VmRef(host2, 123456, "longvmname")));
+
+        CommandContext ctx = cmdCtxFactory.createContext(new String[] { "--dbUrl", "fluff" });
+
+        cmd.run(ctx);
+
+        String output = cmdCtxFactory.getOutput();
+        assertEquals("HOST         VM_ID  VM_NAME\n" +
+                     "h1           1      n\n" +
+                     "h1           2      n1\n" +
+                     "longhostname 123456 longvmname\n", output);
+    }
+
+    @Test
+    public void testName() {
+        assertEquals("list-vms", cmd.getName());
+    }
+
+    @Test
+    public void testDescription() {
+        assertEquals("lists all currently monitored VMs", cmd.getDescription());
+    }
+
+    @Test
+    public void testUsage() {
+        String expected = "list-vms --dbUrl URL\n\n"
+                + "lists all currently monitored VMs\n\n\t"
+                + "Options:\n\n"
+                + "--dbUrl URL  the URL of the storage to connect to.\n";
+
+        assertEquals(expected, cmd.getUsage());
+    }
+}