changeset 1551:17bebd72844e

Show possibly-dead VMs as UNKNOWN in cli Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-November/011471.html
author Omair Majid <omajid@redhat.com>
date Tue, 18 Nov 2014 13:43:44 -0500
parents 96b8227008fa
children 7b820886678a
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMInfoCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListVMsCommandTest.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VMInfoCommandTest.java storage/core/src/main/java/com/redhat/thermostat/storage/model/VmInfo.java
diffstat 8 files changed, 186 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ListVMsCommand.java	Tue Nov 18 13:43:44 2014 -0500
@@ -48,8 +48,10 @@
 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.VmInfo;
 
 public class ListVMsCommand extends AbstractCommand {
@@ -76,6 +78,9 @@
         Collection<HostRef> hosts = hostsDAO.getHosts();
         context.ungetService(hostsDAORef);
 
+        ServiceReference agentInfoDAORef = context.getServiceReference(AgentInfoDAO.class.getName());
+        AgentInfoDAO agentInfoDAO = (AgentInfoDAO) context.getService(agentInfoDAORef);
+
         ServiceReference vmsDAORef = context.getServiceReference(VmInfoDAO.class.getName());
         requireNonNull(vmsDAORef, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE));
         VmInfoDAO vmsDAO = (VmInfoDAO) context.getService(vmsDAORef);
@@ -83,10 +88,11 @@
         VMListFormatter formatter = new VMListFormatter();
         formatter.addHeader();
         for (HostRef host : hosts) {
+            AgentInformation agentInfo = agentInfoDAO.getAgentInformation(host);
             Collection<VmRef> vms = vmsDAO.getVMs(host);
             for (VmRef vm : vms) {
                 VmInfo info = vmsDAO.getVmInfo(vm);
-                formatter.addVM(vm, info);
+                formatter.addVM(vm, agentInfo, info);
             }
         }
         formatter.format(ctx.getConsole().getOutput());
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Tue Nov 18 13:43:44 2014 -0500
@@ -96,9 +96,9 @@
     COLUMN_HEADER_VM_STATUS,
     COLUMN_HEADER_TIME,
 
-    VM_STOP_TIME_RUNNING,
-    VM_STATUS_ALIVE,
-    VM_STATUS_DEAD,
+    VM_STATUS_RUNNING,
+    VM_STATUS_EXITED,
+    VM_STATUS_UNKNOWN,
 
     HOSTID_REQUIRED_MESSAGE,
     VMID_REQUIRED_MESSAGE,
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMInfoCommand.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMInfoCommand.java	Tue Nov 18 13:43:44 2014 -0500
@@ -52,15 +52,18 @@
 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.DAOException;
 import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
 import com.redhat.thermostat.storage.model.VmInfo;
 
 public class VMInfoCommand extends AbstractCommand {
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    private static final String STILL_ALIVE = translator.localize(LocaleResources.VM_STOP_TIME_RUNNING).getContents();
+    private static final String STATUS_RUNNING = translator.localize(LocaleResources.VM_STATUS_RUNNING).getContents();
+    private static final String STATUS_UNKNOWN = translator.localize(LocaleResources.VM_STATUS_UNKNOWN).getContents();
 
     private final BundleContext context;
 
@@ -75,6 +78,10 @@
 
     @Override
     public void run(CommandContext ctx) throws CommandException {
+        ServiceReference agentsDAORef = context.getServiceReference(AgentInfoDAO.class.getName());
+        requireNonNull(agentsDAORef, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE));
+        AgentInfoDAO agentsDAO = (AgentInfoDAO) context.getService(agentsDAORef);
+
         ServiceReference vmsDAORef = context.getServiceReference(VmInfoDAO.class.getName());
         requireNonNull(vmsDAORef, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE));
         VmInfoDAO vmsDAO = (VmInfoDAO) context.getService(vmsDAORef);
@@ -82,11 +89,12 @@
         HostVMArguments hostVMArgs = new HostVMArguments(ctx.getArguments(), true, false);
         HostRef host = hostVMArgs.getHost();
         VmRef vm = hostVMArgs.getVM();
+        AgentInformation agentInfo = agentsDAO.getAgentInformation(host);
         try {
             if (vm != null) {
-                getAndPrintVMInfo(ctx, vmsDAO, vm);
+                getAndPrintVMInfo(ctx, agentInfo, vmsDAO, vm);
             } else {
-                getAndPrintAllVMInfo(ctx, vmsDAO, host);
+                getAndPrintAllVMInfo(ctx, agentInfo, vmsDAO, host);
 
             }
         } catch (DAOException ex) {
@@ -96,14 +104,14 @@
         }
     }
 
-    private void getAndPrintAllVMInfo(CommandContext ctx, VmInfoDAO vmsDAO, HostRef host) {
+    private void getAndPrintAllVMInfo(CommandContext ctx, AgentInformation agentInfo, VmInfoDAO vmsDAO, HostRef host) {
         Collection<VmRef> vms = vmsDAO.getVMs(host);
         for (VmRef vm : vms) {
-            getAndPrintVMInfo(ctx, vmsDAO, vm);
+            getAndPrintVMInfo(ctx, agentInfo, vmsDAO, vm);
         }
     }
 
-    private void getAndPrintVMInfo(CommandContext ctx, VmInfoDAO vmsDAO, VmRef vm) {
+    private void getAndPrintVMInfo(CommandContext ctx, AgentInformation agentInfo, VmInfoDAO vmsDAO, VmRef vm) {
 
         VmInfo vmInfo = vmsDAO.getVmInfo(vm);
 
@@ -111,11 +119,7 @@
         table.printLine(translator.localize(LocaleResources.VM_INFO_VM_ID).getContents(), vmInfo.getVmId());
         table.printLine(translator.localize(LocaleResources.VM_INFO_PROCESS_ID).getContents(), String.valueOf(vmInfo.getVmPid()));
         table.printLine(translator.localize(LocaleResources.VM_INFO_START_TIME).getContents(), new Date(vmInfo.getStartTimeStamp()).toString());
-        if (vmInfo.isAlive()) {
-            table.printLine(translator.localize(LocaleResources.VM_INFO_STOP_TIME).getContents(), STILL_ALIVE);
-        } else {
-            table.printLine(translator.localize(LocaleResources.VM_INFO_STOP_TIME).getContents(), new Date(vmInfo.getStopTimeStamp()).toString());
-        }
+        table.printLine(translator.localize(LocaleResources.VM_INFO_STOP_TIME).getContents(), getVmStopTimeForDisplay(agentInfo, vmInfo));
         printUserInfo(vmInfo, table);
         table.printLine(translator.localize(LocaleResources.VM_INFO_MAIN_CLASS).getContents(), vmInfo.getMainClass());
         table.printLine(translator.localize(LocaleResources.VM_INFO_COMMAND_LINE).getContents(), vmInfo.getJavaCommandLine());
@@ -127,6 +131,19 @@
         table.render(out);
     }
 
+    private String getVmStopTimeForDisplay(AgentInformation agentInfo, VmInfo vmInfo) {
+        switch (vmInfo.isAlive(agentInfo)) {
+        case RUNNING:
+            return STATUS_RUNNING;
+        case EXITED:
+            return new Date(vmInfo.getStopTimeStamp()).toString();
+        case UNKNOWN:
+            return STATUS_UNKNOWN;
+        default:
+            throw new AssertionError("Unknown VM status");
+        }
+    }
+
     private void printUserInfo(VmInfo vmInfo, TableRenderer table) {
         // Check if we have valid user info
         long uid = vmInfo.getUid();
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Tue Nov 18 13:43:44 2014 -0500
@@ -41,6 +41,7 @@
 import com.redhat.thermostat.common.cli.TableRenderer;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.model.AgentInformation;
 import com.redhat.thermostat.storage.model.VmInfo;
 
 class VMListFormatter {
@@ -55,13 +56,14 @@
     private static final String VM_NAME = translator.localize(LocaleResources.COLUMN_HEADER_VM_NAME).getContents();
     private static final String VM_STATUS = translator.localize(LocaleResources.COLUMN_HEADER_VM_STATUS).getContents();
 
-    private static final String STATUS_ALIVE = translator.localize(LocaleResources.VM_STATUS_ALIVE).getContents();
-    private static final String STATUS_DEAD = translator.localize(LocaleResources.VM_STATUS_DEAD).getContents();
+    private static final String STATUS_RUNNING = translator.localize(LocaleResources.VM_STATUS_RUNNING).getContents();
+    private static final String STATUS_EXITED = translator.localize(LocaleResources.VM_STATUS_EXITED).getContents();
+    private static final String STATUS_UNKNOWN = translator.localize(LocaleResources.VM_STATUS_UNKNOWN).getContents();
 
     private final TableRenderer tableRenderer = new TableRenderer(NUM_COLUMNS);
 
-    void addVM(VmRef vm, VmInfo info) {
-        printVM(vm, info);
+    void addVM(VmRef vm, AgentInformation agentInfo, VmInfo info) {
+        printVM(vm, agentInfo, info);
     }
 
     void addHeader() {
@@ -72,12 +74,12 @@
         tableRenderer.render(out);
     }
 
-    private void printVM(VmRef vm, VmInfo info) {
+    private void printVM(VmRef vm, AgentInformation agentInfo, VmInfo info) {
         printLine(vm.getHostRef().getAgentId(),
                   vm.getHostRef().getHostName(),
                   vm.getVmId(),
                   vm.getPid().toString(),
-                  info.isAlive() ? STATUS_ALIVE : STATUS_DEAD,
+                  getAliveStatus(info.isAlive(agentInfo)),
                   vm.getName());
     }
 
@@ -85,5 +87,17 @@
         tableRenderer.printLine(hostId, host, vmId, pid, status, vmName);
     }
 
+    private String getAliveStatus(VmInfo.AliveStatus status) {
+        switch (status) {
+        case RUNNING:
+            return STATUS_RUNNING;
+        case EXITED:
+            return STATUS_EXITED;
+        case UNKNOWN:
+            return STATUS_UNKNOWN;
+        default:
+            throw new AssertionError("Unknown VM status");
+        }
+    }
 }
 
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Tue Nov 18 13:43:44 2014 -0500
@@ -55,9 +55,9 @@
 COLUMN_HEADER_VM_STATUS = STATUS
 COLUMN_HEADER_TIME = TIME
 
-VM_STOP_TIME_RUNNING = <Running>
-VM_STATUS_ALIVE = RUNNING
-VM_STATUS_DEAD = EXITED
+VM_STATUS_RUNNING = RUNNING
+VM_STATUS_EXITED = EXITED
+VM_STATUS_UNKNOWN = UNKNOWN
 
 HOSTID_REQUIRED_MESSAGE = a hostId is required
 VMID_REQUIRED_MESSAGE = a vmId is required
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListVMsCommandTest.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListVMsCommandTest.java	Tue Nov 18 13:43:44 2014 -0500
@@ -55,8 +55,10 @@
 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.VmInfo;
 import com.redhat.thermostat.test.TestCommandContextFactory;
 import com.redhat.thermostat.testutils.StubBundleContext;
@@ -68,6 +70,7 @@
     private ListVMsCommand cmd;
     private TestCommandContextFactory cmdCtxFactory;
     private HostInfoDAO hostsDAO;
+    private AgentInfoDAO agentsDAO;
     private VmInfoDAO vmsDAO;
     private StubBundleContext context;
 
@@ -80,6 +83,7 @@
 
         hostsDAO = mock(HostInfoDAO.class);
         vmsDAO = mock(VmInfoDAO.class);
+        agentsDAO = mock(AgentInfoDAO.class);
     }
 
     private void setupCommandContextFactory() {
@@ -97,13 +101,17 @@
     @Test
     public void verifyOutputFormatOneLine() throws CommandException {
         context.registerService(HostInfoDAO.class, hostsDAO, null);
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
 
         HostRef host1 = new HostRef("123", "h1");
+        AgentInformation agent1 = new AgentInformation();
+        agent1.setAlive(true);
         String vmId = "vmId";
         VmRef vm1 = new VmRef(host1, vmId, 1, "n");
         VmInfo vm1Info = new VmInfo("foo", vmId, 1, 0, 1, "", "", "", "", "", "", "", "", null, null, null, -1, null);
         when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1));
+        when(agentsDAO.getAgentInformation(isA(HostRef.class))).thenReturn(agent1);
         when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(vm1));
         when(vmsDAO.getVmInfo(eq(vm1))).thenReturn(vm1Info);
 
@@ -121,12 +129,17 @@
     @Test
     public void verifyOutputFormatMultiLines() throws CommandException {
         context.registerService(HostInfoDAO.class, hostsDAO, null);
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
 
         HostRef host1 = new HostRef("123", "h1");
         HostRef host2 = new HostRef("456", "longhostname");
         when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1, host2));
 
+        AgentInformation agent1 = new AgentInformation();
+        agent1.setAlive(true);
+        when(agentsDAO.getAgentInformation(isA(HostRef.class))).thenReturn(agent1);
+
         VmRef vm1 = new VmRef(host1, "vm1", 1, "n");
         VmRef vm2 = new VmRef(host1, "vm2", 2, "n1");
         VmRef vm3 = new VmRef(host2, "vm3", 123456, "longvmname");
@@ -150,10 +163,45 @@
                      "123     h1           vm2   2      EXITED n1\n" +
                      "456     longhostname vm3   123456 EXITED longvmname\n", output);
     }
+
+    @Test
+    public void verifyUnknownStatusIfAgentExited() throws CommandException {
+        context.registerService(HostInfoDAO.class, hostsDAO, null);
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
+        context.registerService(VmInfoDAO.class, vmsDAO, null);
+
+        HostRef host1 = new HostRef("123", "h1");
+        when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1));
+
+        AgentInformation agent1 = new AgentInformation();
+        agent1.setAlive(false);
+        when(agentsDAO.getAgentInformation(isA(HostRef.class))).thenReturn(agent1);
+
+        VmRef vm1 = new VmRef(host1, "vm1", 1, "n");
+        VmRef vm2 = new VmRef(host1, "vm2", 2, "n1");
+
+        VmInfo vm1Info = new VmInfo("foo", "vm1", 1, 0, 1, "", "", "", "", "", "", "", "", null, null, null, -1, null);
+        VmInfo vm2Info = new VmInfo("foo", "vm1", 1, 0, Long.MIN_VALUE, "", "", "", "", "", "", "", "", null, null, null, -1, null);
+
+        when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(vm1, vm2));
+        when(vmsDAO.getVmInfo(vm1)).thenReturn(vm1Info);
+        when(vmsDAO.getVmInfo(vm2)).thenReturn(vm2Info);
+
+        SimpleArguments args = new SimpleArguments();
+        CommandContext ctx = cmdCtxFactory.createContext(args);
+
+        cmd.run(ctx);
+
+        String output = cmdCtxFactory.getOutput();
+        assertEquals("HOST_ID HOST VM_ID VM_PID STATUS  VM_NAME\n" +
+                     "123     h1   vm1   1      EXITED  n\n" +
+                     "123     h1   vm2   2      UNKNOWN n1\n", output);
+    }
     
     @Test
     public void testNeedHostInfoDAO() throws CommandException {
         context.registerService(VmInfoDAO.class, vmsDAO, null);
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
 
         SimpleArguments args = new SimpleArguments();
         args.addArgument("--dbUrl", "fluff");
@@ -170,6 +218,7 @@
     @Test
     public void testNeedVmInfoDAO() throws CommandException {
         context.registerService(HostInfoDAO.class, hostsDAO, null);
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
 
         SimpleArguments args = new SimpleArguments();
         args.addArgument("--dbUrl", "fluff");
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VMInfoCommandTest.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VMInfoCommandTest.java	Tue Nov 18 13:43:44 2014 -0500
@@ -57,8 +57,10 @@
 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.DAOException;
 import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
 import com.redhat.thermostat.storage.model.VmInfo;
 import com.redhat.thermostat.test.Bug;
 import com.redhat.thermostat.test.TestCommandContextFactory;
@@ -82,17 +84,25 @@
     }
 
     private VMInfoCommand cmd;
+    private AgentInfoDAO agentsDAO;
     private VmInfoDAO vmsDAO;
     private TestCommandContextFactory cmdCtxFactory;
     private VmRef vm;
     private StubBundleContext context;
+    private AgentInformation agentInfo;
 
     @Before
     public void setUp() {
         context = new StubBundleContext();
         setupCommandContextFactory();
 
+        agentsDAO = mock(AgentInfoDAO.class);
+
         vmsDAO = mock(VmInfoDAO.class);
+
+        agentInfo = new AgentInformation();
+        agentInfo.setAlive(true);
+
         setupDAOs();
     }
 
@@ -102,6 +112,7 @@
 
     private void setupDAOs() {
         HostRef host = new HostRef("123", "dummy");
+        when(agentsDAO.getAgentInformation(host)).thenReturn(agentInfo);
         vm = new VmRef(host, VM_ID, -1, "dummy");
         Calendar start = Calendar.getInstance();
         start.set(2012, 5, 7, 15, 32, 0);
@@ -116,6 +127,7 @@
 
     @Test
     public void testVmInfo() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
         cmd = new VMInfoCommand(context);
         SimpleArguments args = new SimpleArguments();
@@ -137,6 +149,7 @@
     
     @Test
     public void testVmInfoNoUid() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         VmInfo info = vmsDAO.getVmInfo(vm);
         // Set parameters to those where user info cannot be obtained
         info.setUid(-1);
@@ -162,6 +175,7 @@
     
     @Test
     public void testVmInfoNoUsername() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         VmInfo info = vmsDAO.getVmInfo(vm);
         // Set parameters to those where user info cannot be obtained
         info.setUid(2000);
@@ -187,6 +201,7 @@
     
     @Test
     public void testNoVmInfoDAO() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         cmd = new VMInfoCommand(context);
         SimpleArguments args = new SimpleArguments();
         args.addArgument("vmId", "234");
@@ -202,6 +217,7 @@
 
     @Test
     public void testAllVmInfoForHost() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
         cmd = new VMInfoCommand(context);
         SimpleArguments args = new SimpleArguments();
@@ -222,6 +238,7 @@
 
     @Test
     public void testVmInfoUnknownVM() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
         cmd = new VMInfoCommand(context);
         SimpleArguments args = new SimpleArguments();
@@ -238,6 +255,7 @@
             url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1046")
     @Test
     public void testStopTime() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
         cmd = new VMInfoCommand(context);
         Calendar start = Calendar.getInstance();
@@ -253,7 +271,38 @@
         String expected = "VM ID:           " + vmId + "\n" +
                           "Process ID:      234\n" +
                           "Start time:      Thu Jun 07 15:32:00 UTC 2012\n" +
-                          "Stop time:       <Running>\n" +
+                          "Stop time:       RUNNING\n" +
+                          "User ID:         2000(myUser)\n" +
+                          "Main class:      mainClass\n" +
+                          "Command line:    commandLine\n" +
+                          "Java version:    vmVersion\n" +
+                          "Virtual machine: vmName\n" +
+                          "VM arguments:    vmArguments\n";
+        assertEquals(expected, cmdCtxFactory.getOutput());
+    }
+
+    @Test
+    public void testStopTimeWhenAgentDied() throws CommandException {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
+        context.registerService(VmInfoDAO.class, vmsDAO, null);
+
+        agentInfo.setAlive(false);
+
+        cmd = new VMInfoCommand(context);
+        Calendar start = Calendar.getInstance();
+        start.set(2012, 5, 7, 15, 32, 0);
+        final String vmId = "61a255db-1c27-43d6-aaee-28bb4788b8db";
+        VmInfo vmInfo = new VmInfo("foo", vmId, 234, start.getTimeInMillis(), Long.MIN_VALUE, "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new String[0], 2000, "myUser");
+        when(vmsDAO.getVmInfo(vm)).thenReturn(vmInfo);
+
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", VM_ID);
+        args.addArgument("hostId", "123");
+        cmd.run(cmdCtxFactory.createContext(args));
+        String expected = "VM ID:           " + vmId + "\n" +
+                          "Process ID:      234\n" +
+                          "Start time:      Thu Jun 07 15:32:00 UTC 2012\n" +
+                          "Stop time:       UNKNOWN\n" +
                           "User ID:         2000(myUser)\n" +
                           "Main class:      mainClass\n" +
                           "Command line:    commandLine\n" +
@@ -265,6 +314,7 @@
 
     @Test
     public void testStorageRequired() {
+        context.registerService(AgentInfoDAO.class, agentsDAO, null);
         context.registerService(VmInfoDAO.class, vmsDAO, null);
         cmd = new VMInfoCommand(context);
         assertTrue(cmd.isStorageRequired());
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/model/VmInfo.java	Tue Nov 18 12:37:38 2014 -0500
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/model/VmInfo.java	Tue Nov 18 13:43:44 2014 -0500
@@ -46,6 +46,16 @@
 @Entity
 public class VmInfo extends BasePojo {
 
+    public enum AliveStatus {
+        RUNNING,
+        EXITED,
+        /**
+         * We don't know what the status of this VM is. Possible cause: agent
+         * was shut down before the VM was.
+         */
+        UNKNOWN,
+    }
+
     @Entity
     public static class KeyValuePair implements Pojo {
     
@@ -253,10 +263,24 @@
         this.vmVersion = vmVersion;
     }
 
+    /**
+     * @deprecated This can incorrectly show a VM as running when the actual
+     *             status is unknown. Use {@link #isAlive(AgentInformation)}
+     *             instead.
+     */
+    @Deprecated
     public boolean isAlive() {
         return getStartTimeStamp() > getStopTimeStamp();
     }
-    
+
+    public AliveStatus isAlive(AgentInformation agentInfo) {
+        if (agentInfo.isAlive()) {
+            return (isAlive() ? AliveStatus.RUNNING : AliveStatus.EXITED);
+        } else {
+            return (isAlive() ? AliveStatus.UNKNOWN: AliveStatus.EXITED);
+        }
+    }
+
     public Map<String, String> getProperties() {
         return properties;
     }