package org.rhq.server.metrics; import static java.util.Arrays.asList; import static org.rhq.test.AssertUtils.assertCollectionMatchesNoOrder; import static org.testng.Assert.assertEquals; import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import com.datastax.driver.core.ResultSet; import com.google.common.collect.Lists; import org.joda.time.DateTime; import org.testng.annotations.BeforeClass; import org.rhq.cassandra.schema.Table; import org.rhq.server.metrics.aggregation.IndexIterator; import org.rhq.server.metrics.domain.AggregateNumericMetric; import org.rhq.server.metrics.domain.AggregateNumericMetricMapper; import org.rhq.server.metrics.domain.Bucket; import org.rhq.server.metrics.domain.IndexBucket; import org.rhq.server.metrics.domain.IndexEntry; import org.rhq.server.metrics.domain.MetricsTable; import org.rhq.server.metrics.domain.RawNumericMetric; import org.rhq.server.metrics.domain.RawNumericMetricMapper; /** * @author John Sanda */ public class MetricsTest extends CassandraIntegrationTest { private static final double TEST_PRECISION = Math.pow(10, -9); public static final int PARTITION_SIZE = 5; protected MetricsDAO dao; protected MetricsConfiguration configuration = new MetricsConfiguration(); protected DateTimeServiceStub dateTimeServiceStub; private RawNumericMetricMapper rawMapper = new RawNumericMetricMapper(); private AggregateNumericMetricMapper aggregateMapper = new AggregateNumericMetricMapper(); @BeforeClass public void initClass() throws Exception { configuration = createConfiguration(); dao = new MetricsDAO(storageSession, configuration); dateTimeServiceStub = new DateTimeServiceStub(); dateTimeServiceStub.setConfiguration(configuration); dateTimeService = dateTimeServiceStub; } protected MetricsConfiguration createConfiguration() { return new MetricsConfiguration(); } protected DateTime hour(int hourOfDay) { return hour0().plusHours(hourOfDay); } protected DateTime today() { return hour(0); } protected DateTime yesterday() { return hour(0).minusHours(24); } protected DateTime tomorrow() { return hour(0).plusHours(24); } protected double avg(double... values) { double sum = 0; for (double value : values) { sum += value; } return divide(sum, values.length); } protected double divide(double dividend, int divisor) { return new BigDecimal(Double.toString(dividend)).divide(new BigDecimal(Integer.toString(divisor)), MathContext.DECIMAL64).doubleValue(); } protected void purgeDB() { for (Table table : Table.values()) { session.execute("TRUNCATE " + table.getTableName()); } } protected void assertRawDataEquals(int scheduleId, DateTime startTime, DateTime endTime, RawNumericMetric... expected) { assertRawDataEquals(scheduleId, startTime, endTime, asList(expected)); } protected void assertRawDataEmpty(int scheduleId, DateTime startTime, DateTime endTime) { List<RawNumericMetric> emptyRaws = Collections.emptyList(); assertRawDataEquals(scheduleId, startTime, endTime, emptyRaws); } protected void assertRawDataEquals(int scheduleId, DateTime startTime, DateTime endTime, List<RawNumericMetric> expected) { ResultSet resultSet = dao.findRawMetricsAsync(scheduleId, startTime.getMillis(), endTime.getMillis()).get(); List<RawNumericMetric> actual = rawMapper.mapAll(resultSet); assertEquals(actual, expected, "The raw metrics do not match the expected value"); } /** * Verifies that the 1 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert1HourDataEquals(int scheduleId, AggregateNumericMetric... expected) { assert1HourDataEquals(scheduleId, asList(expected)); } /** * Verifies that the 1 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert1HourDataEquals(int scheduleId, Collection<AggregateNumericMetric> expected) { assert1HourDataEquals(scheduleId, new ArrayList<AggregateNumericMetric>(expected)); } /** * Verifies that the 1 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert1HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) { assertMetricDataEquals(Bucket.ONE_HOUR, scheduleId, expected); } /** * Verifies that the 6 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert6HourDataEquals(int scheduleId, AggregateNumericMetric... expected) { assert6HourDataEquals(scheduleId, asList(expected)); } /** * Verifies that the 6 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert6HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) { assertMetricDataEquals(Bucket.SIX_HOUR, scheduleId, expected); } /** * Verifies that the 24 hour data (in the historical table) matches the expected values. * * @param scheduleId The schedule id to query * @param expected The expected values */ protected void assert24HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) { assertMetricDataEquals(Bucket.TWENTY_FOUR_HOUR, scheduleId, expected); } private void assertMetricDataEquals(Bucket bucket, int scheduleId, List<AggregateNumericMetric> expected) { List<AggregateNumericMetric> actual = Lists.newArrayList(findAggregateMetrics(bucket, scheduleId)); assertCollectionMatchesNoOrder("Metric data for schedule id " + scheduleId + " in table " + bucket + " does not match expected values", expected, actual, TEST_PRECISION); } protected void assertMetricDataEquals(int scheduleId, Bucket bucket, AggregateNumericMetric... expected) { assertMetricDataEquals(scheduleId, bucket, asList(expected)); } protected void assertMetricDataEquals(int scheduleId, Bucket bucket, List<AggregateNumericMetric> expected) { ResultSet resultSet = session.execute( "select schedule_id, bucket, time, avg, max, min " + "from " + MetricsTable.AGGREGATE + " " + "where schedule_id = " + scheduleId + " and bucket = '" + bucket + "'"); List<AggregateNumericMetric> actual = aggregateMapper.mapAll(resultSet); assertCollectionMatchesNoOrder("Metric data for schedule id " + scheduleId + " in bucket " + bucket + " does not match expected values", expected, actual, TEST_PRECISION); } protected void assertRawIndexEquals(DateTime time, List<Integer> scheduleIds) { assertIndexEquals(IndexBucket.RAW, time, time.plus(configuration.getRawTimeSliceDuration()), scheduleIds); } @SuppressWarnings("unchecked") protected void assertRawIndexEmpty(DateTime time) { assertIndexEquals(IndexBucket.RAW, time, time.plus(configuration.getRawTimeSliceDuration()), Collections.EMPTY_LIST); } protected void assert1HourIndexEquals(DateTime time, List<Integer> scheduleIds) { assertIndexEquals(IndexBucket.ONE_HOUR, time, time.plus(configuration.getOneHourTimeSliceDuration()), scheduleIds); } @SuppressWarnings("unchecked") protected void assert1HourIndexEmpty(DateTime time) { assertIndexEquals(IndexBucket.ONE_HOUR, time, time.plus(configuration.getOneHourTimeSliceDuration()), Collections.EMPTY_LIST); } protected void assert6HourIndexEquals(DateTime time, List<Integer> scheduleIds) { assertIndexEquals(IndexBucket.SIX_HOUR, time, time.plus(configuration.getSixHourTimeSliceDuration()), scheduleIds); } @SuppressWarnings("unchecked") protected void assert6HourIndexEmpty(DateTime time) { assertIndexEquals(IndexBucket.SIX_HOUR, time, time.plus(configuration.getSixHourTimeSliceDuration()), Collections.EMPTY_LIST); } private void assertIndexEquals(IndexBucket bucket, DateTime startTime, DateTime endTime, List<Integer> scheduleIds) { List<IndexEntry> expected = new ArrayList<IndexEntry>(); for (Integer scheduleId : scheduleIds) { expected.add(new IndexEntry(bucket, (scheduleId % configuration.getIndexPartitions()), startTime, scheduleId)); } List<IndexEntry> actual = new ArrayList<IndexEntry>(); IndexIterator iterator = new IndexIterator(startTime, endTime, bucket, dao, configuration); while (iterator.hasNext()) { actual.add(iterator.next()); } assertEquals(actual, expected, "The " + bucket + " index entries do not match"); } /** * Verifies that the 6 hour data table is empty for the specified schedule id. * * @param scheduleId The schedule id to query */ protected void assert6HourDataEmpty(int scheduleId) { assertMetricDataEmpty(scheduleId, Bucket.SIX_HOUR); } /** * Verifies that the 6 hour data table is empty for the specified schedule ids. * * @param scheduleIds The schedule ids to query */ protected void assert6HourDataEmpty(int... scheduleIds) { for (Integer scheduleId : scheduleIds) { assert6HourDataEmpty(scheduleId); } } /** * Verifies that the 24 hour data table is empty for the specified schedule id. * * @param scheduleId The schedule id to query */ protected void assert24HourDataEmpty(int scheduleId) { assertMetricDataEmpty(scheduleId, Bucket.TWENTY_FOUR_HOUR); } /** * Verifies that the 24 hour data table is empty for the specified schedule ids. * * @param scheduleIds The scheudle ids to query */ protected void assert24HourDataEmpty(int... scheduleIds) { for (Integer scheduleId : scheduleIds) { assert24HourDataEmpty(scheduleId); } } private void assertMetricDataEmpty(int scheduleId, Bucket bucket) { ResultSet resultSet = session.execute( "select schedule_id, bucket, time, avg, max, min " + "from " + MetricsTable.AGGREGATE + " " + "where schedule_id = " + scheduleId + " and bucket = '" + bucket + "'"); List<AggregateNumericMetric> metrics = aggregateMapper.mapAll(resultSet); assertEquals(metrics.size(), 0, "Expected " + bucket + " to be empty for schedule id " + scheduleId + " but found " + metrics); } protected int startScheduleId(int scheduleId) { return (scheduleId / PARTITION_SIZE) * PARTITION_SIZE; } static class DateTimeServiceStub extends DateTimeService { private DateTime now; public void setNow(DateTime now) { this.now = now; } @Override public DateTime now() { if (now == null) { return super.now(); } return now; } @Override public long nowInMillis() { if (now == null) { return super.nowInMillis(); } return now.getMillis(); } } }