/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) 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 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.tools.rrd.converter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jrobin.core.ArcDef;
import org.jrobin.core.Archive;
import org.jrobin.core.DsDef;
import org.jrobin.core.FetchData;
import org.jrobin.core.Robin;
import org.jrobin.core.RrdBackendFactory;
import org.jrobin.core.RrdDb;
import org.jrobin.core.RrdDef;
import org.jrobin.core.RrdException;
import org.jrobin.core.RrdFileBackendFactory;
import org.jrobin.core.RrdJRobin14FileBackend.LockMode;
import org.jrobin.core.RrdJRobin14FileBackendFactory;
import org.jrobin.core.RrdNioBackendFactory;
import org.jrobin.core.RrdNioByteBufferBackendFactory;
import org.jrobin.core.Sample;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opennms.tools.rrd.converter.LogUtils.Level;
public class JRobinConverterTest {
private static final double ACCEPTABLE_DOUBLE_DELTA = 0.00000000001;
JRobinConverter m_converter = null;
private static final long SECONDS_PER_HOUR = 3600L;
private static final long SECONDS_PER_DAY = 24L * SECONDS_PER_HOUR;
private static final long SECONDS_PER_YEAR = 366L * SECONDS_PER_DAY;
private long m_baseTime = 1298046000L;
File m_workDir = new File("target/rrd");
private final File m_sineFull = new File(m_workDir, "sine.rrd");
private final File m_sineSource = new File(m_workDir, "a.rrd");
private final File m_variation = new File(m_workDir, "variation.rrd");
private final File m_overlapping = new File(m_workDir, "overlapping.rrd");
@BeforeClass
public static void setFactory() throws RrdException {
RrdBackendFactory.setDefaultFactory("MNIO");
LogUtils.setLevel(Level.DEBUG);
}
@Before
public void setUp() throws Exception {
m_converter = new JRobinConverter();
m_workDir.mkdirs();
}
@After
public void tearDown() throws Exception {
m_sineFull.delete();
m_sineSource.delete();
m_variation.delete();
m_overlapping.delete();
}
private void createMockVariationRrds(final RrdBackendFactory factory) throws RrdException, IOException {
final long end = getMidnightInSeconds(m_baseTime);
initializeRrd(m_variation, new String[] { "a", "b" }, new String[] { "1:4032", "12:1488", "288:366" });
initializeRrd(m_overlapping, new String[] { "c", "d", "a" }, new String[] { "1:4032", "12:1488", "288:366" });
final RrdDb variation;
final RrdDb overlapping;
if (factory == null) {
variation = new RrdDb(m_variation);
overlapping = new RrdDb(m_overlapping);
} else {
variation = new RrdDb(m_variation.getAbsolutePath(), factory);
overlapping = new RrdDb(m_overlapping.getAbsolutePath(), factory);
}
final long start = (end - SECONDS_PER_YEAR);
final Function sequence = new AverageSequence(300, 10);
final Function sequenceCounter = new Counter(0, sequence);
final Function overlappingSequence = new AverageSequence(300, 20);
final Function overlappingCounter = new Counter(0, overlappingSequence);
long timestamp = start - 300L;
for (; timestamp <= end; timestamp += 300L) {
final Sample variationSample = variation.createSample(timestamp);
final double variationValue = sequenceCounter.evaluate(timestamp);
variationSample.setValue("a", variationValue);
variationSample.update();
final Sample overlappingSample = overlapping.createSample(timestamp);
final double overlappingValue = overlappingCounter.evaluate(timestamp);
overlappingSample.setValue("d", Double.NaN);
if (((timestamp % 1200) / 300) < 2) {
overlappingSample.setValue("a", overlappingValue);
}
overlappingSample.setValue("c", overlappingValue);
overlappingSample.update();
}
variation.close();
overlapping.close();
}
private void createMockSineRrds(final RrdBackendFactory factory) throws RrdException, IOException {
final long start = (m_baseTime - (SECONDS_PER_DAY * 56L));
initializeRrd(m_sineFull, new String[] { "a", "b" }, new String[] { "1:4032", "12:1488", "288:366" });
initializeRrd(m_sineSource, new String[] { "a" }, new String[] { "1:8928", "12:8784" });
final RrdDb sineFull;
final RrdDb sineSource;
if (factory == null) {
sineFull = new RrdDb(m_sineFull);
sineSource = new RrdDb(m_sineSource);
} else {
sineFull = new RrdDb(m_sineFull.getAbsolutePath(), factory);
sineSource = new RrdDb(m_sineSource.getAbsolutePath(), factory);
}
Function bigSine = new Sin(start, 15, -10, SECONDS_PER_DAY * 7L);
Function smallSine = new Sin(start, 7, 5, SECONDS_PER_DAY * 2L);
Function bigSineCounter = new Counter(0, bigSine);
Function smallSineCounter = new Counter(0, smallSine);
long timestamp = start - 300L;
for(; timestamp <= (m_baseTime - (SECONDS_PER_DAY * 28L)); timestamp += 300L) {
Sample sample = sineSource.createSample(timestamp);
double value = bigSineCounter.evaluate(timestamp);
sample.setValue("a", value);
sample.update();
}
for(; timestamp <= m_baseTime; timestamp += 300L) {
Sample sample = sineFull.createSample(timestamp);
double value = smallSineCounter.evaluate(timestamp);
sample.setValue("a", value);
sample.update();
}
sineFull.close();
sineSource.close();
}
private long getMidnightInSeconds(final long seconds) {
return (((seconds) / SECONDS_PER_DAY) * SECONDS_PER_DAY);
}
private void initializeRrd(final File fileName, final String[] dsNames, final String[] archives) throws RrdException, IOException {
final RrdDef rrdDef = new RrdDef(fileName.getAbsolutePath());
rrdDef.setStartTime(0);
final DsDef[] dsDefs = new DsDef[dsNames.length];
for (int i = 0; i < dsNames.length; i++) {
dsDefs[i] = new DsDef(dsNames[i], "COUNTER", 600, 0, Double.NaN);
}
rrdDef.addDatasource(dsDefs);
final ArcDef[] arcDefs = new ArcDef[archives.length];
for (int i = 0; i < archives.length; i++) {
String[] entry = archives[i].split(":");
Integer steps = Integer.valueOf(entry[0]);
Integer rows = Integer.valueOf(entry[1]);
arcDefs[i] = new ArcDef("AVERAGE", 0.5D, steps, rows);
}
rrdDef.addArchive(arcDefs);
final RrdDb db = new RrdDb(rrdDef);
db.close();
}
@Test
public void testGetDsNames() throws Exception {
createMockSineRrds(null);
final List<String> dsNames = m_converter.getDsNames(m_sineFull);
assertTrue(dsNames.contains("a"));
assertEquals(2, dsNames.size());
}
@Test
public void testGetRras() throws Exception {
createMockSineRrds(null);
final List<String> rras = m_converter.getRras(m_sineFull);
assertEquals(3, rras.size());
}
@Test
public void testBackends() throws Exception {
final RrdBackendFactory[] factories = new RrdBackendFactory[] {
new RrdFileBackendFactory(),
new RrdNioBackendFactory(),
new RrdNioByteBufferBackendFactory(),
new RrdJRobin14FileBackendFactory(LockMode.EXCEPTION_IF_LOCKED),
new RrdJRobin14FileBackendFactory(LockMode.WAIT_IF_LOCKED),
new RrdJRobin14FileBackendFactory(LockMode.NO_LOCKS)
};
for (final RrdBackendFactory factory : factories) {
// LogUtils.infof(this, "starting with backend factory %s", factory);
m_sineFull.delete();
m_sineSource.delete();
long factoryStart = System.nanoTime();
createMockSineRrds(factory);
for (int i = 0; i < 10; i++) {
final File newFile = m_converter.createTempRrd(m_sineFull);
try {
m_converter.consolidateRrdFile(m_sineSource, newFile);
} finally {
newFile.delete();
}
}
long nanos = System.nanoTime() - factoryStart;
LogUtils.infof(this, "factory %s took %f seconds", factory, (nanos / 1000000000D));
}
}
@Test
public void testSine() throws Exception {
createMockSineRrds(null);
final File newFile = m_converter.createTempRrd(m_sineFull);
try {
m_converter.consolidateRrdFile(m_sineSource, newFile);
} finally {
newFile.delete();
}
}
@Test
public void testScanRrds() throws Exception {
final File topDirectory = new File("src/test/rrds");
List<File> rrds = m_converter.findRrds(topDirectory);
assertEquals(14, rrds.size());
assertTrue(rrds.contains(new File("src/test/rrds/90020/Se0/mib2-interfaces.rrd")));
assertTrue(rrds.contains(new File("src/test/rrds/90020/Se0/ifOutOctets.rrd")));
rrds = m_converter.findGroupRrds(topDirectory);
assertEquals(1, rrds.size());
assertTrue(rrds.contains(new File("src/test/rrds/90020/Se0/mib2-interfaces.rrd")));
assertFalse(rrds.contains(new File("src/test/rrds/90020/Se0/ifOutOctets.rrd")));
}
@Test
public void testGetMatchingRrds() throws Exception {
createMockSineRrds(null);
final List<File> matches = m_converter.getMatchingGroupRrds(m_sineFull);
assertEquals(1, matches.size());
}
@Test
public void testFetch() throws Exception {
createMockSineRrds(null);
RrdDb rrd = new RrdDb(m_sineFull);
final long endTime = rrd.getLastArchiveUpdateTime();
final long startTime = endTime - (60L * 60L * 24L * 365L);
final FetchData fd = rrd.createFetchRequest("AVERAGE", startTime, endTime, 300).fetchData();
double[] values = fd.getValues("a");
assertEquals(367, values.length);
}
@Test
public void testRrdArchiveZero() throws Exception {
createMockVariationRrds(null);
RrdDb rrd = new RrdDb(m_variation);
TimeSeriesDataSource archive = new RrdArchive(rrd.getArchive(0), Arrays.asList(rrd.getDsNames()));
final int expectedArchiveSize = 4032;
final int expectedArchiveStep = 300;
final long end = getMidnightInSeconds(m_baseTime);
final long start = (end - ((expectedArchiveSize - 1) * expectedArchiveStep));
assertEquals(end, archive.getEndTime());
assertEquals(start, archive.getStartTime());
assertEquals(expectedArchiveStep, archive.getNativeStep());
assertEquals(expectedArchiveSize, archive.getRows());
assertEquals(0.5833333333333334D, archive.getDataAt(start).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.5866666666666667D, archive.getDataAt(start + 300).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, archive.getDataAt(end).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
List<RrdEntry> entries = archive.getData(expectedArchiveStep);
assertEquals(expectedArchiveSize, entries.size());
assertEquals(start, entries.get(0).getTimestamp());
assertEquals(end, entries.get(expectedArchiveSize - 1).getTimestamp());
assertEquals(0.5833333333333334D, entries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.5866666666666667D, entries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.3833333333333334D, entries.get(expectedArchiveSize - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, entries.get(expectedArchiveSize - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(entries.get(0).getValue("b")));
assertEquals(0.5833333333333334D, archive.getDataAt(start + 150).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.5866666666666667D, archive.getDataAt(start + 450).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, archive.getDataAt(end + 150).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
List<RrdEntry> halfEntries = archive.getData(expectedArchiveStep / 2);
assertEquals(expectedArchiveSize * 2, halfEntries.size());
assertEquals(start, halfEntries.get(0).getTimestamp());
assertEquals(end + (expectedArchiveStep / 2), halfEntries.get((expectedArchiveSize * 2) - 1).getTimestamp());
assertEquals(0.5833333333333334D, halfEntries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.5833333333333334D, halfEntries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, halfEntries.get((expectedArchiveSize * 2) - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, halfEntries.get((expectedArchiveSize * 2) - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(halfEntries.get(0).getValue("b")));
for (int i = 0; i < halfEntries.size(); i++) {
final RrdEntry halfEntry = halfEntries.get(i);
final RrdEntry entry = entries.get(i/2);
assertEquals(halfEntry.getTimestamp(), entry.getTimestamp() + ((i % 2) * (expectedArchiveStep / 2)));
assertEquals(halfEntry.getValue("a"), entry.getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(halfEntry.getValue("b"), entry.getValue("b"), ACCEPTABLE_DOUBLE_DELTA);
}
}
@Test
public void testRrdArchiveOne() throws Exception {
createMockVariationRrds(null);
RrdDb rrd = new RrdDb(m_variation);
TimeSeriesDataSource archive = new RrdArchive(rrd.getArchive(1), Arrays.asList(rrd.getDsNames()));
final int expectedArchiveSize = 1488;
final int expectedArchiveStep = 3600;
final long end = getMidnightInSeconds(m_baseTime);
final long start = (end - ((expectedArchiveSize - 1) * expectedArchiveStep));
assertEquals(end, archive.getEndTime());
assertEquals(start, archive.getStartTime());
assertEquals(expectedArchiveStep, archive.getNativeStep());
List<RrdEntry> entries = archive.getData(expectedArchiveStep);
assertEquals(expectedArchiveSize, entries.size());
assertEquals(start, entries.get(0).getTimestamp());
assertEquals(end, entries.get(expectedArchiveSize - 1).getTimestamp());
assertEquals(0.601111111111111D, entries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.634444444444445D, entries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.334444444444445D, entries.get(expectedArchiveSize - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.301111111111111D, entries.get(expectedArchiveSize - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(entries.get(0).getValue("b")));
List<RrdEntry> halfEntries = archive.getData(expectedArchiveStep / 2);
assertEquals(expectedArchiveSize * 2, halfEntries.size());
assertEquals(start, halfEntries.get(0).getTimestamp());
assertEquals(end + (expectedArchiveStep / 2), halfEntries.get((expectedArchiveSize * 2) - 1).getTimestamp());
assertEquals(0.601111111111111D, halfEntries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.601111111111111D, halfEntries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.301111111111111D, halfEntries.get((expectedArchiveSize * 2) - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.301111111111111D, halfEntries.get((expectedArchiveSize * 2) - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(halfEntries.get(0).getValue("b")));
for (int i = 0; i < halfEntries.size(); i++) {
final RrdEntry halfEntry = halfEntries.get(i);
final RrdEntry entry = entries.get(i/2);
assertEquals(halfEntry.getTimestamp(), entry.getTimestamp() + ((i % 2) * (expectedArchiveStep / 2)));
assertEquals(halfEntry.getValue("a"), entry.getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(halfEntry.getValue("b"), entry.getValue("b"), ACCEPTABLE_DOUBLE_DELTA);
}
}
@Test
public void testRrdArchiveTwo() throws Exception {
createMockVariationRrds(null);
RrdDb rrd = new RrdDb(m_variation);
TimeSeriesDataSource archive = new RrdArchive(rrd.getArchive(2), Arrays.asList(rrd.getDsNames()));
final int expectedArchiveSize = 366;
final int expectedArchiveStep = 86400;
final long end = getMidnightInSeconds(m_baseTime);
final long start = (end - ((expectedArchiveSize - 1) * expectedArchiveStep));
assertEquals(end, archive.getEndTime());
assertEquals(start, archive.getStartTime());
assertEquals(expectedArchiveStep, archive.getNativeStep());
List<RrdEntry> entries = archive.getData(expectedArchiveStep);
assertEquals(expectedArchiveSize, entries.size());
assertEquals(start, entries.get(0).getTimestamp());
assertEquals(end, entries.get(expectedArchiveSize - 1).getTimestamp());
assertEquals(0.981666666666667D, entries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, entries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, entries.get(expectedArchiveSize - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, entries.get(expectedArchiveSize - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(entries.get(0).getValue("b")));
List<RrdEntry> halfEntries = archive.getData(expectedArchiveStep / 2);
assertEquals(expectedArchiveSize * 2, halfEntries.size());
assertEquals(start, halfEntries.get(0).getTimestamp());
assertEquals(end + (expectedArchiveStep / 2), halfEntries.get((expectedArchiveSize * 2) - 1).getTimestamp());
assertEquals(0.981666666666667D, halfEntries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, halfEntries.get(1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, halfEntries.get((expectedArchiveSize * 2) - 2).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, halfEntries.get((expectedArchiveSize * 2) - 1).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertTrue(Double.isNaN(halfEntries.get(0).getValue("b")));
for (int i = 0; i < halfEntries.size(); i++) {
final RrdEntry halfEntry = halfEntries.get(i);
final RrdEntry entry = entries.get(i/2);
assertEquals(halfEntry.getTimestamp(), entry.getTimestamp() + ((i % 2) * (expectedArchiveStep / 2)));
assertEquals(halfEntry.getValue("a"), entry.getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(halfEntry.getValue("b"), entry.getValue("b"), ACCEPTABLE_DOUBLE_DELTA);
}
}
@Test
public void testRrdDatabase() throws Exception {
createMockVariationRrds(null);
RrdDb rrd = new RrdDb(m_variation);
TimeSeriesDataSource rrdDatabase = new RrdDatabase(rrd);
final int largestArchiveStep = 86400;
final long end = getMidnightInSeconds(m_baseTime);
final long start = (end - SECONDS_PER_YEAR + largestArchiveStep);
// ^^^^^^^^^^^^^^^^^^ accounts for the extra step added to each RRA in setUp()
assertEquals(end, rrdDatabase.getEndTime());
assertEquals(start, rrdDatabase.getStartTime());
assertEquals(105120, rrdDatabase.getRows());
assertEquals(0.981666666666667D, rrdDatabase.getDataAt(start).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, rrdDatabase.getDataAt(start + 300).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.601111111111111D, rrdDatabase.getDataAt(start + (300 * 87276)).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.301111111111111D, rrdDatabase.getDataAt(start + (300 * 101088)).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.58D, rrdDatabase.getDataAt(end).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
List<RrdEntry> entries = rrdDatabase.getData(300);
RrdEntry lastEntry = null;
for (int i = 0; i < entries.size(); i++) {
final RrdEntry entry = entries.get(i);
if (lastEntry != null) {
assertEquals(entry.getTimestamp(), lastEntry.getTimestamp() + 300);
}
lastEntry = entry;
}
assertEquals(0.981666666666667D, entries.get(0).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.981666666666667D, entries.get(87275).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.601111111111111D, entries.get(87276).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.301111111111111D, entries.get(101088).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.583333333333333D, entries.get(101089).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
}
@Test
public void testAggregateRrdDatabase() throws Exception {
createMockSineRrds(null);
RrdDb source = new RrdDb(m_sineSource);
RrdDb full = new RrdDb(m_sineFull);
RrdDatabase sourceDatabase = new RrdDatabase(source);
RrdDatabase fullDatabase = new RrdDatabase(full);
List<RrdDatabase> datasources = new ArrayList<RrdDatabase>();
datasources.add(sourceDatabase);
datasources.add(fullDatabase);
TimeSeriesDataSource aggregate = new AggregateTimeSeriesDataSource(datasources);
assertEquals(300, aggregate.getNativeStep());
assertEquals(1264006800, aggregate.getStartTime());
assertEquals(1298046000, aggregate.getEndTime());
assertEquals(113464, aggregate.getRows());
assertEquals(Double.NaN, aggregate.getDataAt(1264006800).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.049168976942D, aggregate.getDataAt(1293210000).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.071266671798D, aggregate.getDataAt(1294181400).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.050000000000D, aggregate.getDataAt(1295626800).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.024241692151D, aggregate.getDataAt(1295629200).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.023515134858D, aggregate.getDataAt(1296836700).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.023333333333D, aggregate.getDataAt(1298046000).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
List<RrdEntry> entries = aggregate.getData(150);
assertEquals(entries.get(entries.size() - 1).getValue("a"), aggregate.getDataAt(aggregate.getEndTime() + 299).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
}
@Test
public void testRrdDatabaseAndAggregateRrdDatabase() throws Exception {
createMockSineRrds(null);
long dbTime = 0;
long aggTime = 0;
for (int i = 0; i < 20; i++) {
RrdDb source = new RrdDb(m_sineSource);
RrdDatabase sourceDatabase = new RrdDatabase(source);
TimeSeriesDataSource aggregate = new AggregateTimeSeriesDataSource(Collections.singletonList(sourceDatabase));
long dbStart = System.nanoTime();
List<RrdEntry> rawEntries = sourceDatabase.getData(150);
dbTime += System.nanoTime() - dbStart;
long aggStart = System.nanoTime();
List<RrdEntry> aggregateEntries = aggregate.getData(150);
aggTime += System.nanoTime() - aggStart;
assertEquals(aggregate.getEndTime() + 150, aggregateEntries.get(aggregateEntries.size() - 1).getTimestamp());
assertEquals(rawEntries.size(), aggregateEntries.size());
}
LogUtils.debugf(this, "dbTime = %d (%f)", dbTime, dbTime / 1000000.0 / 20.0);
LogUtils.debugf(this, "aggTime = %d (%f)", aggTime, aggTime / 1000000.0 / 20.0);
}
@Test
public void testRrdDatabaseAndAggregateRrdDatabaseGetDataAt() throws Exception {
createMockSineRrds(null);
long dbTime = 0;
long aggTime = 0;
for (int i = 0; i < 20; i++) {
RrdDb source = new RrdDb(m_sineSource);
RrdDatabase sourceDatabase = new RrdDatabase(source);
TimeSeriesDataSource aggregate = new AggregateTimeSeriesDataSource(Collections.singletonList(sourceDatabase));
long dbStart = System.nanoTime();
List<RrdEntry> rawEntries = getAllData(sourceDatabase);
dbTime += System.nanoTime() - dbStart;
long aggStart = System.nanoTime();
List<RrdEntry> aggregateEntries = getAllData(aggregate);
aggTime += System.nanoTime() - aggStart;
assertEquals(aggregate.getEndTime() + 150, aggregateEntries.get(aggregateEntries.size() - 1).getTimestamp());
assertEquals(rawEntries.size(), aggregateEntries.size());
}
LogUtils.debugf(this, "dbTime = %d (%f)", dbTime, dbTime / 1000000.0 / 20.0);
LogUtils.debugf(this, "aggTime = %d (%f)", aggTime, aggTime / 1000000.0 / 20.0);
}
@Test
public void testAggregateWithOverlappingData() throws Exception {
createMockVariationRrds(null);
RrdDatabase overlappingDatabase = new RrdDatabase(new RrdDb(m_overlapping, true));
RrdDatabase variationDatabase = new RrdDatabase(new RrdDb(m_variation, true));
List<RrdDatabase> datasources = new ArrayList<RrdDatabase>();
datasources.add(overlappingDatabase);
datasources.add(variationDatabase);
TimeSeriesDataSource aggregate = new AggregateTimeSeriesDataSource(datasources);
final List<String> aggregateDsNames = Arrays.asList(new String[] { "c", "d", "a", "b" });
assertEquals(variationDatabase.getStartTime(), overlappingDatabase.getStartTime());
assertEquals(variationDatabase.getStartTime(), aggregate.getStartTime());
assertEquals(variationDatabase.getEndTime(), overlappingDatabase.getEndTime());
assertEquals(variationDatabase.getEndTime(), aggregate.getEndTime());
assertEquals(aggregateDsNames, aggregate.getDsNames());
assertEquals(1.0933333333333D, aggregate.getDataAt(1297956000).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.1966666666666D, aggregate.getDataAt(1297956300).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.1000000000000D, aggregate.getDataAt(1297956600).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(1.1033333333333D, aggregate.getDataAt(1297956900).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
}
@Test
public void testCombine() throws Exception {
createMockSineRrds(null);
final File newFile = m_converter.createTempRrd(m_sineFull);
try {
m_converter.consolidateRrdFile(m_sineFull, newFile);
RrdDb newRrd = new RrdDb(newFile.getPath(), true);
TimeSeriesDataSource rrdDatabase = new RrdDatabase(newRrd);
assertEquals(m_baseTime, newRrd.getLastArchiveUpdateTime());
Archive archive = newRrd.getArchive(0);
Robin robin = archive.getRobin(newRrd.getDsIndex("a"));
assertEquals(4032, robin.getSize());
assertEquals(Double.NaN, rrdDatabase.getDataAt(1293195600).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.049532528339D, rrdDatabase.getDataAt(1293210000).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
assertEquals(0.023333333333D, rrdDatabase.getDataAt(1298046000).getValue("a"), ACCEPTABLE_DOUBLE_DELTA);
} finally {
newFile.delete();
}
}
protected void checkArchive(final RrdDb rrd, final Integer nanSample, final Integer numberSample, final Double numberValue, final Integer archiveIndex, final Integer numDses, final Integer dsIndex, String dsName) throws RrdException, IOException {
LogUtils.debugf(this, "checking archive %s for consistency", rrd);
final Archive archive = rrd.getArchive(archiveIndex);
final Map<String,Integer> indexes = m_converter.getDsIndexes(rrd);
assertEquals(numDses, Integer.valueOf(indexes.size()));
final Robin robin = archive.getRobin(dsIndex);
if (nanSample == null) {
for (final double value : robin.getValues()) {
assertTrue(!Double.isNaN(value));
}
} else {
assertTrue(Double.isNaN(robin.getValue(nanSample)));
}
assertEquals(numberValue, Double.valueOf(robin.getValue(numberSample)));
// Make sure FetchData matches
final FetchData data = rrd.createFetchRequest("AVERAGE", archive.getStartTime(), archive.getEndTime()).fetchData();
final double[] values = data.getValues(dsName);
if (nanSample == null) {
for (final double value : values) {
assertTrue(!Double.isNaN(value));
}
} else {
assertTrue(Double.isNaN(values[nanSample]));
}
assertEquals(numberValue, Double.valueOf(values[numberSample]));
}
private List<RrdEntry> getAllData(final TimeSeriesDataSource sourceDatabase) throws IOException {
final List<RrdEntry> entries = new ArrayList<RrdEntry>(sourceDatabase.getRows());
for (long time = sourceDatabase.getStartTime(); time < sourceDatabase.getEndTime() + sourceDatabase.getNativeStep(); time += 150) {
entries.add(sourceDatabase.getDataAt(time));
}
return entries;
}
interface Function {
double evaluate(long timestamp);
}
class Sin implements Function {
long m_startTime;
double m_offset;
double m_amplitude;
double m_period;
double m_factor;
Sin(final long startTime, final double offset, final double amplitude, final double period) {
m_startTime = startTime;
m_offset = offset;
m_amplitude = amplitude;
m_period = period;
m_factor = 2 * Math.PI / period;
}
public double evaluate(final long timestamp) {
final long x = timestamp - m_startTime;
return (m_amplitude * Math.sin(m_factor * x)) + m_offset;
}
}
class Cos implements Function {
long m_startTime;
double m_offset;
double m_amplitude;
double m_period;
double m_factor;
Cos(final long startTime, final double offset, final double amplitude, final double period) {
m_startTime = startTime;
m_offset = offset;
m_amplitude = amplitude;
m_period = period;
m_factor = 2 * Math.PI / period;
}
public double evaluate(final long timestamp) {
final long x = timestamp - m_startTime;
return (m_amplitude * Math.cos(m_factor * x)) + m_offset;
}
}
class Times implements Function {
Function m_a;
Function m_b;
Times(final Function a, final Function b) {
m_a = a;
m_b = b;
}
public double evaluate(final long timestamp) {
return m_a.evaluate(timestamp)*m_b.evaluate(timestamp);
}
}
class Counter implements Function {
double m_prevValue;
Function m_function;
Counter(final double initialValue, final Function function) {
m_prevValue = initialValue;
m_function = function;
}
public double evaluate(final long timestamp) {
final double m_diff = m_function.evaluate(timestamp);
m_prevValue += m_diff;
return m_prevValue;
}
}
class AverageSequence implements Function {
private long m_baseline;
private long m_variation;
public AverageSequence(final long baseline, final long variation) {
m_baseline = baseline;
m_variation = variation;
}
public double evaluate(final long timestamp) {
final long i = (timestamp % SECONDS_PER_DAY) / 300;
final long h = i / 12;
final long j = i % 12;
final double result = m_baseline + (m_variation * (h-12)) + (j - 6);
return result;
}
}
}