changeset 1535:f380c0ac5d57

VM-Stat command 'since' option addition Addresses the cli/shell side for PR2006 Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-November/011423.html
author Jie Kang <jkang@redhat.com>
date Wed, 05 Nov 2014 10:40:33 -0500
parents 50c227cb0b02
children 97512f3b25a5
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/SinceTimestampParser.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/SinceTimestampParserTest.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java distribution/config/commands/vm-stat.properties
diffstat 8 files changed, 301 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Tue Nov 04 17:03:43 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Wed Nov 05 10:40:33 2014 -0500
@@ -105,6 +105,8 @@
     PURGE_EXPENSIVE_OPERATION_PROMPT,
     PURGE_CANCELLED_MESSAGE,
     AFFIRMATIVE_RESPONSES,
+
+    VM_STAT_INVALID_SINCE_ARGUMENT,
     ;
 
     static final String RESOURCE_BUNDLE = "com.redhat.thermostat.client.cli.strings";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/SinceTimestampParser.java	Wed Nov 05 10:40:33 2014 -0500
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class SinceTimestampParser {
+
+    private final List<String> acceptedTimeUnits = Arrays.asList(new String[]{"DAYS", "HOURS", "MINUTES", "SECONDS"});
+
+    private final String parseString;
+    private final long defaultSinceTimestamp;
+    private final long startTimestamp;
+
+
+    public SinceTimestampParser(String parseString, long startTimestamp, long defaultSinceTimestamp) {
+        this.parseString = parseString;
+        this.startTimestamp = startTimestamp;
+        this.defaultSinceTimestamp = defaultSinceTimestamp;
+    }
+
+    public long parse() throws InvalidSinceTimestampFormatException {
+        long since;
+        if (parseString != null) {
+            since = parseString();
+        } else {
+            since = defaultSinceTimestamp;
+        }
+        return since;
+    }
+
+    private long parseString() throws InvalidSinceTimestampFormatException {
+        if (parseString.equals("all")) {
+            return Long.MIN_VALUE;
+        } else {
+            return parseValueFromString();
+        }
+    }
+
+    private long parseValueFromString() throws InvalidSinceTimestampFormatException {
+        try {
+            String[] split = parseString.split(":");
+            if (split.length != 2) {
+                throw new InvalidSinceTimestampFormatException("Invalid input");
+            }
+
+            long timeValue = Long.valueOf(split[0]);
+
+            String timeString = split[1];
+            TimeUnit timeUnit = getTimeUnitFromString(timeString);
+
+            long value = timeUnit.toMillis(timeValue);
+
+            if (!(value > 0)) {
+                throw new InvalidSinceTimestampFormatException("Time input must be greater than 0");
+            }
+
+            return startTimestamp - value;
+        } catch (Exception e) {
+            throw new InvalidSinceTimestampFormatException(e.getMessage());
+        }
+    }
+
+    private TimeUnit getTimeUnitFromString(String timeUnit) throws InvalidSinceTimestampFormatException {
+        timeUnit = timeUnit.toUpperCase();
+        if (!acceptedTimeUnits.contains(timeUnit)) {
+            throw new InvalidSinceTimestampFormatException("Invalid time unit. Accepted time units are: days, hours, minutes or seconds");
+        }
+        return TimeUnit.valueOf(timeUnit);
+    }
+
+    public static class InvalidSinceTimestampFormatException extends Exception {
+        InvalidSinceTimestampFormatException(String message) {
+            super(message);
+        }
+    }
+}
\ No newline at end of file
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Tue Nov 04 17:03:43 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Wed Nov 05 10:40:33 2014 -0500
@@ -57,16 +57,19 @@
 import com.redhat.thermostat.common.cli.AbstractCommand;
 import com.redhat.thermostat.common.cli.CommandContext;
 import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.CommandLineArgumentParseException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.storage.core.VmRef;
 
 public class VMStatCommand extends AbstractCommand {
 
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
     private static final Logger log = LoggingUtils.getLogger(VMStatCommand.class);
-    
+
     private List<VMStatPrintDelegate> delegates;
     private BundleContext context;
-    
+
     public VMStatCommand() {
         this(FrameworkUtil.getBundle(VMStatCommand.class).getBundleContext());
     }
@@ -93,11 +96,25 @@
 
     @Override
     public void run(final CommandContext ctx) throws CommandException {
+        long currentTime = System.currentTimeMillis();
+        long defaultSinceTime = currentTime - TimeUnit.MINUTES.toMillis(10);
+
         HostVMArguments hostVMArgs = new HostVMArguments(ctx.getArguments());
         VmRef vm = hostVMArgs.getVM();
+
+        long sinceTimestamp;
+
+        String sinceArg = ctx.getArguments().getArgument("since");
+        try {
+            sinceTimestamp = new SinceTimestampParser(sinceArg, currentTime, defaultSinceTime).parse();
+        } catch (SinceTimestampParser.InvalidSinceTimestampFormatException e) {
+            throw new CommandLineArgumentParseException(translator.localize(LocaleResources.VM_STAT_INVALID_SINCE_ARGUMENT));
+        }
+
         // Pass a copy of the delegates list to the printer
-        final VMStatPrinter statPrinter = new VMStatPrinter(vm, new ArrayList<>(delegates), ctx.getConsole().getOutput());
+        final VMStatPrinter statPrinter = new VMStatPrinter(vm, new ArrayList<>(delegates), ctx.getConsole().getOutput(), sinceTimestamp);
         statPrinter.printStats();
+
         boolean continuous = ctx.getArguments().hasArgument("continuous");
         if (continuous) {
             startContinuousStats(ctx, statPrinter);
@@ -115,7 +132,6 @@
         timer.setSchedulingType(Timer.SchedulingType.FIXED_RATE);
         timer.setTimeUnit(TimeUnit.SECONDS);
         timer.setAction(new Runnable() {
-
             @Override
             public void run() {
                 statPrinter.printUpdatedStats();
@@ -140,7 +156,7 @@
         } catch (InterruptedException e) {
             // Return immediately.
         }
-        
+
         context.ungetService(ref);
     }
 
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Tue Nov 04 17:03:43 2014 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Wed Nov 05 10:40:33 2014 -0500
@@ -71,20 +71,20 @@
     private int numCols;
     private Map<VMStatPrintDelegate, DelegateInfo> delegateInfo;
 
-    VMStatPrinter(VmRef vm, List<VMStatPrintDelegate> delegates, PrintStream out) {
+    VMStatPrinter(VmRef vm, List<VMStatPrintDelegate> delegates, PrintStream out, long sinceTimestamp) {
         this.vm = vm;
         this.delegates = delegates;
         this.out = out;
         int numDelegates = delegates.size();
         this.delegateInfo = new HashMap<>();
         this.correlator = new TimeStampedPojoCorrelator(numDelegates);
-        
+
         // Sort the delegates list
         Collections.sort(delegates, new OrderedComparator<>());
-        
+
         for (VMStatPrintDelegate delegate : delegates) {
             DelegateInfo info = new DelegateInfo();
-            info.lastTimeStamp = Long.MIN_VALUE;
+            info.lastTimeStamp = sinceTimestamp;
             delegateInfo.put(delegate, info);
         }
     }
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Tue Nov 04 17:03:43 2014 -0500
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Wed Nov 05 10:40:33 2014 -0500
@@ -64,3 +64,5 @@
 PURGE_EXPENSIVE_OPERATION_PROMPT = Are you sure you want to continue (Y/y/N/n)?
 PURGE_CANCELLED_MESSAGE = Not cleaning Thermostat data at this time.
 AFFIRMATIVE_RESPONSES = y|yes
+VM_STAT_INVALID_SINCE_ARGUMENT = Invalid arguments for -since : Expected format time:timeunit : e.g. 5:seconds. Time must be positive. \
+  Timeunit must be days, hours, minutes or seconds.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/SinceTimestampParserTest.java	Wed Nov 05 10:40:33 2014 -0500
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class SinceTimestampParserTest {
+
+    private SinceTimestampParser parser;
+    private long startTimestamp;
+
+    @Before
+    public void setup() {
+        startTimestamp = System.currentTimeMillis();
+    }
+
+    @Test
+    public void testAllInput() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("all");
+        long result = parser.parse();
+
+        assertEquals(Long.MIN_VALUE, result);
+    }
+
+    @Test
+    public void testDefaultStamp() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        long defaultTimestamp = 0l;
+
+        parser = new SinceTimestampParser(null, startTimestamp, defaultTimestamp);
+        long result = parser.parse();
+
+        assertEquals(defaultTimestamp, result);
+    }
+
+    @Test
+    public void testSecondsTimestamp() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("1:seconds");
+        long result = parser.parse();
+
+        assertEquals(startTimestamp - TimeUnit.SECONDS.toMillis(1), result);
+    }
+
+    @Test
+    public void testMinutesTimestamp() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("2:minutes");
+        long result = parser.parse();
+        assertEquals(startTimestamp - TimeUnit.MINUTES.toMillis(2), result);
+    }
+
+    @Test
+    public void testHoursTimestamp() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("3:hours");
+        long result = parser.parse();
+        assertEquals(startTimestamp - TimeUnit.HOURS.toMillis(3), result);
+    }
+
+    @Test
+    public void testDaysTimestamp() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("4:days");
+        long result = parser.parse();
+        assertEquals(startTimestamp - TimeUnit.DAYS.toMillis(4), result);
+    }
+
+    @Test (expected = SinceTimestampParser.InvalidSinceTimestampFormatException.class)
+    public void testIncorrectTime() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("0:minutes");
+        parser.parse();
+    }
+
+    @Test (expected = SinceTimestampParser.InvalidSinceTimestampFormatException.class)
+    public void testNotAcceptedUnit() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("5:milliseconds");
+        parser.parse();
+    }
+
+    @Test (expected = SinceTimestampParser.InvalidSinceTimestampFormatException.class)
+    public void testIncorrectArgument() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("aString");
+        parser.parse();
+    }
+
+    @Test (expected = SinceTimestampParser.InvalidSinceTimestampFormatException.class)
+    public void testIncorrectArgumentTwo() throws SinceTimestampParser.InvalidSinceTimestampFormatException {
+        parser = setupDefaultParser("5:seconds:hello");
+        parser.parse();
+    }
+
+    public SinceTimestampParser setupDefaultParser(String sinceTimestamp) {
+        return new SinceTimestampParser(sinceTimestamp, startTimestamp, 0l);
+    }
+
+}
\ No newline at end of file
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Tue Nov 04 17:03:43 2014 -0500
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Wed Nov 05 10:40:33 2014 -0500
@@ -38,7 +38,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
@@ -54,13 +53,10 @@
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.redhat.thermostat.client.cli.VMStatPrintDelegate;
@@ -68,7 +64,6 @@
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.SimpleArguments;
-import com.redhat.thermostat.storage.core.HostRef;
 import com.redhat.thermostat.storage.core.VmRef;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
 import com.redhat.thermostat.test.TestCommandContextFactory;
@@ -165,26 +160,20 @@
         
         VMStatCommand cmd = new VMStatCommand(context);
         
-        SimpleArguments args = new SimpleArguments();
-        args.addArgument("vmId", "234");
-        args.addArgument("hostId", "123");
-        cmd.run(cmdCtxFactory.createContext(args));
+        cmd.run(cmdCtxFactory.createContext(setupArguments()));
         String expected = "TIME        FIRST SECOND THIRD FOURTH FIFTH\n"
                 + "12:00:00 AM 1     2      3     4      5\n"
                 + "12:00:01 AM 6     7      8     9      10\n"
                 + "12:00:02 AM 11    12     13    14     15\n";
         assertEquals(expected, cmdCtxFactory.getOutput());
     }
-    
+
     @Test
     public void testNoDelegates() throws CommandException {
         StubBundleContext context = new StubBundleContext();
         VMStatCommand cmd = new VMStatCommand(context);
         
-        SimpleArguments args = new SimpleArguments();
-        args.addArgument("vmId", "234");
-        args.addArgument("hostId", "123");
-        cmd.run(cmdCtxFactory.createContext(args));
+        cmd.run(cmdCtxFactory.createContext(setupArguments()));
         String expected = "TIME\n";
         assertEquals(expected, cmdCtxFactory.getOutput());
     }
@@ -216,9 +205,7 @@
         
         Thread t = new Thread() {
             public void run() {
-                SimpleArguments args = new SimpleArguments();
-                args.addArgument("vmId", "234");
-                args.addArgument("hostId", "123");
+                SimpleArguments args = setupArguments();
                 args.addArgument("continuous", "true");
                 try {
                     cmd.run(cmdCtxFactory.createContext(args));
@@ -275,10 +262,7 @@
         
         VMStatCommand cmd = new VMStatCommand(context);
         
-        SimpleArguments args = new SimpleArguments();
-        args.addArgument("vmId", "234");
-        args.addArgument("hostId", "123");
-        cmd.run(cmdCtxFactory.createContext(args));
+        cmd.run(cmdCtxFactory.createContext(setupArguments()));
         String expected = "TIME        FIRST SECOND THIRD FOURTH FIFTH\n"
                 + "12:00:00 AM 1     2      3     4      5\n"
                 + "12:00:01 AM 6     7      8     9      10\n"
@@ -301,10 +285,7 @@
         
         VMStatCommand cmd = new VMStatCommand(context);
         
-        SimpleArguments args = new SimpleArguments();
-        args.addArgument("vmId", "234");
-        args.addArgument("hostId", "123");
-        cmd.run(cmdCtxFactory.createContext(args));
+        cmd.run(cmdCtxFactory.createContext(setupArguments()));
         String expected = "TIME        FIRST SECOND THIRD FOURTH FIFTH\n"
                 + "12:00:00 AM 1     2      3     4      5\n"
                 + "12:00:01 AM 6     7      8     9      10\n"
@@ -331,10 +312,7 @@
         
         VMStatCommand cmd = new VMStatCommand(context);
         
-        SimpleArguments args = new SimpleArguments();
-        args.addArgument("vmId", "234");
-        args.addArgument("hostId", "123");
-        cmd.run(cmdCtxFactory.createContext(args));
+        cmd.run(cmdCtxFactory.createContext(setupArguments()));
         String expected = "TIME        FIRST SECOND THIRD BAD FOURTH FIFTH\n"
                 + "12:00:00 AM 1     2      3         4      5\n"
                 + "12:00:01 AM 6     7      8     0   9      10\n"
@@ -348,5 +326,13 @@
         VMStatCommand cmd = new VMStatCommand(context);
         assertTrue(cmd.isStorageRequired());
     }
+
+    private SimpleArguments setupArguments() {
+        SimpleArguments args = new SimpleArguments();
+        args.addArgument("vmId", "234");
+        args.addArgument("hostId", "123");
+        args.addArgument("since", "all");
+        return args;
+    }
 }
 
--- a/distribution/config/commands/vm-stat.properties	Tue Nov 04 17:03:43 2014 -0500
+++ b/distribution/config/commands/vm-stat.properties	Wed Nov 05 10:40:33 2014 -0500
@@ -12,28 +12,35 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}
 
-description = show various statistics about a VM
+description = Show various statistics about a VM.
 
-usage = vm-stat -hostId <host> --vmId <vm> [-d <url>] [-l <level>]
+usage = vm-stat -hostId <host> --vmId <vm> [-c] [-s <time:timeunit>] [-d <url>] [-l <level>]
 
-options = hostId, vmId, continuous, AUTO_DB_OPTIONS, AUTO_LOG_OPTION
+options = hostId, vmId, continuous, since, AUTO_DB_OPTIONS, AUTO_LOG_OPTION
 
 hostId.short = a
 hostId.long = hostId
 hostId.hasarg = true
 hostId.required = true
-hostId.description = the ID of the host to monitor
+hostId.description = The ID of the host to monitor
 
 vmId.short = v
 vmId.long = vmId
 vmId.hasarg = true
 vmId.required = true
-vmId.description = the ID of the VM to monitor
+vmId.description = The ID of the VM to monitor
 
 continuous.short = c
 continuous.long = continuous
 continuous.hasarg = false
 continuous.required = false
-continuous.description = print data continuously
+continuous.description = Print data continuously
+
+since.short = s
+since.long = since
+since.hasarg = true
+since.required = false
+since.description = Print data since [-s time:timeunits] ago or print all data [-s all]. Defaults to since 10 minutes ago [-s 10:minutes]. Accepts positive times \
+  and days, hours, minutes, or seconds.
 
 environments = cli, shell