Mercurial > hg > release > thermostat-0.4
changeset 338:eae8d583ebd4
Show time and memory stats in vm-stat command.
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-May/001598.html
PR 1000
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/DisplayableValues.java Tue May 29 12:19:43 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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.client.ui; - -public class DisplayableValues { - - private static final long BYTES_IN_KB = 1024; - private static final long BYTES_IN_MB = 1024*BYTES_IN_KB; - private static final long BYTES_IN_GB = 1024*BYTES_IN_MB; - private static final long BYTES_IN_TB = 1024*BYTES_IN_GB; - - private static final String BYTES_UNIT = "B"; - private static final String KBYTES_UNIT = "KiB"; - private static final String MBYTES_UNIT = "MiB"; - private static final String GBYTES_UNIT = "GiB"; - private static final String TBYTES_UNIT = "TiB"; - - private static final String DOUBLE_FORMAT_STRING = "%.1f"; - - private DisplayableValues() {} // Not to be instantiated. - - public static String[] bytes(final long bytes) { - if (bytes < BYTES_IN_KB) { - return new String[] { String.valueOf(bytes), BYTES_UNIT }; - } else if (bytes < BYTES_IN_MB) { - return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_KB), KBYTES_UNIT }; - } else if (bytes < BYTES_IN_GB) { - return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_MB), MBYTES_UNIT }; - } else if (bytes < BYTES_IN_TB) { - return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_GB), GBYTES_UNIT }; - } else { - return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_TB), TBYTES_UNIT }; - } - } -}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/VmMemoryController.java Tue May 29 12:19:43 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/VmMemoryController.java Wed May 30 20:14:55 2012 +0200 @@ -56,6 +56,7 @@ import com.redhat.thermostat.common.model.VmMemoryStat; import com.redhat.thermostat.common.model.VmMemoryStat.Generation; import com.redhat.thermostat.common.model.VmMemoryStat.Space; +import com.redhat.thermostat.common.utils.DisplayableValues; class VmMemoryController {
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/DisplayableValuesTest.java Tue May 29 12:19:43 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +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.client.ui; - -import static org.junit.Assert.assertEquals; - -import java.util.Locale; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -public class DisplayableValuesTest { - - private static Locale defaultLocale; - - @BeforeClass - public static void setUp() { - defaultLocale = Locale.getDefault(); - Locale.setDefault(Locale.US); - } - - @AfterClass - public static void tearDown() { - Locale.setDefault(defaultLocale); - } - - @Test - public void testBytes() { - testBytesOutput("1", "B", DisplayableValues.bytes(1)); - testBytesOutput("1023", "B", DisplayableValues.bytes(1023)); - testBytesOutput("1.0", "KiB", DisplayableValues.bytes(1024)); - testBytesOutput("1024.0", "KiB", DisplayableValues.bytes(1_048_575)); - testBytesOutput("1.0", "MiB", DisplayableValues.bytes(1_048_576)); - testBytesOutput("10.0", "MiB", DisplayableValues.bytes(10_480_000)); - testBytesOutput("42.0", "MiB", DisplayableValues.bytes(44_040_000)); - testBytesOutput("99.9", "MiB", DisplayableValues.bytes(104_752_742)); - testBytesOutput("100.0", "MiB", DisplayableValues.bytes(104_857_600)); - testBytesOutput("500.0", "MiB", DisplayableValues.bytes(524_288_000)); - testBytesOutput("900.0", "MiB", DisplayableValues.bytes(943_718_400)); - testBytesOutput("999.9", "MiB", DisplayableValues.bytes(1_048_471_000)); - testBytesOutput("1.0", "GiB", DisplayableValues.bytes(1_073_741_824)); - testBytesOutput("1.1", "GiB", DisplayableValues.bytes(1_181_116_000)); - testBytesOutput("9.9", "GiB", DisplayableValues.bytes(10_630_044_000l)); - testBytesOutput("99.9", "GiB", DisplayableValues.bytes(107_266_808_000l)); - testBytesOutput("1.0", "TiB", DisplayableValues.bytes(1_099_511_627_776l)); - } - - private void testBytesOutput(String number, String units, String[] output) { - assertEquals(2, output.length); - assertEquals(number, output[0]); - assertEquals(units, output[1]); - } -}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAO.java Tue May 29 12:19:43 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAO.java Wed May 30 20:14:55 2012 +0200 @@ -36,6 +36,8 @@ package com.redhat.thermostat.common.dao; +import java.util.List; + import com.redhat.thermostat.common.model.VmMemoryStat; import com.redhat.thermostat.common.storage.Category; import com.redhat.thermostat.common.storage.Key; @@ -92,6 +94,8 @@ public VmMemoryStat getLatestMemoryStat(VmRef ref); + public List<VmMemoryStat> getLatestVmMemoryStats(VmRef vm); + public void putVmMemoryStat(VmMemoryStat stat); }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java Tue May 29 12:19:43 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java Wed May 30 20:14:55 2012 +0200 @@ -36,6 +36,11 @@ package com.redhat.thermostat.common.dao; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.redhat.thermostat.common.model.VmCpuStat; import com.redhat.thermostat.common.model.VmMemoryStat; import com.redhat.thermostat.common.storage.Chunk; import com.redhat.thermostat.common.storage.Cursor; @@ -47,6 +52,8 @@ private final Storage storage; private final VmMemoryStatConverter converter; + private Map<VmRef, VmLatestPojoListGetter<VmMemoryStat>> getters = new HashMap<>(); + VmMemoryStatDAOImpl(Storage storage) { this.storage = storage; converter = new VmMemoryStatConverter(); @@ -69,4 +76,13 @@ storage.putChunk(converter.toChunk(stat)); } + @Override + public List<VmMemoryStat> getLatestVmMemoryStats(VmRef ref) { + VmLatestPojoListGetter<VmMemoryStat> getter = getters.get(ref); + if (getter == null) { + getter = new VmLatestPojoListGetter<VmMemoryStat>(storage, vmMemoryStatsCategory, converter, ref); + getters.put(ref, getter); + } + return getter.getLatest(); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/model/TimeStampedPojoCorrelator.java Wed May 30 20:14:55 2012 +0200 @@ -0,0 +1,161 @@ +/* + * 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.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +public class TimeStampedPojoCorrelator { + + public static class Correlation { + + private long timestamp; + private TimeStampedPojo[] correlation; + + private Correlation(long timestamp, TimeStampedPojo[] correlation) { + this.timestamp = timestamp; + this.correlation = correlation; + } + + public TimeStampedPojo get(int i) { + return correlation[i]; + } + + public long getTimeStamp() { + return timestamp; + } + + } + + private static class TimeStampedPojoComparator implements Comparator<TimeStampedPojo> { + + @Override + public int compare(TimeStampedPojo o1, TimeStampedPojo o2) { + return Long.compare(o1.getTimeStamp(), o2.getTimeStamp()); + } + + + } + + private class Correlator implements Iterator<Correlation> { + + private TimeStampedPojo[] current; + private Correlation last; + private List<Iterator<TimeStampedPojo>> seriesIterators; + + private Correlator() { + current = new TimeStampedPojo[numSeries]; + seriesIterators = new ArrayList<>(); + int index = 0; + for (List<TimeStampedPojo> series : seriesList) { + Iterator<TimeStampedPojo> seriesIterator = series.iterator(); + seriesIterators.add(seriesIterator); + if (seriesIterator.hasNext()) { + current[index] = seriesIterator.next(); + } + index++; + } + + } + + @Override + public boolean hasNext() { + boolean hasNext = false; + for (TimeStampedPojo pojo : current) { + hasNext |= pojo != null; + } + return hasNext; + } + + @Override + public Correlation next() { + long minTimestamp = Long.MAX_VALUE; + for (TimeStampedPojo pojo : current) { + if (pojo != null) { + minTimestamp = Math.min(minTimestamp, pojo.getTimeStamp()); + } + } + TimeStampedPojo[] next = new TimeStampedPojo[numSeries]; + for (int i = 0; i < numSeries; i++) { + if (current[i] != null && current[i].getTimeStamp() == minTimestamp) { + next[i] = current[i]; + Iterator<TimeStampedPojo> iterator = seriesIterators.get(i); + current[i] = iterator.hasNext() ? iterator.next() : null; + } else { + next[i] = last != null ? last.get(i) : null; + } + } + last = new Correlation(minTimestamp, next); + return last; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + private int numSeries; + + private List<List<TimeStampedPojo>> seriesList; + + public TimeStampedPojoCorrelator(int numSeries) { + this.numSeries = numSeries; + seriesList = new ArrayList<>(); + for (int i = 0; i < numSeries; i++) { + seriesList.add(new ArrayList<TimeStampedPojo>()); + } + } + + public void add(int seriesIndex, TimeStampedPojo timeStampedPojo) { + List<? extends TimeStampedPojo> series = seriesList.get(seriesIndex); + int insertIdx = Collections.binarySearch(series, timeStampedPojo, new TimeStampedPojoComparator()); + if (insertIdx < 0) { + insertIdx = -(insertIdx + 1); + } + seriesList.get(seriesIndex).add(insertIdx, timeStampedPojo); + } + + public Iterator<Correlation> iterator() { + return new Correlator(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/utils/DisplayableValues.java Wed May 30 20:14:55 2012 +0200 @@ -0,0 +1,69 @@ +/* + * 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.utils; + +public class DisplayableValues { + + private static final long BYTES_IN_KB = 1024; + private static final long BYTES_IN_MB = 1024*BYTES_IN_KB; + private static final long BYTES_IN_GB = 1024*BYTES_IN_MB; + private static final long BYTES_IN_TB = 1024*BYTES_IN_GB; + + private static final String BYTES_UNIT = "B"; + private static final String KBYTES_UNIT = "KiB"; + private static final String MBYTES_UNIT = "MiB"; + private static final String GBYTES_UNIT = "GiB"; + private static final String TBYTES_UNIT = "TiB"; + + private static final String DOUBLE_FORMAT_STRING = "%.1f"; + + private DisplayableValues() {} // Not to be instantiated. + + public static String[] bytes(final long bytes) { + if (bytes < BYTES_IN_KB) { + return new String[] { String.valueOf(bytes), BYTES_UNIT }; + } else if (bytes < BYTES_IN_MB) { + return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_KB), KBYTES_UNIT }; + } else if (bytes < BYTES_IN_GB) { + return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_MB), MBYTES_UNIT }; + } else if (bytes < BYTES_IN_TB) { + return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_GB), GBYTES_UNIT }; + } else { + return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_TB), TBYTES_UNIT }; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/test/java/com/redhat/thermostat/common/model/TimeStampedPojoCorrelatorTest.java Wed May 30 20:14:55 2012 +0200 @@ -0,0 +1,203 @@ +/* + * 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.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Iterator; + +import org.junit.Test; + +import com.redhat.thermostat.common.model.TimeStampedPojoCorrelator.Correlation; + +public class TimeStampedPojoCorrelatorTest { + + private static class TestTimeStampedPojo implements TimeStampedPojo { + + private long timestamp; + + private TestTimeStampedPojo(long timestamp) { + this.timestamp = timestamp; + } + + @Override + public long getTimeStamp() { + return timestamp; + } + + } + + @Test + public void testOneSeries() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(1); + correlator.add(0, new TestTimeStampedPojo(3)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(0, new TestTimeStampedPojo(2)); + + Iterator<Correlation> i = correlator.iterator(); + + assertTrue(i.hasNext()); + Correlation correlation1 = i.next(); + assertEquals(1, correlation1.get(0).getTimeStamp()); + assertEquals(1, correlation1.getTimeStamp()); + assertTrue(i.hasNext()); + Correlation correlation2 = i.next(); + assertEquals(2, correlation2.get(0).getTimeStamp()); + assertEquals(2, correlation2.getTimeStamp()); + assertTrue(i.hasNext()); + Correlation correlation3 = i.next(); + assertEquals(3, correlation3.get(0).getTimeStamp()); + assertEquals(3, correlation3.getTimeStamp()); + assertFalse(i.hasNext()); + } + + @Test(expected=UnsupportedOperationException.class) + public void testOneSeriesRemove() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(1); + correlator.add(0, new TestTimeStampedPojo(3)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(0, new TestTimeStampedPojo(2)); + + Iterator<Correlation> i = correlator.iterator(); + i.next(); + i.remove(); + } + + @Test + public void test3SeriesInterleaving() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(3); + correlator.add(0, new TestTimeStampedPojo(9)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(0, new TestTimeStampedPojo(4)); + correlator.add(1, new TestTimeStampedPojo(8)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(1, new TestTimeStampedPojo(5)); + correlator.add(2, new TestTimeStampedPojo(7)); + correlator.add(2, new TestTimeStampedPojo(3)); + correlator.add(2, new TestTimeStampedPojo(6)); + + Iterator<Correlation> i = correlator.iterator(); + assertNextCorrelation(i, 1, 1l, null, null); + assertNextCorrelation(i, 2, 1l, 2l, null); + assertNextCorrelation(i, 3, 1l, 2l, 3l); + assertNextCorrelation(i, 4, 4l, 2l, 3l); + assertNextCorrelation(i, 5, 4l, 5l, 3l); + assertNextCorrelation(i, 6, 4l, 5l, 6l); + assertNextCorrelation(i, 7, 4l, 5l, 7l); + assertNextCorrelation(i, 8, 4l, 8l, 7l); + assertNextCorrelation(i, 9, 9l, 8l, 7l); + assertFalse(i.hasNext()); + } + + @Test + public void test3SeriesColliding() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(3); + correlator.add(0, new TestTimeStampedPojo(3)); + correlator.add(0, new TestTimeStampedPojo(2)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(1, new TestTimeStampedPojo(1)); + correlator.add(1, new TestTimeStampedPojo(3)); + correlator.add(2, new TestTimeStampedPojo(1)); + correlator.add(2, new TestTimeStampedPojo(3)); + correlator.add(2, new TestTimeStampedPojo(2)); + + Iterator<Correlation> i = correlator.iterator(); + assertNextCorrelation(i, 1, 1l, 1l, 1l); + assertNextCorrelation(i, 2, 2l, 2l, 2l); + assertNextCorrelation(i, 3, 3l, 3l, 3l); + assertFalse(i.hasNext()); + } + + @Test + public void test3SeriesMissing1() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(3); + correlator.add(0, new TestTimeStampedPojo(3)); + correlator.add(0, new TestTimeStampedPojo(2)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(1, new TestTimeStampedPojo(1)); + correlator.add(1, new TestTimeStampedPojo(3)); + + Iterator<Correlation> i = correlator.iterator(); + assertNextCorrelation(i, 1, 1l, 1l, null); + assertNextCorrelation(i, 2, 2l, 2l, null); + assertNextCorrelation(i, 3, 3l, 3l, null); + assertFalse(i.hasNext()); + } + + @Test + public void test3SeriesEquals() { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(3); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(0, new TestTimeStampedPojo(1)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(1, new TestTimeStampedPojo(2)); + correlator.add(2, new TestTimeStampedPojo(3)); + correlator.add(2, new TestTimeStampedPojo(3)); + correlator.add(2, new TestTimeStampedPojo(3)); + + Iterator<Correlation> i = correlator.iterator(); + assertNextCorrelation(i, 1, 1l, null, null); + assertNextCorrelation(i, 1, 1l, null, null); + assertNextCorrelation(i, 1, 1l, null, null); + assertNextCorrelation(i, 2, 1l, 2l, null); + assertNextCorrelation(i, 2, 1l, 2l, null); + assertNextCorrelation(i, 2, 1l, 2l, null); + assertNextCorrelation(i, 3, 1l, 2l, 3l); + assertNextCorrelation(i, 3, 1l, 2l, 3l); + assertNextCorrelation(i, 3, 1l, 2l, 3l); + assertFalse(i.hasNext()); + } + + private void assertNextCorrelation(Iterator<Correlation> iter, long timestamp, Long... timestamps) { + assertTrue(iter.hasNext()); + Correlation correlation = iter.next(); + assertEquals(timestamp, correlation.getTimeStamp()); + for (int i = 0 ; i < timestamps.length; i++) { + if (timestamps[i] == null) { + assertEquals(null, correlation.get(i)); + } else { + assertEquals(timestamps[i].longValue(), correlation.get(i).getTimeStamp()); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/test/java/com/redhat/thermostat/common/utils/DisplayableValuesTest.java Wed May 30 20:14:55 2012 +0200 @@ -0,0 +1,88 @@ +/* + * 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.utils; + +import static org.junit.Assert.assertEquals; + +import java.util.Locale; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class DisplayableValuesTest { + + private static Locale defaultLocale; + + @BeforeClass + public static void setUp() { + defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + } + + @AfterClass + public static void tearDown() { + Locale.setDefault(defaultLocale); + } + + @Test + public void testBytes() { + testBytesOutput("1", "B", DisplayableValues.bytes(1)); + testBytesOutput("1023", "B", DisplayableValues.bytes(1023)); + testBytesOutput("1.0", "KiB", DisplayableValues.bytes(1024)); + testBytesOutput("1024.0", "KiB", DisplayableValues.bytes(1_048_575)); + testBytesOutput("1.0", "MiB", DisplayableValues.bytes(1_048_576)); + testBytesOutput("10.0", "MiB", DisplayableValues.bytes(10_480_000)); + testBytesOutput("42.0", "MiB", DisplayableValues.bytes(44_040_000)); + testBytesOutput("99.9", "MiB", DisplayableValues.bytes(104_752_742)); + testBytesOutput("100.0", "MiB", DisplayableValues.bytes(104_857_600)); + testBytesOutput("500.0", "MiB", DisplayableValues.bytes(524_288_000)); + testBytesOutput("900.0", "MiB", DisplayableValues.bytes(943_718_400)); + testBytesOutput("999.9", "MiB", DisplayableValues.bytes(1_048_471_000)); + testBytesOutput("1.0", "GiB", DisplayableValues.bytes(1_073_741_824)); + testBytesOutput("1.1", "GiB", DisplayableValues.bytes(1_181_116_000)); + testBytesOutput("9.9", "GiB", DisplayableValues.bytes(10_630_044_000l)); + testBytesOutput("99.9", "GiB", DisplayableValues.bytes(107_266_808_000l)); + testBytesOutput("1.0", "TiB", DisplayableValues.bytes(1_099_511_627_776l)); + } + + private void testBytesOutput(String number, String units, String[] output) { + assertEquals(2, output.length); + assertEquals(number, output[0]); + assertEquals(units, output[1]); + } +}
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/VMStatCommand.java Tue May 29 12:19:43 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMStatCommand.java Wed May 30 20:14:55 2012 +0200 @@ -37,8 +37,12 @@ package com.redhat.thermostat.tools.cli; import java.io.PrintStream; +import java.text.DateFormat; import java.text.DecimalFormat; +import java.util.Arrays; import java.util.Collection; +import java.util.Date; +import java.util.Iterator; import java.util.List; import com.redhat.thermostat.common.appctx.ApplicationContext; @@ -48,8 +52,12 @@ import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.dao.DAOFactory; import com.redhat.thermostat.common.dao.VmCpuStatDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.model.TimeStampedPojoCorrelator; import com.redhat.thermostat.common.model.VmCpuStat; +import com.redhat.thermostat.common.model.VmMemoryStat; +import com.redhat.thermostat.common.utils.DisplayableValues; public class VMStatCommand implements Command { @@ -57,25 +65,118 @@ private static final String CMD_DESCRIPTION = "show various statistics about a VM"; private static final String CPU_PERCENT = "%CPU"; + private static final String MEM_PREFIX = "MEM."; + private static final String TIME = "TIME"; @Override public void run(CommandContext ctx) throws CommandException { DAOFactory daoFactory = ApplicationContext.getInstance().getDAOFactory(); VmCpuStatDAO vmCpuStatDAO = daoFactory.getVmCpuStatDAO(); + VmMemoryStatDAO vmMemoryStatDAO = daoFactory.getVmMemoryStatDAO(); + HostVMArguments hostVMArgs = new HostVMArguments(ctx.getArguments()); VmRef vm = hostVMArgs.getVM(); List<VmCpuStat> cpuStats = vmCpuStatDAO.getLatestVmCpuStats(vm); - printStats(ctx.getConsole().getOutput(), cpuStats); + List<VmMemoryStat> memStats = vmMemoryStatDAO.getLatestVmMemoryStats(vm); + printStats(ctx.getConsole().getOutput(), cpuStats, memStats); + } + + private void printStats(PrintStream out, List<VmCpuStat> cpuStats, List<VmMemoryStat> memStats) { + TimeStampedPojoCorrelator correlator = correlate(cpuStats, memStats); + int numSpaces = getNumSpaces(memStats); + int numColumns = numSpaces + 2; + TableRenderer table = new TableRenderer(numColumns); + printHeaders(memStats, numSpaces, numColumns, table); + Iterator<TimeStampedPojoCorrelator.Correlation> i = correlator.iterator(); + while (i.hasNext()) { + printStats(numSpaces, table, i); + } + table.render(out); + } + + private void printStats(int numSpaces, TableRenderer table, Iterator<TimeStampedPojoCorrelator.Correlation> i) { + + TimeStampedPojoCorrelator.Correlation correlation = i.next(); + + VmCpuStat cpuStat = (VmCpuStat) correlation.get(0); + DecimalFormat format = new DecimalFormat("#0.0"); + String cpuLoad = cpuStat != null ? format.format(cpuStat.getCpuLoad()) : ""; + + DateFormat dateFormat = DateFormat.getTimeInstance(); + String time = dateFormat.format(new Date(correlation.getTimeStamp())); + + String[] memoryUsage = getMemoryUsage((VmMemoryStat) correlation.get(1), numSpaces); + + String[] line = new String[numSpaces + 2]; + System.arraycopy(memoryUsage, 0, line, 2, numSpaces); + line[0] = time; + line[1] = cpuLoad; + table.printLine(line); + } + + private void printHeaders(List<VmMemoryStat> memStats, int numSpaces, int numColumns, TableRenderer table) { + String[] spacesNames = getSpacesNames(memStats, numSpaces); + String[] headers = new String[numColumns]; + headers[0] = TIME; + headers[1] = CPU_PERCENT; + System.arraycopy(spacesNames, 0, headers, 2, numSpaces); + table.printLine(headers); } - private void printStats(PrintStream out, List<VmCpuStat> cpuStats) { - TableRenderer table = new TableRenderer(1); - table.printLine(CPU_PERCENT); - for (VmCpuStat cpuStat : cpuStats) { - DecimalFormat format = new DecimalFormat("#0.0"); - table.printLine(format.format(cpuStat.getCpuLoad())); + private String[] getMemoryUsage(VmMemoryStat vmMemoryStat, int numSpaces) { + String[] memoryUsage = new String[numSpaces]; + if (vmMemoryStat == null) { + Arrays.fill(memoryUsage, ""); + return memoryUsage; + } + int i = 0; + for (VmMemoryStat.Generation gen : vmMemoryStat.getGenerations()) { + for (VmMemoryStat.Space space : gen.spaces) { + String[] displayableSize = DisplayableValues.bytes(space.used); + memoryUsage[i] = displayableSize[0] + " " + displayableSize[1]; + i++; + } + } + return memoryUsage; + } + + private String[] getSpacesNames(List<VmMemoryStat> memStats, int numSpaces) { + if (numSpaces < 1) { + return new String[0]; } - table.render(out); + String[] spacesNames = new String[numSpaces]; + VmMemoryStat stat = memStats.get(0); + int i = 0; + for (VmMemoryStat.Generation gen : stat.getGenerations()) { + for (VmMemoryStat.Space space : gen.spaces) { + spacesNames[i] = MEM_PREFIX + space.name; + i++; + } + } + return spacesNames; + } + + private int getNumSpaces(List<VmMemoryStat> memStats) { + if (memStats.size() < 1) { + return 0; + } + VmMemoryStat stat = memStats.get(0); + int numSpaces = 0; + for (VmMemoryStat.Generation gen : stat.getGenerations()) { + numSpaces += gen.spaces.size(); + } + return numSpaces; + } + + private TimeStampedPojoCorrelator correlate(List<VmCpuStat> cpuStats, List<VmMemoryStat> memStats) { + TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(2); + for(VmCpuStat cpuStat : cpuStats) { + correlator.add(0, cpuStat); + } + for (VmMemoryStat memStat : memStats) { + correlator.add(1, memStat); + } + return correlator; } @Override
--- a/tools/src/test/java/com/redhat/thermostat/tools/cli/VmStatCommandTest.java Tue May 29 12:19:43 2012 +0200 +++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/VmStatCommandTest.java Wed May 30 20:14:55 2012 +0200 @@ -63,8 +63,12 @@ import com.redhat.thermostat.common.dao.DAOFactory; import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmCpuStatDAO; +import com.redhat.thermostat.common.dao.VmMemoryStatDAO; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.common.model.VmCpuStat; +import com.redhat.thermostat.common.model.VmMemoryStat; +import com.redhat.thermostat.common.model.VmMemoryStat.Generation; +import com.redhat.thermostat.common.model.VmMemoryStat.Space; import com.redhat.thermostat.test.TestCommandContextFactory; public class VmStatCommandTest { @@ -86,6 +90,7 @@ private VmCpuStatDAO vmCpuStatDAO; private AppContextSetup appContextSetup; private TestCommandContextFactory cmdCtxFactory; + private VmMemoryStatDAO vmMemoryStatDAO; @Before public void setUp() { @@ -123,23 +128,90 @@ int vmId = 234; HostRef host = new HostRef("123", "dummy"); VmRef vm = new VmRef(host, 234, "dummy"); - VmCpuStat cpustat1 = new VmCpuStat(123, vmId, 50.123454); - VmCpuStat cpustat2 = new VmCpuStat(123, vmId, 65); - VmCpuStat cpustat3 = new VmCpuStat(123, vmId, 70); - List<VmCpuStat> cpuStats = Arrays.asList(cpustat1, cpustat2, cpustat3); + VmCpuStat cpustat1 = new VmCpuStat(2, vmId, 65); + VmCpuStat cpustat2 = new VmCpuStat(3, vmId, 70); + List<VmCpuStat> cpuStats = Arrays.asList(cpustat1, cpustat2); when(vmCpuStatDAO.getLatestVmCpuStats(vm)).thenReturn(cpuStats); DAOFactory daoFactory = mock(DAOFactory.class); when(daoFactory.getVmCpuStatDAO()).thenReturn(vmCpuStatDAO); ApplicationContext.getInstance().setDAOFactory(daoFactory); + + VmMemoryStat.Space space1_1_1 = newSpace("space1", 123456, 12345, 1, 0); + VmMemoryStat.Space space1_1_2 = newSpace("space2", 123456, 12345, 1, 0); + List<VmMemoryStat.Space> spaces1_1 = Arrays.asList(space1_1_1, space1_1_2); + VmMemoryStat.Generation gen1_1 = newGeneration("gen1", "col1", 123456, 12345, spaces1_1); + + VmMemoryStat.Space space1_2_1 = newSpace("space3", 123456, 12345, 1, 0); + VmMemoryStat.Space space1_2_2 = newSpace("space4", 123456, 12345, 1, 0); + List<VmMemoryStat.Space> spaces1_2 = Arrays.asList(space1_2_1, space1_2_2); + VmMemoryStat.Generation gen1_2 = newGeneration("gen2", "col1", 123456, 12345, spaces1_2); + + List<VmMemoryStat.Generation> gens1 = Arrays.asList(gen1_1, gen1_2); + + VmMemoryStat memStat1 = new VmMemoryStat(1, vmId, gens1); + + VmMemoryStat.Space space2_1_1 = newSpace("space1", 123456, 12345, 2, 0); + VmMemoryStat.Space space2_1_2 = newSpace("space2", 123456, 12345, 2, 0); + List<VmMemoryStat.Space> spaces2_1 = Arrays.asList(space2_1_1, space2_1_2); + VmMemoryStat.Generation gen2_1 = newGeneration("gen1", "col1", 123456, 12345, spaces2_1); + + VmMemoryStat.Space space2_2_1 = newSpace("space3", 123456, 12345, 3, 0); + VmMemoryStat.Space space2_2_2 = newSpace("space4", 123456, 12345, 4, 0); + List<VmMemoryStat.Space> spaces2_2 = Arrays.asList(space2_2_1, space2_2_2); + VmMemoryStat.Generation gen2_2 = newGeneration("gen2", "col1", 123456, 12345, spaces2_2); + + List<VmMemoryStat.Generation> gens2 = Arrays.asList(gen2_1, gen2_2); + + VmMemoryStat memStat2 = new VmMemoryStat(2, vmId, gens2); + + VmMemoryStat.Space space3_1_1 = newSpace("space1", 123456, 12345, 4, 0); + VmMemoryStat.Space space3_1_2 = newSpace("space2", 123456, 12345, 5, 0); + List<VmMemoryStat.Space> spaces3_1 = Arrays.asList(space3_1_1, space3_1_2); + VmMemoryStat.Generation gen3_1 = newGeneration("gen1", "col1", 123456, 12345, spaces3_1); + + VmMemoryStat.Space space3_2_1 = newSpace("space3", 123456, 12345, 6, 0); + VmMemoryStat.Space space3_2_2 = newSpace("space4", 123456, 12345, 7, 0); + List<VmMemoryStat.Space> spaces3_2 = Arrays.asList(space3_2_1, space3_2_2); + VmMemoryStat.Generation gen3_2 = newGeneration("gen2", "col1", 123456, 12345, spaces3_2); + + List<VmMemoryStat.Generation> gens3 = Arrays.asList(gen3_1, gen3_2); + + VmMemoryStat memStat3 = new VmMemoryStat(3, vmId, gens3); + + vmMemoryStatDAO = mock(VmMemoryStatDAO.class); + when(vmMemoryStatDAO.getLatestVmMemoryStats(vm)).thenReturn(Arrays.asList(memStat1, memStat2, memStat3)); + when(daoFactory.getVmMemoryStatDAO()).thenReturn(vmMemoryStatDAO); + } + + private Space newSpace(String name, long maxCapacity, long capacity, long used, int index) { + VmMemoryStat.Space space = new VmMemoryStat.Space(); + space.name = name; + space.maxCapacity = maxCapacity; + space.capacity = capacity; + space.used = used; + space.index = index; + return space; + } + + private Generation newGeneration(String name, String collector, long maxCapacity, long capacity, List<Space> spaces) { + VmMemoryStat.Generation gen = new VmMemoryStat.Generation(); + gen.name = name; + gen.collector = collector; + gen.maxCapacity = capacity; + gen.spaces = spaces; + return gen; } @Test - public void testBasicCPU() throws CommandException { + public void testBasicCPUMemory() throws CommandException { SimpleArguments args = new SimpleArguments(); args.addArgument("vmId", "234"); args.addArgument("hostId", "123"); cmd.run(cmdCtxFactory.createContext(args)); - String expected = "%CPU\n50.1\n65.0\n70.0\n"; + String expected = "TIME %CPU MEM.space1 MEM.space2 MEM.space3 MEM.space4\n" + + "1:00:00 AM 1 B 1 B 1 B 1 B\n" + + "1:00:00 AM 65.0 2 B 2 B 3 B 4 B\n" + + "1:00:00 AM 70.0 4 B 5 B 6 B 7 B\n"; assertEquals(expected, cmdCtxFactory.getOutput()); }