/*
* Copyright (c) 2015 Dell Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.tsdr.datastorage;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import com.google.common.util.concurrent.Futures;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.opendaylight.tsdr.datastorage.aggregate.AggregationFunction;
import org.opendaylight.tsdr.datastorage.aggregate.IntervalGenerator;
import org.opendaylight.tsdr.spi.persistence.TSDRBinaryPersistenceService;
import org.opendaylight.tsdr.spi.persistence.TSDRLogPersistenceService;
import org.opendaylight.tsdr.spi.persistence.TSDRMetricPersistenceService;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.GetTSDRLogRecordsInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.GetTSDRLogRecordsOutput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.GetTSDRLogRecordsOutputBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.StoreTSDRLogRecordInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.TsdrLogDataService;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.gettsdrlogrecords.output.Logs;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.gettsdrlogrecords.output.LogsBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.log.data.rev160325.storetsdrlogrecord.input.TSDRLogRecord;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.AggregationType;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRAggregatedMetricsInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRAggregatedMetricsOutput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRAggregatedMetricsOutputBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRMetricsInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRMetricsInputBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRMetricsOutput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.GetTSDRMetricsOutputBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.StoreTSDRMetricRecordInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.TsdrMetricDataService;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.gettsdraggregatedmetrics.output.AggregatedMetrics;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.gettsdraggregatedmetrics.output.AggregatedMetricsBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.gettsdrmetrics.output.Metrics;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.gettsdrmetrics.output.MetricsBuilder;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.metric.data.rev160325.storetsdrmetricrecord.input.TSDRMetricRecord;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.DataCategory;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.PurgeAllTSDRRecordInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.PurgeTSDRRecordInput;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.TSDRService;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TSDR storage service implementation class.
*
* <p>
* It takes the data collected from data collection service as input, convert it
* into TSDR data model, and then send a request to TSDR persistence service to
* store into the persistence data store.
* </p>
*
* @author <a href="mailto:yuling_c@dell.com">YuLing Chen</a>
*
* Created: March 1, 2015
*/
public class TSDRStorageServiceImpl implements TSDRService,TsdrMetricDataService,TsdrLogDataService, AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(TSDRStorageServiceImpl.class);
private final ServiceLoader<AggregationFunction> aggregationFunctions;
private TSDRMetricPersistenceService metricPersistenceService;
private TSDRLogPersistenceService logPersistenceService;
private TSDRBinaryPersistenceService binaryPersistenceService;
public TSDRStorageServiceImpl(TSDRMetricPersistenceService metricService,TSDRLogPersistenceService logService){
this.metricPersistenceService = metricService;
this.logPersistenceService = logService;
aggregationFunctions = ServiceLoader.load(AggregationFunction.class,this.getClass().getClassLoader());
}
public void setMetricPersistenceService(TSDRMetricPersistenceService metricService){
this.metricPersistenceService = metricService;
}
public void setLogPersistenceService(TSDRLogPersistenceService logService) {
this.logPersistenceService = logService;
}
public void setBinaryPersistenceService(TSDRBinaryPersistenceService binaryService){
this.binaryPersistenceService = binaryService;
}
/**
* stores TSDRMetricRecord.
*
*/
@Override
public Future<RpcResult<java.lang.Void>> storeTSDRMetricRecord(StoreTSDRMetricRecordInput input) {
log.debug("Entering TSDRStorageService.storeTSDRMetrics()");
if ( input == null || input.getTSDRMetricRecord() == null){
log.error("Input of storeTSDRMetrics is null");
return Futures.immediateFuture(RpcResultBuilder.<Void> success()
.build());
}
List<TSDRMetricRecord> tsdrMetricRecordList = new ArrayList<TSDRMetricRecord>(input.getTSDRMetricRecord().size());
tsdrMetricRecordList.addAll(input.getTSDRMetricRecord());
if(this.metricPersistenceService != null) {
metricPersistenceService.storeMetric(tsdrMetricRecordList);
}else{
log.warn("storeTSDRMetricRecord: cannot store the metric -- persistence service is found to be null");
}
log.debug("Exiting TSDRStorageService.storeTSDRMetrics()");
return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
}
/**
* purges TSDRMetricRecord.
*
*/
@Override
public Future<RpcResult<java.lang.Void>> purgeTSDRRecord(PurgeTSDRRecordInput input){
log.info("Entering TSDRStorageService.purgeTSDRRecord()");
if ( input == null || input.getRetentionTime() == null
|| input.getRetentionTime() == 0
|| input.getTSDRDataCategory() == null){
/*
* The data category and retention_time of this API cannot be null.
*
*/
log.error("Input of purgeTSDRRecord invalid");
return Futures.immediateFuture(RpcResultBuilder.<Void> success()
.build());
}
DataCategory category = input.getTSDRDataCategory();
Long timestamp = input.getRetentionTime();
if(this.metricPersistenceService != null) {
this.metricPersistenceService.purge(category, timestamp);
}else{
log.warn("purgeTSDRRecord -- persistence service is found to be null");
}
log.info("Exiting TSDRStorageService.purgeTSDRRecord()");
return Futures.immediateFuture(RpcResultBuilder.<Void> success()
.build());
}
/**
* purges TSDRMetricRecord.
*
*/
@Override
public Future<RpcResult<java.lang.Void>> purgeAllTSDRRecord(PurgeAllTSDRRecordInput input){
log.info("Entering TSDRStorageService.purgeAllTSDRRecord()");
if ( input == null || input.getRetentionTime() == null || input.getRetentionTime() == 0){
/*
* The retention time cannot be null.
*
*/
log.error("Input of purgeAllTSDRRecord is invalid");
return Futures.immediateFuture(RpcResultBuilder.<Void> success()
.build());
}
Long timestamp = input.getRetentionTime();
if(this.metricPersistenceService != null) {
this.metricPersistenceService.purge(timestamp);
}else{
log.warn("purgeAllTSDRRecord -- persistence service is found to be null");
}
log.info("Exiting TSDRStorageService.purgeAllTSDRRecord()");
return Futures.immediateFuture(RpcResultBuilder.<Void> success()
.build());
}
@Override
/**
* Close DB connections in the persistence data store.
*/
public void close() throws Exception {
if(this.metricPersistenceService!=null){
//add close here
}
}
@Override
public Future<RpcResult<GetTSDRMetricsOutput>> getTSDRMetrics(GetTSDRMetricsInput input) {
List<TSDRMetricRecord> result = this.metricPersistenceService.getTSDRMetricRecords(input.getTSDRDataCategory(), input.getStartTime(), input.getEndTime());
return buildResult(result);
}
private Future<RpcResult<GetTSDRMetricsOutput>> buildResult(List<TSDRMetricRecord> result){
GetTSDRMetricsOutputBuilder output = new GetTSDRMetricsOutputBuilder();
if(this.metricPersistenceService==null){
RpcResultBuilder<GetTSDRMetricsOutput> builder = RpcResultBuilder.failed();
return builder.buildFuture();
}
List<Metrics> metrics = new LinkedList<Metrics>();
for(TSDRMetricRecord m:result){
MetricsBuilder b = new MetricsBuilder();
b.setMetricName(m.getMetricName());
b.setMetricValue(m.getMetricValue());
b.setNodeID(m.getNodeID());
b.setRecordKeys(m.getRecordKeys());
b.setTimeStamp(m.getTimeStamp());
b.setTSDRDataCategory(m.getTSDRDataCategory());
metrics.add(b.build());
}
output.setMetrics(metrics);
RpcResultBuilder<GetTSDRMetricsOutput> builder = RpcResultBuilder.success(output);
return builder.buildFuture();
}
@Override
public Future<RpcResult<GetTSDRAggregatedMetricsOutput>> getTSDRAggregatedMetrics(
final GetTSDRAggregatedMetricsInput input) {
if(this.metricPersistenceService==null){
RpcResultBuilder<GetTSDRAggregatedMetricsOutput> builder = RpcResultBuilder.failed();
return builder.buildFuture();
}
// Locate the appropriate aggregation function implementation
final AggregationFunction aggregationFunction = Iterators.find(
aggregationFunctions.iterator(), new Predicate<AggregationFunction>() {
@Override
public boolean apply(AggregationFunction candidate) {
return candidate.getType().equals(input.getAggregation());
}
}, null);
if (aggregationFunction == null) {
return RpcResultBuilder.<GetTSDRAggregatedMetricsOutput>failed()
.withError(ErrorType.APPLICATION,
String.format("No aggregation function implementation was found for '%s'.",
input.getAggregation()))
.buildFuture();
}
// Gather the metrics for the given time span
final GetTSDRMetricsInput metricsInput = new GetTSDRMetricsInputBuilder()
.setTSDRDataCategory(input.getTSDRDataCategory())
.setStartTime(input.getStartTime())
.setEndTime(input.getEndTime()).build();
final Future<RpcResult<GetTSDRMetricsOutput>> result = getTSDRMetrics(metricsInput);
//Fix for bug 5655 - Do not aggregate when # of points is less than requested
long numberOfPoints = (input.getEndTime()-input.getStartTime())/input.getInterval();
try {
//In case of a MEAN aggregation and the number of requested points is larger than what is, just return the original
//result.
if(input.getAggregation() == AggregationType.MEAN &&
result.get().getResult().getMetrics().size()<=numberOfPoints){
final List<AggregatedMetrics> aggregatedMetrics = Lists.newLinkedList();
for(Metrics m:result.get().getResult().getMetrics()){
// Aggregate the metrics in the interval
aggregatedMetrics.add(new AggregatedMetricsBuilder()
.setTimeStamp(m.getTimeStamp())
.setMetricValue(m.getMetricValue()).build());
}
// We're done
final GetTSDRAggregatedMetricsOutputBuilder outputBuilder = new GetTSDRAggregatedMetricsOutputBuilder()
.setAggregatedMetrics(aggregatedMetrics);
return RpcResultBuilder.success(outputBuilder).buildFuture();
}
} catch (InterruptedException | ExecutionException e) {
RpcResultBuilder builder = RpcResultBuilder.failed();
builder.withError(ErrorType.APPLICATION,"Failed to extract data for aggregation");
return builder.buildFuture();
}
// Aggregate the results
return Futures.lazyTransform(result, new Function<RpcResult<GetTSDRMetricsOutput>,RpcResult<GetTSDRAggregatedMetricsOutput>>() {
@Override
public RpcResult<GetTSDRAggregatedMetricsOutput> apply(RpcResult<GetTSDRMetricsOutput> metricsOutput) {
final List<AggregatedMetrics> aggregatedMetrics = Lists.newLinkedList();
final PeekingIterator<Metrics> metricIterator = Iterators.peekingIterator(metricsOutput.getResult().getMetrics().iterator());
// Generate and iterate over all the intervals in the given range
for (Long intervalStartInclusive : new IntervalGenerator(input.getStartTime(), input.getEndTime(), input.getInterval())) {
final Long intervalEndExclusive = intervalStartInclusive + input.getInterval();
// Gather the list of metrics that fall within the current interval
// We make the assumption that the list of metrics is already sorted by time-stamp
final List<Metrics> metricsInInterval = Lists.newLinkedList();
while (metricIterator.hasNext()) {
if (metricIterator.peek().getTimeStamp() >= intervalEndExclusive) {
break;
}
metricsInInterval.add(metricIterator.next());
}
// Aggregate the metrics in the interval
aggregatedMetrics.add(new AggregatedMetricsBuilder()
.setTimeStamp(intervalStartInclusive)
.setMetricValue(aggregationFunction.aggregate(metricsInInterval)).build());
}
// We're done
final GetTSDRAggregatedMetricsOutput output = new GetTSDRAggregatedMetricsOutputBuilder()
.setAggregatedMetrics(aggregatedMetrics).build();
return RpcResultBuilder.success(output).build();
}
});
}
@Override
public Future<RpcResult<GetTSDRLogRecordsOutput>> getTSDRLogRecords(GetTSDRLogRecordsInput input) {
if(this.logPersistenceService==null){
RpcResultBuilder<GetTSDRLogRecordsOutput> builder = RpcResultBuilder.failed();
return builder.buildFuture();
}
List<TSDRLogRecord> result = this.logPersistenceService.getTSDRLogRecords(input.getTSDRDataCategory(), input.getStartTime(), input.getEndTime());
GetTSDRLogRecordsOutputBuilder output = new GetTSDRLogRecordsOutputBuilder();
List<Logs> logs = new LinkedList<Logs>();
for(TSDRLogRecord l:result){
LogsBuilder b = new LogsBuilder();
b.setTSDRDataCategory(l.getTSDRDataCategory());
b.setTimeStamp(l.getTimeStamp());
b.setRecordKeys(l.getRecordKeys());
b.setNodeID(l.getNodeID());
b.setIndex(l.getIndex());
b.setRecordAttributes(l.getRecordAttributes());
b.setRecordFullText(l.getRecordFullText());
logs.add(b.build());
}
output.setLogs(logs);
RpcResultBuilder<GetTSDRLogRecordsOutput> builder = RpcResultBuilder.success(output);
return builder.buildFuture();
}
public Future<RpcResult<Void>> storeTSDRLogRecord(StoreTSDRLogRecordInput input) {
log.debug("Entering TSDRStorageService.storeTSDRLog()");
if ( input == null || input.getTSDRLogRecord() == null){
log.error("Input of storeTSDRLog is null");
return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
}
List<TSDRLogRecord> tsdrLogRecordList = new ArrayList<TSDRLogRecord>(input.getTSDRLogRecord().size());
tsdrLogRecordList.addAll(input.getTSDRLogRecord());
if(this.logPersistenceService != null) {
this.logPersistenceService.storeLog(tsdrLogRecordList);
}else{
log.warn("storeTSDRMetricRecord: cannot store the metric -- persistence service is found to be null");
}
log.debug("Exiting TSDRStorageService.storeTSDRMetrics()");
return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
}
}