blob: f387dbf6b49caee1b0d6cdd1315430a7d39d337e [file] [log] [blame]
Jian Li46148902016-01-29 13:33:50 -08001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.cpman.impl;
17
18import org.apache.commons.lang3.ArrayUtils;
19import org.onosproject.cpman.MetricsDatabase;
20import org.rrd4j.ConsolFun;
21import org.rrd4j.DsType;
22import org.rrd4j.core.ArcDef;
23import org.rrd4j.core.DsDef;
24import org.rrd4j.core.FetchRequest;
25import org.rrd4j.core.RrdBackendFactory;
26import org.rrd4j.core.RrdDb;
27import org.rrd4j.core.RrdDef;
28import org.rrd4j.core.Sample;
29
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Collections;
34import java.util.List;
35import java.util.Map;
36import java.util.concurrent.TimeUnit;
37import java.util.stream.IntStream;
38
39import static com.google.common.base.Preconditions.checkArgument;
40import static com.google.common.base.Preconditions.checkNotNull;
41
42/**
43 * An implementation of control plane metrics back-end database.
44 */
45public final class DefaultMetricsDatabase implements MetricsDatabase {
46 private String metricName;
47 private RrdDb rrdDb;
48 private Sample sample;
49 private static final long SECONDS_OF_DAY = 60L * 60L * 24L;
50 private static final long SECONDS_OF_MINUTE = 60L;
51 private static final ConsolFun CONSOL_FUNCTION = ConsolFun.LAST;
52 private static final String NON_EXIST_METRIC = "Non-existing metric type.";
53 private static final String INSUFFICIENT_DURATION = "Given duration less than one minute.";
54 private static final String EXCEEDED_DURATION = "Given duration exceeds a day time.";
55
56 private DefaultMetricsDatabase(String metricName, RrdDb rrdDb) {
57 this.metricName = metricName;
58 this.rrdDb = rrdDb;
59 }
60
61 @Override
62 public String metricName() {
63 return this.metricName;
64 }
65
66 @Override
67 public void updateMetric(String metricType, double value) {
68 updateMetric(metricType, value, System.currentTimeMillis() / 1000L);
69 }
70
71 @Override
72 public void updateMetric(String metricType, double value, long time) {
73 try {
74 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
75 sample = rrdDb.createSample(time);
76 sample.setValue(metricType, value);
77 sample.update();
78 } catch (IOException e) {
79 e.printStackTrace();
80 }
81 }
82
83 @Override
84 public void updateMetrics(Map<String, Double> metrics) {
85 updateMetrics(metrics, System.currentTimeMillis() / 1000L);
86 }
87
88 @Override
89 public void updateMetrics(Map<String, Double> metrics, long time) {
90 try {
91 sample = rrdDb.createSample(time);
92 metrics.forEach((k, v) -> {
93 try {
94 checkArgument(rrdDb.containsDs(k), NON_EXIST_METRIC);
95 sample.setValue(k, v);
96 } catch (IOException e) {
97 e.printStackTrace();
98 }
99 });
100 sample.update();
101 } catch (IOException e) {
102 e.printStackTrace();
103 }
104 }
105
106 @Override
107 public double recentMetric(String metricType) {
108 try {
109 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
110 return rrdDb.getDatasource(metricType).getLastValue();
111 } catch (IOException e) {
112 e.printStackTrace();
113 }
114 return 0D;
115 }
116
117 @Override
118 public double[] recentMetrics(String metricType, int duration, TimeUnit unit) {
119 try {
120 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
121 long endTime = rrdDb.getLastUpdateTime();
122 long startTime = endTime - TimeUnit.SECONDS.convert(duration, unit);
123 if (checkTimeRange(startTime, endTime)) {
124 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
125 return arrangeDataPoints(fr.fetchData().getValues(metricType));
126 }
127 } catch (IOException e) {
128 e.printStackTrace();
129 }
130 return new double[0];
131 }
132
133 @Override
134 public double minMetric(String metricType) {
135 try {
136 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
137 long endTime = rrdDb.getLastUpdateTime() - 1;
138 long startTime = endTime - SECONDS_OF_DAY + 1;
139 return minMetric(metricType, startTime, endTime);
140 } catch (IOException e) {
141 e.printStackTrace();
142 }
143 return 0D;
144 }
145
146 @Override
147 public double maxMetric(String metricType) {
148 try {
149 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
150 long endTime = rrdDb.getLastUpdateTime();
151 long startTime = endTime - SECONDS_OF_DAY;
152 return maxMetric(metricType, startTime, endTime);
153 } catch (IOException e) {
154 e.printStackTrace();
155 }
156 return 0D;
157 }
158
159 @Override
160 public double[] metrics(String metricType) {
161 try {
162 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
163 long endTime = rrdDb.getLastUpdateTime();
164 long startTime = endTime - SECONDS_OF_DAY;
165 return metrics(metricType, startTime, endTime);
166 } catch (IOException e) {
167 e.printStackTrace();
168 }
169 return new double[0];
170 }
171
172 @Override
173 public double[] metrics(String metricType, long startTime, long endTime) {
174 try {
175 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
176 if (checkTimeRange(startTime, endTime)) {
177 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
178 return arrangeDataPoints(fr.fetchData().getValues(metricType));
179 }
180 } catch (IOException e) {
181 e.printStackTrace();
182 }
183 return new double[0];
184 }
185
Jian Liec343ff2016-02-02 17:11:13 -0800186 @Override
187 public long lastUpdate(String metricType) {
188 try {
189 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
190 rrdDb.getLastUpdateTime();
191 } catch (IOException e) {
192 e.printStackTrace();
193 }
194 return 0L;
195 }
196
Jian Li46148902016-01-29 13:33:50 -0800197 // try to check whether projected time range is within a day
198 private boolean checkTimeRange(long startTime, long endTime) {
199 // check whether the given startTime and endTime larger than 1 minute
200 checkArgument(endTime - startTime >= SECONDS_OF_MINUTE, INSUFFICIENT_DURATION);
201
202 // check whether the given start time and endTime smaller than 1 day
203 checkArgument(endTime - startTime <= SECONDS_OF_DAY, EXCEEDED_DURATION);
204 return true;
205 }
206
207 // try to remove first and last data points
208 private double[] arrangeDataPoints(double[] data) {
209 return Arrays.copyOfRange(data, 1, data.length - 1);
210 }
211
212 // obtains maximum metric value among projected range
213 private double maxMetric(String metricType, long startTime, long endTime) {
214 double[] all = metrics(metricType, startTime, endTime);
215 List list = Arrays.asList(ArrayUtils.toObject(all));
216 return (double) Collections.max(list);
217 }
218
219 // obtains minimum metric value among projected range
220 private double minMetric(String metricType, long startTime, long endTime) {
221 double[] all = metrics(metricType, startTime, endTime);
222 List list = Arrays.asList(ArrayUtils.toObject(all));
223 return (double) Collections.min(list);
224 }
225
226 public static final class Builder implements MetricsDatabase.Builder {
227 private static final int RESOLUTION = 60; // seconds
228 private static final String STORING_METHOD = "MEMORY";
229 private static final DsType SOURCE_TYPE = DsType.GAUGE;
230 private static final String DB_PATH = "CPMAN";
231 private static final ConsolFun CONSOL_FUNCTION = ConsolFun.LAST;
232 private static final double MIN_VALUE = 0;
233 private static final double MAX_VALUE = Double.NaN;
234 private static final double XFF_VALUE = 0.2;
235 private static final int STEP_VALUE = 1;
236 private static final int ROW_VALUE = 60 * 24;
237 private static final String METRIC_NAME_MSG = "Must specify a metric name.";
238 private static final String METRIC_TYPE_MSG = "Must supply at least a metric type.";
239
240 private RrdDb rrdDb;
241 private RrdDef rrdDef;
242 private List<DsDef> dsDefs;
243 private String metricName;
244
245 public Builder() {
246
247 // define the resolution of monitored metrics
248 rrdDef = new RrdDef(DB_PATH, RESOLUTION);
249
250 // initialize data source definition list
251 dsDefs = new ArrayList<>();
252 }
253
254 @Override
255 public Builder withMetricName(String metricName) {
256 this.metricName = metricName;
257 return this;
258 }
259
260 @Override
261 public Builder addMetricType(String metricType) {
262 dsDefs.add(defineSchema(metricType));
263 return this;
264 }
265
266 @Override
267 public MetricsDatabase build() {
268 checkNotNull(metricName, METRIC_NAME_MSG);
269 checkArgument(dsDefs.size() != 0, METRIC_TYPE_MSG);
270
271 try {
272 DsDef[] dsDefArray = new DsDef[dsDefs.size()];
273 IntStream.range(0, dsDefs.size()).forEach(i -> dsDefArray[i] = dsDefs.get(i));
274
275 rrdDef.addDatasource(dsDefArray);
276 rrdDef.setStep(RESOLUTION);
277
278 // raw archive, no aggregation is required
279 ArcDef rawArchive = new ArcDef(CONSOL_FUNCTION, XFF_VALUE,
280 STEP_VALUE, ROW_VALUE);
281 rrdDef.addArchive(rawArchive);
282
283 // always store the metric data in memory...
284 rrdDb = new RrdDb(rrdDef, RrdBackendFactory.getFactory(STORING_METHOD));
285 } catch (IOException e) {
286 e.printStackTrace();
287 }
288
289 return new DefaultMetricsDatabase(metricName, rrdDb);
290 }
291
292 private DsDef defineSchema(String metricType) {
293 return new DsDef(metricType, SOURCE_TYPE, RESOLUTION,
294 MIN_VALUE, MAX_VALUE);
295 }
296 }
297}