Thomas Vachuska

Merge remote-tracking branch 'origin/master'

1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onos-app-metrics</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-app-metrics-intent</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS intent metrics application</description>
18 +
19 + <dependencies>
20 + <dependency>
21 + <groupId>org.onlab.onos</groupId>
22 + <artifactId>onos-cli</artifactId>
23 + <version>${project.version}</version>
24 + </dependency>
25 +
26 + <dependency>
27 + <groupId>org.apache.karaf.shell</groupId>
28 + <artifactId>org.apache.karaf.shell.console</artifactId>
29 + </dependency>
30 + </dependencies>
31 +
32 +</project>
1 +package org.onlab.onos.metrics.intent;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import java.util.LinkedList;
6 +import java.util.List;
7 +
8 +import com.codahale.metrics.Gauge;
9 +import com.codahale.metrics.Meter;
10 +import com.google.common.collect.ImmutableList;
11 +import org.apache.felix.scr.annotations.Activate;
12 +import org.apache.felix.scr.annotations.Component;
13 +import org.apache.felix.scr.annotations.Deactivate;
14 +import org.apache.felix.scr.annotations.Reference;
15 +import org.apache.felix.scr.annotations.ReferenceCardinality;
16 +import org.apache.felix.scr.annotations.Service;
17 +import org.onlab.metrics.MetricsComponent;
18 +import org.onlab.metrics.MetricsFeature;
19 +import org.onlab.metrics.MetricsService;
20 +import org.onlab.onos.net.intent.IntentEvent;
21 +import org.onlab.onos.net.intent.IntentListener;
22 +import org.onlab.onos.net.intent.IntentService;
23 +import org.slf4j.Logger;
24 +
25 +/**
26 + * ONOS Intent Metrics Application that collects intent-related metrics.
27 + */
28 +@Component(immediate = true)
29 +@Service
30 +public class IntentMetrics implements IntentMetricsService,
31 + IntentListener {
32 + private static final Logger log = getLogger(IntentMetrics.class);
33 +
34 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
35 + protected IntentService intentService;
36 + private LinkedList<IntentEvent> lastEvents = new LinkedList<>();
37 + private static final int LAST_EVENTS_MAX_N = 100;
38 +
39 + //
40 + // Metrics
41 + //
42 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
43 + protected MetricsService metricsService;
44 + //
45 + private static final String COMPONENT_NAME = "Intent";
46 + private static final String FEATURE_SUBMITTED_NAME = "Submitted";
47 + private static final String FEATURE_INSTALLED_NAME = "Installed";
48 + private static final String FEATURE_WITHDRAW_REQUESTED_NAME =
49 + "WithdrawRequested";
50 + private static final String FEATURE_WITHDRAWN_NAME = "Withdrawn";
51 + private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
52 + private static final String METER_RATE_NAME = "Rate";
53 + //
54 + private MetricsComponent metricsComponent;
55 + private MetricsFeature metricsFeatureSubmitted;
56 + private MetricsFeature metricsFeatureInstalled;
57 + private MetricsFeature metricsFeatureWithdrawRequested;
58 + private MetricsFeature metricsFeatureWithdrawn;
59 + //
60 + // Timestamps:
61 + // - Intent Submitted API operation (ms from the Epoch)
62 + // - Intent Installed operation completion (ms from the Epoch)
63 + // - Intent Withdraw Requested API operation (ms from the Epoch)
64 + // - Intent Withdrawn operation completion (ms from the Epoch)
65 + //
66 + private volatile long intentSubmittedTimestampEpochMs = 0;
67 + private volatile long intentInstalledTimestampEpochMs = 0;
68 + private volatile long intentWithdrawRequestedTimestampEpochMs = 0;
69 + private volatile long intentWithdrawnTimestampEpochMs = 0;
70 + //
71 + private Gauge<Long> intentSubmittedTimestampEpochMsGauge;
72 + private Gauge<Long> intentInstalledTimestampEpochMsGauge;
73 + private Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge;
74 + private Gauge<Long> intentWithdrawnTimestampEpochMsGauge;
75 + //
76 + // Rate meters:
77 + // - Rate of the Submitted Intent API operations
78 + // - Rate of the Installed Intent operations
79 + // - Rate of the Withdrawn Requested Intent API operations
80 + // - Rate of the Withdrawn Intent operations
81 + //
82 + private Meter intentSubmittedRateMeter;
83 + private Meter intentInstalledRateMeter;
84 + private Meter intentWithdrawRequestedRateMeter;
85 + private Meter intentWithdrawnRateMeter;
86 +
87 + @Activate
88 + protected void activate() {
89 + clear();
90 + registerMetrics();
91 + intentService.addListener(this);
92 + log.info("ONOS Intent Metrics started.");
93 + }
94 +
95 + @Deactivate
96 + public void deactivate() {
97 + intentService.removeListener(this);
98 + removeMetrics();
99 + clear();
100 + log.info("ONOS Intent Metrics stopped.");
101 + }
102 +
103 + @Override
104 + public List<IntentEvent> getEvents() {
105 + synchronized (lastEvents) {
106 + return ImmutableList.<IntentEvent>copyOf(lastEvents);
107 + }
108 + }
109 +
110 + @Override
111 + public Gauge<Long> intentSubmittedTimestampEpochMsGauge() {
112 + return intentSubmittedTimestampEpochMsGauge;
113 + }
114 +
115 + @Override
116 + public Gauge<Long> intentInstalledTimestampEpochMsGauge() {
117 + return intentInstalledTimestampEpochMsGauge;
118 + }
119 +
120 + @Override
121 + public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge() {
122 + return intentWithdrawRequestedTimestampEpochMsGauge;
123 + }
124 +
125 + @Override
126 + public Gauge<Long> intentWithdrawnTimestampEpochMsGauge() {
127 + return intentWithdrawnTimestampEpochMsGauge;
128 + }
129 +
130 + @Override
131 + public Meter intentSubmittedRateMeter() {
132 + return intentSubmittedRateMeter;
133 + }
134 +
135 + @Override
136 + public Meter intentInstalledRateMeter() {
137 + return intentInstalledRateMeter;
138 + }
139 +
140 + @Override
141 + public Meter intentWithdrawRequestedRateMeter() {
142 + return intentWithdrawRequestedRateMeter;
143 + }
144 +
145 + @Override
146 + public Meter intentWithdrawnRateMeter() {
147 + return intentWithdrawnRateMeter;
148 + }
149 +
150 + @Override
151 + public void event(IntentEvent event) {
152 + synchronized (lastEvents) {
153 + //
154 + // TODO: The processing below is incomplete: we don't have
155 + // an event equivalent of "Withdraw Requested"
156 + //
157 + switch (event.type()) {
158 + case SUBMITTED:
159 + intentSubmittedTimestampEpochMs = System.currentTimeMillis();
160 + intentSubmittedRateMeter.mark(1);
161 + break;
162 + case INSTALLED:
163 + intentInstalledTimestampEpochMs = System.currentTimeMillis();
164 + intentInstalledRateMeter.mark(1);
165 + break;
166 + case FAILED:
167 + // TODO: Just ignore?
168 + break;
169 + /*
170 + case WITHDRAW_REQUESTED:
171 + intentWithdrawRequestedTimestampEpochMs =
172 + System.currentTimeMillis();
173 + intentWithdrawRequestedRateMeter.mark(1);
174 + break;
175 + */
176 + case WITHDRAWN:
177 + intentWithdrawnTimestampEpochMs = System.currentTimeMillis();
178 + intentWithdrawnRateMeter.mark(1);
179 + break;
180 + default:
181 + break;
182 + }
183 +
184 + //
185 + // Keep only the last N events, where N = LAST_EVENTS_MAX_N
186 + //
187 + while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
188 + lastEvents.remove();
189 + }
190 + lastEvents.add(event);
191 + }
192 +
193 + log.debug("Intent Event: time = {} type = {} event = {}",
194 + event.time(), event.type(), event);
195 + }
196 +
197 + /**
198 + * Clears the internal state.
199 + */
200 + private void clear() {
201 + synchronized (lastEvents) {
202 + intentSubmittedTimestampEpochMs = 0;
203 + intentInstalledTimestampEpochMs = 0;
204 + intentWithdrawRequestedTimestampEpochMs = 0;
205 + intentWithdrawnTimestampEpochMs = 0;
206 + lastEvents.clear();
207 + }
208 + }
209 +
210 + /**
211 + * Registers the metrics.
212 + */
213 + private void registerMetrics() {
214 + metricsComponent = metricsService.registerComponent(COMPONENT_NAME);
215 + //
216 + metricsFeatureSubmitted =
217 + metricsComponent.registerFeature(FEATURE_SUBMITTED_NAME);
218 + metricsFeatureInstalled =
219 + metricsComponent.registerFeature(FEATURE_INSTALLED_NAME);
220 + metricsFeatureWithdrawRequested =
221 + metricsComponent.registerFeature(FEATURE_WITHDRAW_REQUESTED_NAME);
222 + metricsFeatureWithdrawn =
223 + metricsComponent.registerFeature(FEATURE_WITHDRAWN_NAME);
224 + //
225 + intentSubmittedTimestampEpochMsGauge =
226 + metricsService.registerMetric(metricsComponent,
227 + metricsFeatureSubmitted,
228 + GAUGE_TIMESTAMP_NAME,
229 + new Gauge<Long>() {
230 + @Override
231 + public Long getValue() {
232 + return intentSubmittedTimestampEpochMs;
233 + }
234 + });
235 + //
236 + intentInstalledTimestampEpochMsGauge =
237 + metricsService.registerMetric(metricsComponent,
238 + metricsFeatureInstalled,
239 + GAUGE_TIMESTAMP_NAME,
240 + new Gauge<Long>() {
241 + @Override
242 + public Long getValue() {
243 + return intentInstalledTimestampEpochMs;
244 + }
245 + });
246 + //
247 + intentWithdrawRequestedTimestampEpochMsGauge =
248 + metricsService.registerMetric(metricsComponent,
249 + metricsFeatureWithdrawRequested,
250 + GAUGE_TIMESTAMP_NAME,
251 + new Gauge<Long>() {
252 + @Override
253 + public Long getValue() {
254 + return intentWithdrawRequestedTimestampEpochMs;
255 + }
256 + });
257 + //
258 + intentWithdrawnTimestampEpochMsGauge =
259 + metricsService.registerMetric(metricsComponent,
260 + metricsFeatureWithdrawn,
261 + GAUGE_TIMESTAMP_NAME,
262 + new Gauge<Long>() {
263 + @Override
264 + public Long getValue() {
265 + return intentWithdrawnTimestampEpochMs;
266 + }
267 + });
268 + //
269 + intentSubmittedRateMeter =
270 + metricsService.createMeter(metricsComponent,
271 + metricsFeatureSubmitted,
272 + METER_RATE_NAME);
273 + //
274 + intentInstalledRateMeter =
275 + metricsService.createMeter(metricsComponent,
276 + metricsFeatureInstalled,
277 + METER_RATE_NAME);
278 + //
279 + intentWithdrawRequestedRateMeter =
280 + metricsService.createMeter(metricsComponent,
281 + metricsFeatureWithdrawRequested,
282 + METER_RATE_NAME);
283 + //
284 + intentWithdrawnRateMeter =
285 + metricsService.createMeter(metricsComponent,
286 + metricsFeatureWithdrawn,
287 + METER_RATE_NAME);
288 + }
289 +
290 + /**
291 + * Removes the metrics.
292 + */
293 + private void removeMetrics() {
294 + metricsService.removeMetric(metricsComponent,
295 + metricsFeatureSubmitted,
296 + GAUGE_TIMESTAMP_NAME);
297 + metricsService.removeMetric(metricsComponent,
298 + metricsFeatureInstalled,
299 + GAUGE_TIMESTAMP_NAME);
300 + metricsService.removeMetric(metricsComponent,
301 + metricsFeatureWithdrawRequested,
302 + GAUGE_TIMESTAMP_NAME);
303 + metricsService.removeMetric(metricsComponent,
304 + metricsFeatureWithdrawn,
305 + GAUGE_TIMESTAMP_NAME);
306 + metricsService.removeMetric(metricsComponent,
307 + metricsFeatureSubmitted,
308 + METER_RATE_NAME);
309 + metricsService.removeMetric(metricsComponent,
310 + metricsFeatureInstalled,
311 + METER_RATE_NAME);
312 + metricsService.removeMetric(metricsComponent,
313 + metricsFeatureWithdrawRequested,
314 + METER_RATE_NAME);
315 + metricsService.removeMetric(metricsComponent,
316 + metricsFeatureWithdrawn,
317 + METER_RATE_NAME);
318 + }
319 +}
1 +package org.onlab.onos.metrics.intent;
2 +
3 +import java.util.List;
4 +
5 +import com.codahale.metrics.Gauge;
6 +import com.codahale.metrics.Meter;
7 +import org.onlab.onos.net.intent.IntentEvent;
8 +
9 +/**
10 + * Service interface exported by IntentMetrics.
11 + */
12 +public interface IntentMetricsService {
13 + /**
14 + * Gets the last saved intent events.
15 + *
16 + * @return the last saved intent events.
17 + */
18 + public List<IntentEvent> getEvents();
19 +
20 + /**
21 + * Gets the Metrics' Gauge for the intent SUBMITTED event timestamp
22 + * (ms from the epoch).
23 + *
24 + * @return the Metrics' Gauge for the intent SUBMITTED event timestamp
25 + * (ms from the epoch)
26 + */
27 + public Gauge<Long> intentSubmittedTimestampEpochMsGauge();
28 +
29 + /**
30 + * Gets the Metrics' Gauge for the intent INSTALLED event timestamp
31 + * (ms from the epoch).
32 + *
33 + * @return the Metrics' Gauge for the intent INSTALLED event timestamp
34 + * (ms from the epoch)
35 + */
36 + public Gauge<Long> intentInstalledTimestampEpochMsGauge();
37 +
38 + /**
39 + * Gets the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
40 + * timestamp (ms from the epoch).
41 + *
42 + * TODO: This intent event is not implemented yet.
43 + *
44 + * @return the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
45 + * timestamp (ms from the epoch)
46 + */
47 + public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge();
48 +
49 + /**
50 + * Gets the Metrics' Gauge for the intent WITHDRAWN event timestamp
51 + * (ms from the epoch).
52 + *
53 + * @return the Metrics' Gauge for the intent WITHDRAWN event timestamp
54 + * (ms from the epoch)
55 + */
56 + public Gauge<Long> intentWithdrawnTimestampEpochMsGauge();
57 +
58 + /**
59 + * Gets the Metrics' Meter for the submitted intents event rate.
60 + *
61 + * @return the Metrics' Meter for the submitted intents event rate
62 + */
63 + public Meter intentSubmittedRateMeter();
64 +
65 + /**
66 + * Gets the Metrics' Meter for the installed intents event rate.
67 + *
68 + * @return the Metrics' Meter for the installed intent event rate
69 + */
70 + public Meter intentInstalledRateMeter();
71 +
72 + /**
73 + * Gets the Metrics' Meter for the withdraw requested intents event rate.
74 + *
75 + * @return the Metrics' Meter for the withdraw requested intents event rate
76 + */
77 + public Meter intentWithdrawRequestedRateMeter();
78 +
79 + /**
80 + * Gets the Metrics' Meter for the withdraw completed intents event rate.
81 + *
82 + * @return the Metrics' Meter for the withdraw completed intents event rate
83 + */
84 + public Meter intentWithdrawnRateMeter();
85 +}
1 +package org.onlab.onos.metrics.intent.cli;
2 +
3 +import java.util.List;
4 +
5 +import com.fasterxml.jackson.databind.JsonNode;
6 +import com.fasterxml.jackson.databind.ObjectMapper;
7 +import com.fasterxml.jackson.databind.node.ArrayNode;
8 +import com.fasterxml.jackson.databind.node.ObjectNode;
9 +import org.apache.karaf.shell.commands.Command;
10 +import org.onlab.onos.cli.AbstractShellCommand;
11 +import org.onlab.onos.metrics.intent.IntentMetricsService;
12 +import org.onlab.onos.net.intent.IntentEvent;
13 +
14 +/**
15 + * Command to show the list of last intent events.
16 + */
17 +@Command(scope = "onos", name = "intents-events",
18 + description = "Lists the last intent events")
19 +public class IntentEventsListCommand extends AbstractShellCommand {
20 +
21 + private static final String FORMAT_EVENT = "Event=%s";
22 +
23 + @Override
24 + protected void execute() {
25 + IntentMetricsService service = get(IntentMetricsService.class);
26 +
27 + if (outputJson()) {
28 + print("%s", json(service.getEvents()));
29 + } else {
30 + for (IntentEvent event : service.getEvents()) {
31 + print(FORMAT_EVENT, event);
32 + print(""); // Extra empty line for clarity
33 + }
34 + }
35 + }
36 +
37 + /**
38 + * Produces a JSON array of intent events.
39 + *
40 + * @param intentEvents the intent events with the data
41 + * @return JSON array with the intent events
42 + */
43 + private JsonNode json(List<IntentEvent> intentEvents) {
44 + ObjectMapper mapper = new ObjectMapper();
45 + ArrayNode result = mapper.createArrayNode();
46 +
47 + for (IntentEvent event : intentEvents) {
48 + result.add(json(mapper, event));
49 + }
50 + return result;
51 + }
52 +
53 + /**
54 + * Produces JSON object for a intent event.
55 + *
56 + * @param mapper the JSON object mapper to use
57 + * @param intentEvent the intent event with the data
58 + * @return JSON object for the intent event
59 + */
60 + private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) {
61 + ObjectNode result = mapper.createObjectNode();
62 +
63 + result.put("time", intentEvent.time())
64 + .put("type", intentEvent.type().toString())
65 + .put("event", intentEvent.toString());
66 + return result;
67 + }
68 +}
1 +package org.onlab.onos.metrics.intent.cli;
2 +
3 +import java.io.IOException;
4 +import java.util.concurrent.TimeUnit;
5 +
6 +import com.codahale.metrics.Gauge;
7 +import com.codahale.metrics.Meter;
8 +import com.codahale.metrics.json.MetricsModule;
9 +import com.fasterxml.jackson.core.JsonProcessingException;
10 +import com.fasterxml.jackson.databind.JsonNode;
11 +import com.fasterxml.jackson.databind.ObjectMapper;
12 +import com.fasterxml.jackson.databind.node.ObjectNode;
13 +import org.apache.karaf.shell.commands.Command;
14 +import org.onlab.onos.cli.AbstractShellCommand;
15 +import org.onlab.onos.metrics.intent.IntentMetricsService;
16 +
17 +/**
18 + * Command to show the intent events metrics.
19 + */
20 +@Command(scope = "onos", name = "intents-events-metrics",
21 + description = "Lists intent events metrics")
22 +public class IntentEventsMetricsCommand extends AbstractShellCommand {
23 +
24 + private static final String FORMAT_GAUGE =
25 + "Intent %s Event Timestamp (ms from epoch)=%d";
26 + private static final String FORMAT_METER =
27 + "Intent %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
28 +
29 + @Override
30 + protected void execute() {
31 + IntentMetricsService service = get(IntentMetricsService.class);
32 + Gauge<Long> gauge;
33 + Meter meter;
34 +
35 + if (outputJson()) {
36 + ObjectMapper mapper = new ObjectMapper()
37 + .registerModule(new MetricsModule(TimeUnit.SECONDS,
38 + TimeUnit.MILLISECONDS,
39 + false));
40 + ObjectNode result = mapper.createObjectNode();
41 + //
42 + gauge = service.intentSubmittedTimestampEpochMsGauge();
43 + result.put("intentSubmittedTimestamp", json(mapper, gauge));
44 + gauge = service.intentInstalledTimestampEpochMsGauge();
45 + result.put("intentInstalledTimestamp", json(mapper, gauge));
46 + gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
47 + result.put("intentWithdrawRequestedTimestamp",
48 + json(mapper, gauge));
49 + gauge = service.intentWithdrawnTimestampEpochMsGauge();
50 + result.put("intentWithdrawnTimestamp", json(mapper, gauge));
51 + //
52 + meter = service.intentSubmittedRateMeter();
53 + result.put("intentSubmittedRate", json(mapper, meter));
54 + meter = service.intentInstalledRateMeter();
55 + result.put("intentInstalledRate", json(mapper, meter));
56 + meter = service.intentWithdrawRequestedRateMeter();
57 + result.put("intentWithdrawRequestedRate", json(mapper, meter));
58 + meter = service.intentWithdrawnRateMeter();
59 + result.put("intentWithdrawnRate", json(mapper, meter));
60 + //
61 + print("%s", result);
62 + } else {
63 + gauge = service.intentSubmittedTimestampEpochMsGauge();
64 + printGauge("Submitted", gauge);
65 + gauge = service.intentInstalledTimestampEpochMsGauge();
66 + printGauge("Installed", gauge);
67 + gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
68 + printGauge("Withdraw Requested", gauge);
69 + gauge = service.intentWithdrawnTimestampEpochMsGauge();
70 + printGauge("Withdrawn", gauge);
71 + //
72 + meter = service.intentSubmittedRateMeter();
73 + printMeter("Submitted", meter);
74 + meter = service.intentInstalledRateMeter();
75 + printMeter("Installed", meter);
76 + meter = service.intentWithdrawRequestedRateMeter();
77 + printMeter("Withdraw Requested", meter);
78 + meter = service.intentWithdrawnRateMeter();
79 + printMeter("Withdrawn", meter);
80 + }
81 + }
82 +
83 + /**
84 + * Produces JSON node for an Object.
85 + *
86 + * @param mapper the JSON object mapper to use
87 + * @param object the Object with the data
88 + * @return JSON node for the Object
89 + */
90 + private JsonNode json(ObjectMapper mapper, Object object) {
91 + //
92 + // NOTE: The API for custom serializers is incomplete,
93 + // hence we have to parse the JSON string to create JsonNode.
94 + //
95 + try {
96 + final String objectJson = mapper.writeValueAsString(object);
97 + JsonNode objectNode = mapper.readTree(objectJson);
98 + return objectNode;
99 + } catch (JsonProcessingException e) {
100 + log.error("Error writing value as JSON string", e);
101 + } catch (IOException e) {
102 + log.error("Error writing value as JSON string", e);
103 + }
104 + return null;
105 + }
106 +
107 + /**
108 + * Prints a Gauge.
109 + *
110 + * @param operationStr the string with the intent operation to print
111 + * @param gauge the Gauge to print
112 + */
113 + private void printGauge(String operationStr, Gauge<Long> gauge) {
114 + print(FORMAT_GAUGE, operationStr, gauge.getValue());
115 + }
116 +
117 + /**
118 + * Prints a Meter.
119 + *
120 + * @param operationStr the string with the intent operation to print
121 + * @param meter the Meter to print
122 + */
123 + private void printMeter(String operationStr, Meter meter) {
124 + TimeUnit rateUnit = TimeUnit.SECONDS;
125 + double rateFactor = rateUnit.toSeconds(1);
126 + print(FORMAT_METER, operationStr, meter.getCount(),
127 + meter.getMeanRate() * rateFactor,
128 + meter.getOneMinuteRate() * rateFactor,
129 + meter.getFiveMinuteRate() * rateFactor,
130 + meter.getFifteenMinuteRate() * rateFactor);
131 + }
132 +}
1 +/**
2 + * ONOS Intent Metrics Application that collects intent-related metrics.
3 + */
4 +package org.onlab.onos.metrics.intent;
1 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
2 +
3 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
4 + <command>
5 + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsListCommand"/>
6 + </command>
7 + <command>
8 + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsMetricsCommand"/>
9 + </command>
10 + </command-bundle>
11 +
12 +</blueprint>
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 <description>ONOS metrics applications</description> 17 <description>ONOS metrics applications</description>
18 18
19 <modules> 19 <modules>
20 + <module>intent</module>
20 <module>topology</module> 21 <module>topology</module>
21 </modules> 22 </modules>
22 23
......
...@@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent; ...@@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent;
18 import org.onlab.metrics.MetricsFeature; 18 import org.onlab.metrics.MetricsFeature;
19 import org.onlab.metrics.MetricsService; 19 import org.onlab.metrics.MetricsService;
20 import org.onlab.onos.event.Event; 20 import org.onlab.onos.event.Event;
21 +import org.onlab.onos.net.device.DeviceEvent;
22 +import org.onlab.onos.net.device.DeviceListener;
23 +import org.onlab.onos.net.device.DeviceService;
24 +import org.onlab.onos.net.host.HostEvent;
25 +import org.onlab.onos.net.host.HostListener;
26 +import org.onlab.onos.net.host.HostService;
27 +import org.onlab.onos.net.link.LinkEvent;
28 +import org.onlab.onos.net.link.LinkListener;
29 +import org.onlab.onos.net.link.LinkService;
21 import org.onlab.onos.net.topology.TopologyEvent; 30 import org.onlab.onos.net.topology.TopologyEvent;
22 import org.onlab.onos.net.topology.TopologyListener; 31 import org.onlab.onos.net.topology.TopologyListener;
23 import org.onlab.onos.net.topology.TopologyService; 32 import org.onlab.onos.net.topology.TopologyService;
...@@ -28,14 +37,26 @@ import org.slf4j.Logger; ...@@ -28,14 +37,26 @@ import org.slf4j.Logger;
28 */ 37 */
29 @Component(immediate = true) 38 @Component(immediate = true)
30 @Service 39 @Service
31 -public class TopologyMetrics implements TopologyMetricsService, 40 +public class TopologyMetrics implements TopologyMetricsService {
32 - TopologyListener {
33 private static final Logger log = getLogger(TopologyMetrics.class); 41 private static final Logger log = getLogger(TopologyMetrics.class);
34 42
35 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 43 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
44 + protected DeviceService deviceService;
45 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 + protected HostService hostService;
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected LinkService linkService;
49 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
36 protected TopologyService topologyService; 50 protected TopologyService topologyService;
37 - private LinkedList<TopologyEvent> lastEvents = new LinkedList<>(); 51 +
38 - private static final int LAST_EVENTS_MAX_N = 10; 52 + private LinkedList<Event> lastEvents = new LinkedList<>();
53 + private static final int LAST_EVENTS_MAX_N = 100;
54 +
55 + private final DeviceListener deviceListener = new InnerDeviceListener();
56 + private final HostListener hostListener = new InnerHostListener();
57 + private final LinkListener linkListener = new InnerLinkListener();
58 + private final TopologyListener topologyListener =
59 + new InnerTopologyListener();
39 60
40 // 61 //
41 // Metrics 62 // Metrics
...@@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService,
61 protected void activate() { 82 protected void activate() {
62 clear(); 83 clear();
63 registerMetrics(); 84 registerMetrics();
64 - topologyService.addListener(this); 85 +
86 + // Register for all topology-related events
87 + deviceService.addListener(deviceListener);
88 + hostService.addListener(hostListener);
89 + linkService.addListener(linkListener);
90 + topologyService.addListener(topologyListener);
91 +
65 log.info("ONOS Topology Metrics started."); 92 log.info("ONOS Topology Metrics started.");
66 } 93 }
67 94
68 @Deactivate 95 @Deactivate
69 public void deactivate() { 96 public void deactivate() {
70 - topologyService.removeListener(this); 97 + // De-register from all topology-related events
98 + deviceService.removeListener(deviceListener);
99 + hostService.removeListener(hostListener);
100 + linkService.removeListener(linkListener);
101 + topologyService.removeListener(topologyListener);
102 +
71 removeMetrics(); 103 removeMetrics();
72 clear(); 104 clear();
73 log.info("ONOS Topology Metrics stopped."); 105 log.info("ONOS Topology Metrics stopped.");
74 } 106 }
75 107
76 @Override 108 @Override
77 - public List<TopologyEvent> getEvents() { 109 + public List<Event> getEvents() {
78 synchronized (lastEvents) { 110 synchronized (lastEvents) {
79 - return ImmutableList.<TopologyEvent>copyOf(lastEvents); 111 + return ImmutableList.<Event>copyOf(lastEvents);
80 } 112 }
81 } 113 }
82 114
...@@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService,
90 return eventRateMeter; 122 return eventRateMeter;
91 } 123 }
92 124
93 - @Override 125 + /**
94 - public void event(TopologyEvent event) { 126 + * Records an event.
127 + *
128 + * @param event the event to record
129 + * @param updateEventRateMeter if true, update the Event Rate Meter
130 + */
131 + private void recordEvent(Event event, boolean updateEventRateMeter) {
132 + synchronized (lastEvents) {
95 lastEventTimestampEpochMs = System.currentTimeMillis(); 133 lastEventTimestampEpochMs = System.currentTimeMillis();
96 - // 134 + if (updateEventRateMeter) {
97 - // NOTE: If we want to count each "reason" as a separate event,
98 - // then we should use 'event.reason().size()' instead of '1' to
99 - // mark the meter below.
100 - //
101 eventRateMeter.mark(1); 135 eventRateMeter.mark(1);
102 -
103 - log.debug("Topology Event: time = {} type = {} subject = {}",
104 - event.time(), event.type(), event.subject());
105 - for (Event reason : event.reasons()) {
106 - log.debug("Topology Event Reason: time = {} type = {} subject = {}",
107 - reason.time(), reason.type(), reason.subject());
108 } 136 }
109 137
110 // 138 //
111 // Keep only the last N events, where N = LAST_EVENTS_MAX_N 139 // Keep only the last N events, where N = LAST_EVENTS_MAX_N
112 // 140 //
113 - synchronized (lastEvents) {
114 while (lastEvents.size() >= LAST_EVENTS_MAX_N) { 141 while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
115 lastEvents.remove(); 142 lastEvents.remove();
116 } 143 }
...@@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService,
119 } 146 }
120 147
121 /** 148 /**
149 + * Inner Device Event Listener class.
150 + */
151 + private class InnerDeviceListener implements DeviceListener {
152 + @Override
153 + public void event(DeviceEvent event) {
154 + recordEvent(event, true);
155 + log.debug("Device Event: time = {} type = {} event = {}",
156 + event.time(), event.type(), event);
157 + }
158 + }
159 +
160 + /**
161 + * Inner Host Event Listener class.
162 + */
163 + private class InnerHostListener implements HostListener {
164 + @Override
165 + public void event(HostEvent event) {
166 + recordEvent(event, true);
167 + log.debug("Host Event: time = {} type = {} event = {}",
168 + event.time(), event.type(), event);
169 + }
170 + }
171 +
172 + /**
173 + * Inner Link Event Listener class.
174 + */
175 + private class InnerLinkListener implements LinkListener {
176 + @Override
177 + public void event(LinkEvent event) {
178 + recordEvent(event, true);
179 + log.debug("Link Event: time = {} type = {} event = {}",
180 + event.time(), event.type(), event);
181 + }
182 + }
183 +
184 + /**
185 + * Inner Topology Event Listener class.
186 + */
187 + private class InnerTopologyListener implements TopologyListener {
188 + @Override
189 + public void event(TopologyEvent event) {
190 + //
191 + // NOTE: Don't update the eventRateMeter, because the real
192 + // events are already captured/counted.
193 + //
194 + recordEvent(event, false);
195 + log.debug("Topology Event: time = {} type = {} event = {}",
196 + event.time(), event.type(), event);
197 + for (Event reason : event.reasons()) {
198 + log.debug("Topology Event Reason: time = {} type = {} event = {}",
199 + reason.time(), reason.type(), reason);
200 + }
201 + }
202 + }
203 +
204 + /**
122 * Clears the internal state. 205 * Clears the internal state.
123 */ 206 */
124 private void clear() { 207 private void clear() {
125 - lastEventTimestampEpochMs = 0;
126 synchronized (lastEvents) { 208 synchronized (lastEvents) {
209 + lastEventTimestampEpochMs = 0;
127 lastEvents.clear(); 210 lastEvents.clear();
128 } 211 }
129 } 212 }
......
...@@ -4,7 +4,7 @@ import java.util.List; ...@@ -4,7 +4,7 @@ import java.util.List;
4 4
5 import com.codahale.metrics.Gauge; 5 import com.codahale.metrics.Gauge;
6 import com.codahale.metrics.Meter; 6 import com.codahale.metrics.Meter;
7 -import org.onlab.onos.net.topology.TopologyEvent; 7 +import org.onlab.onos.event.Event;
8 8
9 /** 9 /**
10 * Service interface exported by TopologyMetrics. 10 * Service interface exported by TopologyMetrics.
...@@ -15,7 +15,7 @@ public interface TopologyMetricsService { ...@@ -15,7 +15,7 @@ public interface TopologyMetricsService {
15 * 15 *
16 * @return the last saved topology events. 16 * @return the last saved topology events.
17 */ 17 */
18 - public List<TopologyEvent> getEvents(); 18 + public List<Event> getEvents();
19 19
20 /** 20 /**
21 * Gets the Metrics' Gauge for the last topology event timestamp 21 * Gets the Metrics' Gauge for the last topology event timestamp
......
...@@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; ...@@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent;
19 description = "Lists the last topology events") 19 description = "Lists the last topology events")
20 public class TopologyEventsListCommand extends AbstractShellCommand { 20 public class TopologyEventsListCommand extends AbstractShellCommand {
21 21
22 - private static final String FORMAT_EVENT = 22 + private static final String FORMAT_EVENT = "Event=%s";
23 - "Topology Event time=%d type=%s subject=%s"; 23 + private static final String FORMAT_REASON = " Reason=%s";
24 - private static final String FORMAT_REASON =
25 - " Reason time=%d type=%s subject=%s";
26 24
27 @Override 25 @Override
28 protected void execute() { 26 protected void execute() {
...@@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
31 if (outputJson()) { 29 if (outputJson()) {
32 print("%s", json(service.getEvents())); 30 print("%s", json(service.getEvents()));
33 } else { 31 } else {
34 - for (TopologyEvent event : service.getEvents()) { 32 + for (Event event : service.getEvents()) {
35 - print(FORMAT_EVENT, event.time(), event.type(), 33 + print(FORMAT_EVENT, event);
36 - event.subject()); 34 + if (event instanceof TopologyEvent) {
37 - for (Event reason : event.reasons()) { 35 + TopologyEvent topologyEvent = (TopologyEvent) event;
38 - print(FORMAT_REASON, reason.time(), reason.type(), 36 + for (Event reason : topologyEvent.reasons()) {
39 - reason.subject()); 37 + print(FORMAT_REASON, reason);
38 + }
40 } 39 }
41 print(""); // Extra empty line for clarity 40 print(""); // Extra empty line for clarity
42 } 41 }
...@@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
46 /** 45 /**
47 * Produces a JSON array of topology events. 46 * Produces a JSON array of topology events.
48 * 47 *
49 - * @param topologyEvents the topology events with the data 48 + * @param events the topology events with the data
50 * @return JSON array with the topology events 49 * @return JSON array with the topology events
51 */ 50 */
52 - private JsonNode json(List<TopologyEvent> topologyEvents) { 51 + private JsonNode json(List<Event> events) {
53 ObjectMapper mapper = new ObjectMapper(); 52 ObjectMapper mapper = new ObjectMapper();
54 ArrayNode result = mapper.createArrayNode(); 53 ArrayNode result = mapper.createArrayNode();
55 54
56 - for (TopologyEvent event : topologyEvents) { 55 + for (Event event : events) {
57 result.add(json(mapper, event)); 56 result.add(json(mapper, event));
58 } 57 }
59 return result; 58 return result;
...@@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
66 * @param topologyEvent the topology event with the data 65 * @param topologyEvent the topology event with the data
67 * @return JSON object for the topology event 66 * @return JSON object for the topology event
68 */ 67 */
69 - private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) { 68 + private ObjectNode json(ObjectMapper mapper, Event event) {
70 ObjectNode result = mapper.createObjectNode(); 69 ObjectNode result = mapper.createObjectNode();
71 - ArrayNode reasons = mapper.createArrayNode();
72 70
71 + result.put("time", event.time())
72 + .put("type", event.type().toString())
73 + .put("event", event.toString());
74 +
75 + // Add the reasons if a TopologyEvent
76 + if (event instanceof TopologyEvent) {
77 + TopologyEvent topologyEvent = (TopologyEvent) event;
78 + ArrayNode reasons = mapper.createArrayNode();
73 for (Event reason : topologyEvent.reasons()) { 79 for (Event reason : topologyEvent.reasons()) {
74 reasons.add(json(mapper, reason)); 80 reasons.add(json(mapper, reason));
75 } 81 }
76 - result.put("time", topologyEvent.time()) 82 + result.put("reasons", reasons);
77 - .put("type", topologyEvent.type().toString())
78 - .put("subject", topologyEvent.subject().toString())
79 - .put("reasons", reasons);
80 - return result;
81 } 83 }
82 84
83 - /**
84 - * Produces JSON object for a generic event.
85 - *
86 - * @param event the generic event with the data
87 - * @return JSON object for the generic event
88 - */
89 - private ObjectNode json(ObjectMapper mapper, Event event) {
90 - ObjectNode result = mapper.createObjectNode();
91 -
92 - result.put("time", event.time())
93 - .put("type", event.type().toString())
94 - .put("subject", event.subject().toString());
95 return result; 85 return result;
96 } 86 }
97 } 87 }
......
...@@ -199,9 +199,16 @@ ...@@ -199,9 +199,16 @@
199 199
200 <feature name="onos-app-metrics" version="1.0.0" 200 <feature name="onos-app-metrics" version="1.0.0"
201 description="ONOS metrics applications"> 201 description="ONOS metrics applications">
202 + <feature>onos-app-metrics-intent</feature>
202 <feature>onos-app-metrics-topology</feature> 203 <feature>onos-app-metrics-topology</feature>
203 </feature> 204 </feature>
204 205
206 + <feature name="onos-app-metrics-intent" version="1.0.0"
207 + description="ONOS intent metrics application">
208 + <feature>onos-api</feature>
209 + <bundle>mvn:org.onlab.onos/onos-app-metrics-intent/1.0.0-SNAPSHOT</bundle>
210 + </feature>
211 +
205 <feature name="onos-app-metrics-topology" version="1.0.0" 212 <feature name="onos-app-metrics-topology" version="1.0.0"
206 description="ONOS topology metrics application"> 213 description="ONOS topology metrics application">
207 <feature>onos-api</feature> 214 <feature>onos-api</feature>
......