blob: d0efa9f07cfc5fcb38dc9e556a9b439b9a630525 [file] [log] [blame]
Jian Lib97f6fb2016-03-21 11:33:28 -07001/*
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.influxdbmetrics;
17
18import com.codahale.metrics.MetricRegistry;
19import com.izettle.metrics.influxdb.InfluxDbHttpSender;
20import com.izettle.metrics.influxdb.InfluxDbReporter;
21import org.apache.commons.lang.StringUtils;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.onlab.metrics.MetricsService;
30import org.onlab.util.Tools;
31import org.onosproject.cfg.ComponentConfigService;
32import org.onosproject.cluster.ClusterService;
33import org.onosproject.cluster.ControllerNode;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
36import org.osgi.service.component.ComponentContext;
37import org.slf4j.Logger;
38
39import java.util.Dictionary;
40import java.util.concurrent.TimeUnit;
41
42import static org.slf4j.LoggerFactory.getLogger;
43
44/**
45 * A Metric reporter that reports all metrics value to influxDB server.
46 */
47@Component(immediate = true)
48public class InfluxDbMetricsReporter {
49 private final Logger log = getLogger(getClass());
50
51 private static final int REPORT_PERIOD = 1;
52 private static final TimeUnit REPORT_TIME_UNIT = TimeUnit.MINUTES;
53
54 private static final String DEFAULT_PROTOCOL = "http";
55 private static final String DEFAULT_ADDRESS = "localhost";
56 private static final int DEFAULT_PORT = 8086;
57 private static final String DEFAULT_METRIC_NAMES = "default";
58 private static final String DEFAULT_DATABASE = "onos";
59 private static final String DEFAULT_USERNAME = "onos";
60 private static final String DEFAULT_PASSWORD = "onos.password";
61 private static final String SEPARATOR = ":";
62 private static final int DEFAULT_CONN_TIMEOUT = 1000;
63 private static final int DEFAULT_READ_TIMEOUT = 1000;
64
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected CoreService coreService;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected MetricsService metricsService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected ComponentConfigService cfgService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected ClusterService clusterService;
76
77 @Property(name = "monitorAll", boolValue = true,
78 label = "Enable to monitor all of metrics stored in metric registry " +
79 "default is true")
80 protected boolean monitorAll = true;
81
82 @Property(name = "metricNames", value = DEFAULT_METRIC_NAMES,
83 label = "Names of metric to be monitored in third party monitoring " +
84 "server; default metric names are 'default'")
85 protected String metricNames = DEFAULT_METRIC_NAMES;
86
87 @Property(name = "address", value = DEFAULT_ADDRESS,
88 label = "IP address of influxDB server; " +
89 "default is localhost")
90 protected String address = DEFAULT_ADDRESS;
91
92 @Property(name = "port", intValue = DEFAULT_PORT,
93 label = "Port number of influxDB server; " +
94 "default is 8086")
95 protected int port = DEFAULT_PORT;
96
97 @Property(name = "database", value = DEFAULT_DATABASE,
98 label = "Database name of influxDB server; " +
99 "default is onos")
100 protected String database = DEFAULT_DATABASE;
101
102 @Property(name = "username", value = DEFAULT_USERNAME,
103 label = "Username of influxDB server; default is onos")
104 protected String username = DEFAULT_USERNAME;
105
106 @Property(name = "password", value = DEFAULT_PASSWORD,
107 label = "Password of influxDB server; default is onos.password")
108 protected String password = DEFAULT_PASSWORD;
109
110 private ApplicationId appId;
111 private InfluxDbReporter influxDbReporter;
112 private InfluxDbHttpSender influxDbHttpSender;
113
114 @Activate
115 public void activate() {
116 cfgService.registerProperties(getClass());
117 appId = coreService.registerApplication("org.onosproject.influxdbmetrics");
118
119 startReport();
120 log.info("Started");
121 }
122
123 @Deactivate
124 public void deactivate() {
125 cfgService.unregisterProperties(getClass(), false);
126
127 stopReport();
128 log.info("Stopped");
129 }
130
131 @Modified
132 public void modified(ComponentContext context) {
133 readComponentConfiguration(context);
134 restartReport();
135 }
136
137 protected void startReport() {
138 try {
139 influxDbHttpSender = new InfluxDbHttpSender(DEFAULT_PROTOCOL, address,
140 port, database, username + SEPARATOR + password, REPORT_TIME_UNIT,
141 DEFAULT_CONN_TIMEOUT, DEFAULT_READ_TIMEOUT);
142 MetricRegistry mr = metricsService.getMetricRegistry();
143 influxDbReporter = InfluxDbReporter.forRegistry(addHostPrefix(filter(mr)))
144 .convertRatesTo(TimeUnit.SECONDS)
145 .convertDurationsTo(TimeUnit.MILLISECONDS)
146 .build(influxDbHttpSender);
147 influxDbReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
148 log.info("Start to report metrics to influxDB.");
149 } catch (Exception e) {
150 log.error("Fail to connect to given influxDB server!");
151 }
152 }
153
154 protected void stopReport() {
155 influxDbReporter.stop();
156 influxDbHttpSender = null;
157 influxDbReporter = null;
158 log.info("Stop reporting metrics to influxDB.");
159 }
160
161 protected void restartReport() {
162 stopReport();
163 startReport();
164 }
165
166 /**
167 * Filters the metrics to only include a set of the given metrics.
168 *
169 * @param metricRegistry original metric registry
170 * @return filtered metric registry
171 */
172 protected MetricRegistry filter(MetricRegistry metricRegistry) {
173 if (!monitorAll) {
174 final MetricRegistry filtered = new MetricRegistry();
175 metricRegistry.getNames().stream().filter(name ->
176 containsName(name, metricNames)).forEach(name ->
177 filtered.register(name, metricRegistry.getMetrics().get(name)));
178 return filtered;
179 } else {
180 return metricRegistry;
181 }
182 }
183
184 /**
185 * Appends node IP prefix to all performance metrics.
186 *
187 * @param metricRegistry original metric registry
188 * @return prefix added metric registry
189 */
190 protected MetricRegistry addHostPrefix(MetricRegistry metricRegistry) {
191 MetricRegistry moddedRegistry = new MetricRegistry();
192 ControllerNode node = clusterService.getLocalNode();
193 String prefix = node.id().id() + ".";
194 metricRegistry.getNames().stream().forEach(name ->
195 moddedRegistry.register(prefix + name, metricRegistry.getMetrics().get(name)));
196
197 return moddedRegistry;
198 }
199
200 /**
201 * Looks up whether the metric name contains the given prefix keywords.
202 * Note that the keywords are separated with comma as delimiter.
203 *
204 * @param full the original metric name that to be compared with
205 * @param prefixes the prefix keywords that are matched against with the metric name
206 * @return boolean value that denotes whether the metric name starts with the given prefix
207 */
208 protected boolean containsName(String full, String prefixes) {
209 String[] prefixArray = StringUtils.split(prefixes, ",");
210 for (String prefix : prefixArray) {
211 if (StringUtils.startsWith(full, StringUtils.trimToEmpty(prefix))) {
212 return true;
213 }
214 }
215 return false;
216 }
217
218 /**
219 * Extracts properties from the component configuration context.
220 *
221 * @param context the component context
222 */
223 private void readComponentConfiguration(ComponentContext context) {
224 Dictionary<?, ?> properties = context.getProperties();
225
226 String addressStr = Tools.get(properties, "address");
227 address = addressStr != null ? addressStr : DEFAULT_ADDRESS;
228 log.info("Configured. InfluxDB server address is {}", address);
229
230 String metricNameStr = Tools.get(properties, "metricNames");
231 metricNames = metricNameStr != null ? metricNameStr : DEFAULT_METRIC_NAMES;
232 log.info("Configured. Metric name is {}", metricNames);
233
234 String databaseStr = Tools.get(properties, "database");
235 database = databaseStr != null ? databaseStr : DEFAULT_DATABASE;
236 log.info("Configured. InfluxDB server database is {}", database);
237
238 String usernameStr = Tools.get(properties, "username");
239 username = usernameStr != null ? usernameStr : DEFAULT_USERNAME;
240 log.info("Configured. InfluxDB server username is {}", username);
241
242 String passwordStr = Tools.get(properties, "password");
243 password = passwordStr != null ? passwordStr : DEFAULT_PASSWORD;
244 log.info("Configured. InfluxDB server password is {}", password);
245
246 Integer portConfigured = Tools.getIntegerProperty(properties, "port");
247 if (portConfigured == null) {
248 port = DEFAULT_PORT;
249 log.info("InfluxDB port is not configured, default value is {}", port);
250 } else {
251 port = portConfigured;
252 log.info("Configured. InfluxDB port is configured to {}", port);
253 }
254
255 Boolean monitorAllEnabled = Tools.isPropertyEnabled(properties, "monitorAll");
256 if (monitorAllEnabled == null) {
257 log.info("Monitor all metrics is not configured, " +
258 "using current value of {}", monitorAll);
259 } else {
260 monitorAll = monitorAllEnabled;
261 log.info("Configured. Monitor all metrics is {}",
262 monitorAll ? "enabled" : "disabled");
263 }
264 }
265}