changeset 307:5d4d4d1a0cf7

Implement VMInfoCommand. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-May/001350.html
author Roman Kennke <rkennke@redhat.com>
date Wed, 16 May 2012 15:20:08 +0200
parents 724f56f1a360
children edd01fa07b0d
files common/src/main/java/com/redhat/thermostat/common/dao/DAOException.java common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java common/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java tools/src/main/java/com/redhat/thermostat/tools/cli/VMInfoCommand.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 tools/src/test/java/com/redhat/thermostat/tools/cli/VMInfoCommandTest.java
diffstat 8 files changed, 410 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/DAOException.java	Wed May 16 15:20:08 2012 +0200
@@ -0,0 +1,57 @@
+/*
+ * 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.common.dao;
+
+public class DAOException extends RuntimeException {
+
+    public DAOException() {
+        super();
+    }
+
+    public DAOException(String message) {
+        super(message);
+    }
+
+    public DAOException(Throwable cause) {
+        super(cause);
+    }
+
+    public DAOException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Tue May 15 16:09:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Wed May 16 15:20:08 2012 +0200
@@ -62,6 +62,9 @@
         query.put(Key.AGENT_ID, ref.getAgent().getAgentId());
         query.put(vmIdKey, ref.getId());
         Chunk result = storage.find(query);
+        if (result == null) {
+            throw new DAOException("Unknown VM: host:" + ref.getAgent().getAgentId() + ";vm:" + ref.getId());
+        }
         return converter.fromChunk(result);
     }
 
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Tue May 15 16:09:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Wed May 16 15:20:08 2012 +0200
@@ -36,7 +36,7 @@
 
 package com.redhat.thermostat.common.dao;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
@@ -177,6 +177,28 @@
         // FIXME test environment, properties and loaded native libraries later
     }
 
+    @Test
+    public void testGetVmInfoUnknownVM() {
+
+        Storage storage = mock(Storage.class);
+
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn("system");
+
+        VmRef vmRef = mock(VmRef.class);
+        when(vmRef.getAgent()).thenReturn(hostRef);
+        when(vmRef.getId()).thenReturn(321);
+
+        VmInfoDAO dao = new VmInfoDAOImpl(storage);
+        try {
+            dao.getVmInfo(vmRef);
+            fail();
+        } catch (DAOException ex) {
+            assertEquals("Unknown VM: host:system;vm:321", ex.getMessage());
+        }
+
+    }
+
     private Storage setupStorageForSingleVM() {
         Chunk query1 = new Chunk(VmInfoDAO.vmInfoCategory, false);
         query1.put(Key.AGENT_ID, "123");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMInfoCommand.java	Wed May 16 15:20:08 2012 +0200
@@ -0,0 +1,128 @@
+/*
+ * 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.Arrays;
+import java.util.Collection;
+import java.util.Date;
+
+import com.redhat.thermostat.cli.ArgumentSpec;
+import com.redhat.thermostat.cli.Command;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.cli.SimpleArgumentSpec;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.DAOException;
+import com.redhat.thermostat.common.dao.DAOFactory;
+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.common.model.VmInfo;
+
+public class VMInfoCommand implements Command {
+
+    private static final String NAME = "vm-info";
+    private static final String DESCRIPTION = "shows basic information about a VM";
+    private static final String HOST_ID_ARGUMENT = "hostId";
+    private static final String VM_ID_ARGUMENT = "vmId";
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        DAOFactory daoFactory = ApplicationContext.getInstance().getDAOFactory();
+        VmInfoDAO vmsDAO = daoFactory.getVmInfoDAO();
+        String hostId = ctx.getArguments().getArgument(HOST_ID_ARGUMENT);
+        String vmId = ctx.getArguments().getArgument(VM_ID_ARGUMENT);
+        HostRef host = new HostRef(hostId, "dummy");
+        int parsedVmId = parseVmId(vmId);
+        VmRef vm = new VmRef(host, parsedVmId, "dummy");
+        try {
+            getAndPrintVMInfo(ctx, vmsDAO, vm);
+        } catch (DAOException ex) {
+            ctx.getConsole().getError().println(ex.getMessage());
+        }
+    }
+
+    private int parseVmId(String vmId) throws CommandException {
+        try {
+            return Integer.parseInt(vmId);
+        } catch (NumberFormatException ex) {
+            throw new CommandException("Invalid VM ID: " + vmId, ex);
+        }
+    }
+
+    private void getAndPrintVMInfo(CommandContext ctx, VmInfoDAO vmsDAO, VmRef vm) {
+        VmInfo vmInfo = vmsDAO.getVmInfo(vm);
+        PrintStream out = ctx.getConsole().getOutput();
+        out.println("Process ID:      " + vmInfo.getVmPid());
+        out.println("Start time:      " + new Date(vmInfo.getStartTimeStamp()));
+        out.println("Stop time:       " + new Date(vmInfo.getStopTimeStamp()));
+        out.println("Main class:      " + vmInfo.getMainClass());
+        out.println("Command line:    " + vmInfo.getJavaCommandLine());
+        out.println("Java version:    " + vmInfo.getJavaVersion());
+        out.println("Virtual machine: " + vmInfo.getVmName());
+        out.println("VM arguments:    " + vmInfo.getVmArguments());
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getDescription() {
+        return DESCRIPTION;
+    }
+
+    @Override
+    public String getUsage() {
+        return DESCRIPTION;
+    }
+
+    @Override
+    public Collection<ArgumentSpec> getAcceptedArguments() {
+        ArgumentSpec vmId = new SimpleArgumentSpec(VM_ID_ARGUMENT, "the ID of the VM to monitor", true, true);
+        ArgumentSpec hostId = new SimpleArgumentSpec(HOST_ID_ARGUMENT, "the ID of the host to monitor", true, true);
+        return Arrays.asList(vmId, hostId);
+    }
+
+    @Override
+    public boolean isStorageRequired() {
+        return true;
+    }
+
+}
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/VMListFormatter.java	Tue May 15 16:09:46 2012 +0200
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMListFormatter.java	Wed May 16 15:20:08 2012 +0200
@@ -45,6 +45,8 @@
 class VMListFormatter {
 
     // TODO: Localize.
+    private static final String HOST_ID = "HOST_ID";
+
     private static final String HOST = "HOST";
 
     private static final String VM_ID = "VM_ID";
@@ -53,11 +55,13 @@
 
     private List<VmRef> vms = new ArrayList<>();
 
+    private int longestHostId = HOST_ID.length();
     private int longestHost = HOST.length();
     private int longestVmId = VM_ID.length();
 
     void addVM(VmRef vm) {
         vms.add(vm);
+        longestHostId = Math.max(longestHostId, vm.getAgent().getAgentId().length());
         longestHost = Math.max(longestHost, vm.getAgent().getHostName().length());
         longestVmId = Math.max(longestVmId, vm.getIdString().length());
     }
@@ -70,14 +74,16 @@
     }
 
     private void printHeader(PrintStream output) {
-        printLine(output, HOST, VM_ID, VM_NAME);
+        printLine(output, HOST_ID, HOST, VM_ID, VM_NAME);
     }
 
     private void printVM(PrintStream output, VmRef vm) {
-        printLine(output, vm.getAgent().getHostName(), vm.getId().toString(), vm.getName());
+        printLine(output, vm.getAgent().getAgentId(), vm.getAgent().getHostName(), vm.getId().toString(), vm.getName());
     }
 
-    private void printLine(PrintStream output, String host, String vmId, String vmName) {
+    private void printLine(PrintStream output, String hostId, String host, String vmId, String vmName) {
+        output.print(hostId);
+        output.print(fillSpace(longestHostId - hostId.length() + 1));
         output.print(host);
         output.print(fillSpace(longestHost - host.length() + 1));
         output.print(vmId);
--- a/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Tue May 15 16:09:46 2012 +0200
+++ b/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Wed May 16 15:20:08 2012 +0200
@@ -4,3 +4,4 @@
 com.redhat.thermostat.agent.AgentApplication
 com.redhat.thermostat.tools.cli.ListVMsCommand
 com.redhat.thermostat.tools.cli.ShellCommand
+com.redhat.thermostat.tools.cli.VMInfoCommand
--- a/tools/src/test/java/com/redhat/thermostat/tools/cli/ListVMsCommandTest.java	Tue May 15 16:09:46 2012 +0200
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/ListVMsCommandTest.java	Wed May 16 15:20:08 2012 +0200
@@ -130,8 +130,8 @@
         cmd.run(ctx);
 
         String output = cmdCtxFactory.getOutput();
-        assertEquals("HOST VM_ID VM_NAME\n" +
-                     "h1   1     n\n", output);
+        assertEquals("HOST_ID HOST VM_ID VM_NAME\n" +
+                     "123     h1   1     n\n", output);
     }
 
     @Test
@@ -151,10 +151,10 @@
         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);
+        assertEquals("HOST_ID HOST         VM_ID  VM_NAME\n" +
+                     "123     h1           1      n\n" +
+                     "123     h1           2      n1\n" +
+                     "456     longhostname 123456 longvmname\n", output);
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/VMInfoCommandTest.java	Wed May 16 15:20:08 2012 +0200
@@ -0,0 +1,183 @@
+/*
+ * 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.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.cli.AppContextSetup;
+import com.redhat.thermostat.cli.ArgumentSpec;
+import com.redhat.thermostat.cli.CommandContextFactory;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.cli.SimpleArgumentSpec;
+import com.redhat.thermostat.cli.SimpleArguments;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.dao.DAOException;
+import com.redhat.thermostat.common.dao.DAOFactory;
+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.common.model.VmInfo;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
+public class VMInfoCommandTest {
+
+    private VMInfoCommand cmd;
+    private VmInfoDAO vmsDAO;
+    private AppContextSetup appContextSetup;
+    private TestCommandContextFactory cmdCtxFactory;
+
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+        setupCommandContextFactory();
+
+        cmd = new VMInfoCommand();
+
+        setupDAOs();
+
+    }
+
+    private void setupCommandContextFactory() {
+        appContextSetup = mock(AppContextSetup.class);
+        cmdCtxFactory = new TestCommandContextFactory() {
+            @Override
+            protected AppContextSetup getAppContextSetup() {
+                return appContextSetup;
+            }
+        };
+        CommandContextFactory.setInstance(cmdCtxFactory);
+    }
+
+    private void setupDAOs() {
+        vmsDAO = mock(VmInfoDAO.class);
+        HostRef host = new HostRef("123", "dummy");
+        VmRef vm = new VmRef(host, 234, "dummy");
+        Calendar start = Calendar.getInstance();
+        start.set(2012, 5, 7, 15, 32, 0);
+        Calendar end = Calendar.getInstance();
+        end.set(2013, 10, 1, 1, 22, 0);
+        VmInfo vmInfo = new VmInfo(234, start.getTimeInMillis(), end.getTimeInMillis(), "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new ArrayList<String>());
+        when(vmsDAO.getVmInfo(vm)).thenReturn(vmInfo);
+        when(vmsDAO.getVmInfo(new VmRef(host, 9876, "dummy"))).thenThrow(new DAOException("Unknown VM ID: 9876"));
+        DAOFactory daoFactory = mock(DAOFactory.class);
+        when(daoFactory.getVmInfoDAO()).thenReturn(vmsDAO);
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+    }
+
+
+    @Test
+    public void testVmInfo() throws CommandException {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", "234");
+        args.addArgument("hostId", "123");
+        cmd.run(cmdCtxFactory.createContext(args));
+        String expected = "Process ID:      234\n" +
+                          "Start time:      Thu Jun 07 15:32:00 CEST 2012\n" +
+                          "Stop time:       Fri Nov 01 01:22:00 CET 2013\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 testVmInfoUnknownVM() throws CommandException {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", "9876");
+        args.addArgument("hostId", "123");
+        cmd.run(cmdCtxFactory.createContext(args));
+        String expected = "Unknown VM ID: 9876\n";
+        assertEquals("", cmdCtxFactory.getOutput());
+        assertEquals(expected, cmdCtxFactory.getError());
+    }
+
+    @Test
+    public void testVmInfoNonNumericalVMID() throws CommandException {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", "fluff");
+        args.addArgument("hostId", "123");
+        try {
+            cmd.run(cmdCtxFactory.createContext(args));
+        } catch (CommandException ex) {
+            String expected = "Invalid VM ID: fluff";
+            assertEquals(expected, ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testName() {
+        assertEquals("vm-info", cmd.getName());
+    }
+
+    @Test
+    public void testDescription() {
+        assertEquals("shows basic information about a VM", cmd.getDescription());
+    }
+
+    @Test
+    public void testUsage() {
+        String expected = "shows basic information about a VM";
+
+        assertEquals(expected, cmd.getUsage());
+    }
+
+    @Test
+    public void testAcceptedArguments() {
+        Collection<ArgumentSpec> args = cmd.getAcceptedArguments();
+        assertNotNull(args);
+        assertEquals(2, args.size());
+        assertTrue(args.contains(new SimpleArgumentSpec("vmId", "the ID of the VM to monitor", true, true)));
+        assertTrue(args.contains(new SimpleArgumentSpec("hostId", "the ID of the host to monitor", true, true)));
+    }
+
+}