blob: 31a19da4223aa20e6f41bdb80225e94ffdcf41cb [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
Jian Lic5cb4a12016-02-03 23:24:42 -080056 /**
57 * Constructs a metrics database using the given metric name and
58 * round robin database.
59 *
60 * @param metricName metric name
61 * @param rrdDb round robin database
62 */
Jian Li46148902016-01-29 13:33:50 -080063 private DefaultMetricsDatabase(String metricName, RrdDb rrdDb) {
64 this.metricName = metricName;
65 this.rrdDb = rrdDb;
66 }
67
68 @Override
69 public String metricName() {
70 return this.metricName;
71 }
72
73 @Override
74 public void updateMetric(String metricType, double value) {
75 updateMetric(metricType, value, System.currentTimeMillis() / 1000L);
76 }
77
78 @Override
79 public void updateMetric(String metricType, double value, long time) {
80 try {
81 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
82 sample = rrdDb.createSample(time);
83 sample.setValue(metricType, value);
84 sample.update();
85 } catch (IOException e) {
86 e.printStackTrace();
87 }
88 }
89
90 @Override
91 public void updateMetrics(Map<String, Double> metrics) {
92 updateMetrics(metrics, System.currentTimeMillis() / 1000L);
93 }
94
95 @Override
96 public void updateMetrics(Map<String, Double> metrics, long time) {
97 try {
98 sample = rrdDb.createSample(time);
99 metrics.forEach((k, v) -> {
100 try {
101 checkArgument(rrdDb.containsDs(k), NON_EXIST_METRIC);
102 sample.setValue(k, v);
103 } catch (IOException e) {
104 e.printStackTrace();
105 }
106 });
107 sample.update();
108 } catch (IOException e) {
109 e.printStackTrace();
110 }
111 }
112
113 @Override
114 public double recentMetric(String metricType) {
115 try {
116 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
117 return rrdDb.getDatasource(metricType).getLastValue();
118 } catch (IOException e) {
119 e.printStackTrace();
120 }
121 return 0D;
122 }
123
124 @Override
125 public double[] recentMetrics(String metricType, int duration, TimeUnit unit) {
126 try {
127 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
128 long endTime = rrdDb.getLastUpdateTime();
129 long startTime = endTime - TimeUnit.SECONDS.convert(duration, unit);
130 if (checkTimeRange(startTime, endTime)) {
131 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
132 return arrangeDataPoints(fr.fetchData().getValues(metricType));
133 }
134 } catch (IOException e) {
135 e.printStackTrace();
136 }
137 return new double[0];
138 }
139
140 @Override
141 public double minMetric(String metricType) {
142 try {
143 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
144 long endTime = rrdDb.getLastUpdateTime() - 1;
145 long startTime = endTime - SECONDS_OF_DAY + 1;
146 return minMetric(metricType, startTime, endTime);
147 } catch (IOException e) {
148 e.printStackTrace();
149 }
150 return 0D;
151 }
152
153 @Override
154 public double maxMetric(String metricType) {
155 try {
156 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
157 long endTime = rrdDb.getLastUpdateTime();
158 long startTime = endTime - SECONDS_OF_DAY;
159 return maxMetric(metricType, startTime, endTime);
160 } catch (IOException e) {
161 e.printStackTrace();
162 }
163 return 0D;
164 }
165
166 @Override
167 public double[] metrics(String metricType) {
168 try {
169 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
170 long endTime = rrdDb.getLastUpdateTime();
171 long startTime = endTime - SECONDS_OF_DAY;
172 return metrics(metricType, startTime, endTime);
173 } catch (IOException e) {
174 e.printStackTrace();
175 }
176 return new double[0];
177 }
178
179 @Override
180 public double[] metrics(String metricType, long startTime, long endTime) {
181 try {
182 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
183 if (checkTimeRange(startTime, endTime)) {
184 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
185 return arrangeDataPoints(fr.fetchData().getValues(metricType));
186 }
187 } catch (IOException e) {
188 e.printStackTrace();
189 }
190 return new double[0];
191 }
192
Jian Liec343ff2016-02-02 17:11:13 -0800193 @Override
194 public long lastUpdate(String metricType) {
195 try {
196 checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
197 rrdDb.getLastUpdateTime();
198 } catch (IOException e) {
199 e.printStackTrace();
200 }
201 return 0L;
202 }
203
Jian Li46148902016-01-29 13:33:50 -0800204 // try to check whether projected time range is within a day
205 private boolean checkTimeRange(long startTime, long endTime) {
206 // check whether the given startTime and endTime larger than 1 minute
207 checkArgument(endTime - startTime >= SECONDS_OF_MINUTE, INSUFFICIENT_DURATION);
208
209 // check whether the given start time and endTime smaller than 1 day
210 checkArgument(endTime - startTime <= SECONDS_OF_DAY, EXCEEDED_DURATION);
211 return true;
212 }
213
214 // try to remove first and last data points
215 private double[] arrangeDataPoints(double[] data) {
216 return Arrays.copyOfRange(data, 1, data.length - 1);
217 }
218
219 // obtains maximum metric value among projected range
220 private double maxMetric(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.max(list);
224 }
225
226 // obtains minimum metric value among projected range
227 private double minMetric(String metricType, long startTime, long endTime) {
228 double[] all = metrics(metricType, startTime, endTime);
229 List list = Arrays.asList(ArrayUtils.toObject(all));
230 return (double) Collections.min(list);
231 }
232
233 public static final class Builder implements MetricsDatabase.Builder {
Jian Li85060ac2016-02-04 09:58:56 -0800234 private static final int RESOLUTION_IN_SECOND = 60;
Jian Li46148902016-01-29 13:33:50 -0800235 private static final String STORING_METHOD = "MEMORY";
236 private static final DsType SOURCE_TYPE = DsType.GAUGE;
237 private static final String DB_PATH = "CPMAN";
238 private static final ConsolFun CONSOL_FUNCTION = ConsolFun.LAST;
239 private static final double MIN_VALUE = 0;
240 private static final double MAX_VALUE = Double.NaN;
241 private static final double XFF_VALUE = 0.2;
242 private static final int STEP_VALUE = 1;
243 private static final int ROW_VALUE = 60 * 24;
244 private static final String METRIC_NAME_MSG = "Must specify a metric name.";
245 private static final String METRIC_TYPE_MSG = "Must supply at least a metric type.";
246
247 private RrdDb rrdDb;
248 private RrdDef rrdDef;
249 private List<DsDef> dsDefs;
250 private String metricName;
251
252 public Builder() {
Jian Li46148902016-01-29 13:33:50 -0800253 // initialize data source definition list
254 dsDefs = new ArrayList<>();
255 }
256
257 @Override
258 public Builder withMetricName(String metricName) {
259 this.metricName = metricName;
Jian Li7d180c52016-02-01 21:53:08 -0800260
261 // define the resolution of monitored metrics
Jian Li85060ac2016-02-04 09:58:56 -0800262 rrdDef = new RrdDef(DB_PATH + "_" + metricName, RESOLUTION_IN_SECOND);
Jian Li46148902016-01-29 13:33:50 -0800263 return this;
264 }
265
266 @Override
267 public Builder addMetricType(String metricType) {
268 dsDefs.add(defineSchema(metricType));
269 return this;
270 }
271
272 @Override
273 public MetricsDatabase build() {
274 checkNotNull(metricName, METRIC_NAME_MSG);
275 checkArgument(dsDefs.size() != 0, METRIC_TYPE_MSG);
276
277 try {
278 DsDef[] dsDefArray = new DsDef[dsDefs.size()];
279 IntStream.range(0, dsDefs.size()).forEach(i -> dsDefArray[i] = dsDefs.get(i));
280
281 rrdDef.addDatasource(dsDefArray);
Jian Li85060ac2016-02-04 09:58:56 -0800282 rrdDef.setStep(RESOLUTION_IN_SECOND);
Jian Li46148902016-01-29 13:33:50 -0800283
284 // raw archive, no aggregation is required
285 ArcDef rawArchive = new ArcDef(CONSOL_FUNCTION, XFF_VALUE,
286 STEP_VALUE, ROW_VALUE);
287 rrdDef.addArchive(rawArchive);
288
289 // always store the metric data in memory...
290 rrdDb = new RrdDb(rrdDef, RrdBackendFactory.getFactory(STORING_METHOD));
291 } catch (IOException e) {
292 e.printStackTrace();
293 }
294
295 return new DefaultMetricsDatabase(metricName, rrdDb);
296 }
297
298 private DsDef defineSchema(String metricType) {
Jian Li85060ac2016-02-04 09:58:56 -0800299 return new DsDef(metricType, SOURCE_TYPE, RESOLUTION_IN_SECOND,
Jian Li46148902016-01-29 13:33:50 -0800300 MIN_VALUE, MAX_VALUE);
301 }
302 }
303}