Merge remote-tracking branch 'origin/master'
diff --git a/apps/metrics/intent/pom.xml b/apps/metrics/intent/pom.xml
new file mode 100644
index 0000000..4d42065
--- /dev/null
+++ b/apps/metrics/intent/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-app-metrics</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-metrics-intent</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS intent metrics application</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java
new file mode 100644
index 0000000..38366b6
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java
@@ -0,0 +1,319 @@
+package org.onlab.onos.metrics.intent;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.metrics.MetricsComponent;
+import org.onlab.metrics.MetricsFeature;
+import org.onlab.metrics.MetricsService;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentListener;
+import org.onlab.onos.net.intent.IntentService;
+import org.slf4j.Logger;
+
+/**
+ * ONOS Intent Metrics Application that collects intent-related metrics.
+ */
+@Component(immediate = true)
+@Service
+public class IntentMetrics implements IntentMetricsService,
+ IntentListener {
+ private static final Logger log = getLogger(IntentMetrics.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+ private LinkedList<IntentEvent> lastEvents = new LinkedList<>();
+ private static final int LAST_EVENTS_MAX_N = 100;
+
+ //
+ // Metrics
+ //
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MetricsService metricsService;
+ //
+ private static final String COMPONENT_NAME = "Intent";
+ private static final String FEATURE_SUBMITTED_NAME = "Submitted";
+ private static final String FEATURE_INSTALLED_NAME = "Installed";
+ private static final String FEATURE_WITHDRAW_REQUESTED_NAME =
+ "WithdrawRequested";
+ private static final String FEATURE_WITHDRAWN_NAME = "Withdrawn";
+ private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
+ private static final String METER_RATE_NAME = "Rate";
+ //
+ private MetricsComponent metricsComponent;
+ private MetricsFeature metricsFeatureSubmitted;
+ private MetricsFeature metricsFeatureInstalled;
+ private MetricsFeature metricsFeatureWithdrawRequested;
+ private MetricsFeature metricsFeatureWithdrawn;
+ //
+ // Timestamps:
+ // - Intent Submitted API operation (ms from the Epoch)
+ // - Intent Installed operation completion (ms from the Epoch)
+ // - Intent Withdraw Requested API operation (ms from the Epoch)
+ // - Intent Withdrawn operation completion (ms from the Epoch)
+ //
+ private volatile long intentSubmittedTimestampEpochMs = 0;
+ private volatile long intentInstalledTimestampEpochMs = 0;
+ private volatile long intentWithdrawRequestedTimestampEpochMs = 0;
+ private volatile long intentWithdrawnTimestampEpochMs = 0;
+ //
+ private Gauge<Long> intentSubmittedTimestampEpochMsGauge;
+ private Gauge<Long> intentInstalledTimestampEpochMsGauge;
+ private Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge;
+ private Gauge<Long> intentWithdrawnTimestampEpochMsGauge;
+ //
+ // Rate meters:
+ // - Rate of the Submitted Intent API operations
+ // - Rate of the Installed Intent operations
+ // - Rate of the Withdrawn Requested Intent API operations
+ // - Rate of the Withdrawn Intent operations
+ //
+ private Meter intentSubmittedRateMeter;
+ private Meter intentInstalledRateMeter;
+ private Meter intentWithdrawRequestedRateMeter;
+ private Meter intentWithdrawnRateMeter;
+
+ @Activate
+ protected void activate() {
+ clear();
+ registerMetrics();
+ intentService.addListener(this);
+ log.info("ONOS Intent Metrics started.");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentService.removeListener(this);
+ removeMetrics();
+ clear();
+ log.info("ONOS Intent Metrics stopped.");
+ }
+
+ @Override
+ public List<IntentEvent> getEvents() {
+ synchronized (lastEvents) {
+ return ImmutableList.<IntentEvent>copyOf(lastEvents);
+ }
+ }
+
+ @Override
+ public Gauge<Long> intentSubmittedTimestampEpochMsGauge() {
+ return intentSubmittedTimestampEpochMsGauge;
+ }
+
+ @Override
+ public Gauge<Long> intentInstalledTimestampEpochMsGauge() {
+ return intentInstalledTimestampEpochMsGauge;
+ }
+
+ @Override
+ public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge() {
+ return intentWithdrawRequestedTimestampEpochMsGauge;
+ }
+
+ @Override
+ public Gauge<Long> intentWithdrawnTimestampEpochMsGauge() {
+ return intentWithdrawnTimestampEpochMsGauge;
+ }
+
+ @Override
+ public Meter intentSubmittedRateMeter() {
+ return intentSubmittedRateMeter;
+ }
+
+ @Override
+ public Meter intentInstalledRateMeter() {
+ return intentInstalledRateMeter;
+ }
+
+ @Override
+ public Meter intentWithdrawRequestedRateMeter() {
+ return intentWithdrawRequestedRateMeter;
+ }
+
+ @Override
+ public Meter intentWithdrawnRateMeter() {
+ return intentWithdrawnRateMeter;
+ }
+
+ @Override
+ public void event(IntentEvent event) {
+ synchronized (lastEvents) {
+ //
+ // TODO: The processing below is incomplete: we don't have
+ // an event equivalent of "Withdraw Requested"
+ //
+ switch (event.type()) {
+ case SUBMITTED:
+ intentSubmittedTimestampEpochMs = System.currentTimeMillis();
+ intentSubmittedRateMeter.mark(1);
+ break;
+ case INSTALLED:
+ intentInstalledTimestampEpochMs = System.currentTimeMillis();
+ intentInstalledRateMeter.mark(1);
+ break;
+ case FAILED:
+ // TODO: Just ignore?
+ break;
+ /*
+ case WITHDRAW_REQUESTED:
+ intentWithdrawRequestedTimestampEpochMs =
+ System.currentTimeMillis();
+ intentWithdrawRequestedRateMeter.mark(1);
+ break;
+ */
+ case WITHDRAWN:
+ intentWithdrawnTimestampEpochMs = System.currentTimeMillis();
+ intentWithdrawnRateMeter.mark(1);
+ break;
+ default:
+ break;
+ }
+
+ //
+ // Keep only the last N events, where N = LAST_EVENTS_MAX_N
+ //
+ while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
+ lastEvents.remove();
+ }
+ lastEvents.add(event);
+ }
+
+ log.debug("Intent Event: time = {} type = {} event = {}",
+ event.time(), event.type(), event);
+ }
+
+ /**
+ * Clears the internal state.
+ */
+ private void clear() {
+ synchronized (lastEvents) {
+ intentSubmittedTimestampEpochMs = 0;
+ intentInstalledTimestampEpochMs = 0;
+ intentWithdrawRequestedTimestampEpochMs = 0;
+ intentWithdrawnTimestampEpochMs = 0;
+ lastEvents.clear();
+ }
+ }
+
+ /**
+ * Registers the metrics.
+ */
+ private void registerMetrics() {
+ metricsComponent = metricsService.registerComponent(COMPONENT_NAME);
+ //
+ metricsFeatureSubmitted =
+ metricsComponent.registerFeature(FEATURE_SUBMITTED_NAME);
+ metricsFeatureInstalled =
+ metricsComponent.registerFeature(FEATURE_INSTALLED_NAME);
+ metricsFeatureWithdrawRequested =
+ metricsComponent.registerFeature(FEATURE_WITHDRAW_REQUESTED_NAME);
+ metricsFeatureWithdrawn =
+ metricsComponent.registerFeature(FEATURE_WITHDRAWN_NAME);
+ //
+ intentSubmittedTimestampEpochMsGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeatureSubmitted,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return intentSubmittedTimestampEpochMs;
+ }
+ });
+ //
+ intentInstalledTimestampEpochMsGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeatureInstalled,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return intentInstalledTimestampEpochMs;
+ }
+ });
+ //
+ intentWithdrawRequestedTimestampEpochMsGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeatureWithdrawRequested,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return intentWithdrawRequestedTimestampEpochMs;
+ }
+ });
+ //
+ intentWithdrawnTimestampEpochMsGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeatureWithdrawn,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return intentWithdrawnTimestampEpochMs;
+ }
+ });
+ //
+ intentSubmittedRateMeter =
+ metricsService.createMeter(metricsComponent,
+ metricsFeatureSubmitted,
+ METER_RATE_NAME);
+ //
+ intentInstalledRateMeter =
+ metricsService.createMeter(metricsComponent,
+ metricsFeatureInstalled,
+ METER_RATE_NAME);
+ //
+ intentWithdrawRequestedRateMeter =
+ metricsService.createMeter(metricsComponent,
+ metricsFeatureWithdrawRequested,
+ METER_RATE_NAME);
+ //
+ intentWithdrawnRateMeter =
+ metricsService.createMeter(metricsComponent,
+ metricsFeatureWithdrawn,
+ METER_RATE_NAME);
+ }
+
+ /**
+ * Removes the metrics.
+ */
+ private void removeMetrics() {
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureSubmitted,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureInstalled,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureWithdrawRequested,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureWithdrawn,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureSubmitted,
+ METER_RATE_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureInstalled,
+ METER_RATE_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureWithdrawRequested,
+ METER_RATE_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeatureWithdrawn,
+ METER_RATE_NAME);
+ }
+}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
new file mode 100644
index 0000000..4acd00f
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
@@ -0,0 +1,85 @@
+package org.onlab.onos.metrics.intent;
+
+import java.util.List;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+import org.onlab.onos.net.intent.IntentEvent;
+
+/**
+ * Service interface exported by IntentMetrics.
+ */
+public interface IntentMetricsService {
+ /**
+ * Gets the last saved intent events.
+ *
+ * @return the last saved intent events.
+ */
+ public List<IntentEvent> getEvents();
+
+ /**
+ * Gets the Metrics' Gauge for the intent SUBMITTED event timestamp
+ * (ms from the epoch).
+ *
+ * @return the Metrics' Gauge for the intent SUBMITTED event timestamp
+ * (ms from the epoch)
+ */
+ public Gauge<Long> intentSubmittedTimestampEpochMsGauge();
+
+ /**
+ * Gets the Metrics' Gauge for the intent INSTALLED event timestamp
+ * (ms from the epoch).
+ *
+ * @return the Metrics' Gauge for the intent INSTALLED event timestamp
+ * (ms from the epoch)
+ */
+ public Gauge<Long> intentInstalledTimestampEpochMsGauge();
+
+ /**
+ * Gets the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
+ * timestamp (ms from the epoch).
+ *
+ * TODO: This intent event is not implemented yet.
+ *
+ * @return the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
+ * timestamp (ms from the epoch)
+ */
+ public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge();
+
+ /**
+ * Gets the Metrics' Gauge for the intent WITHDRAWN event timestamp
+ * (ms from the epoch).
+ *
+ * @return the Metrics' Gauge for the intent WITHDRAWN event timestamp
+ * (ms from the epoch)
+ */
+ public Gauge<Long> intentWithdrawnTimestampEpochMsGauge();
+
+ /**
+ * Gets the Metrics' Meter for the submitted intents event rate.
+ *
+ * @return the Metrics' Meter for the submitted intents event rate
+ */
+ public Meter intentSubmittedRateMeter();
+
+ /**
+ * Gets the Metrics' Meter for the installed intents event rate.
+ *
+ * @return the Metrics' Meter for the installed intent event rate
+ */
+ public Meter intentInstalledRateMeter();
+
+ /**
+ * Gets the Metrics' Meter for the withdraw requested intents event rate.
+ *
+ * @return the Metrics' Meter for the withdraw requested intents event rate
+ */
+ public Meter intentWithdrawRequestedRateMeter();
+
+ /**
+ * Gets the Metrics' Meter for the withdraw completed intents event rate.
+ *
+ * @return the Metrics' Meter for the withdraw completed intents event rate
+ */
+ public Meter intentWithdrawnRateMeter();
+}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java
new file mode 100644
index 0000000..e07c3a5
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java
@@ -0,0 +1,68 @@
+package org.onlab.onos.metrics.intent.cli;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.metrics.intent.IntentMetricsService;
+import org.onlab.onos.net.intent.IntentEvent;
+
+/**
+ * Command to show the list of last intent events.
+ */
+@Command(scope = "onos", name = "intents-events",
+ description = "Lists the last intent events")
+public class IntentEventsListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT_EVENT = "Event=%s";
+
+ @Override
+ protected void execute() {
+ IntentMetricsService service = get(IntentMetricsService.class);
+
+ if (outputJson()) {
+ print("%s", json(service.getEvents()));
+ } else {
+ for (IntentEvent event : service.getEvents()) {
+ print(FORMAT_EVENT, event);
+ print(""); // Extra empty line for clarity
+ }
+ }
+ }
+
+ /**
+ * Produces a JSON array of intent events.
+ *
+ * @param intentEvents the intent events with the data
+ * @return JSON array with the intent events
+ */
+ private JsonNode json(List<IntentEvent> intentEvents) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.createArrayNode();
+
+ for (IntentEvent event : intentEvents) {
+ result.add(json(mapper, event));
+ }
+ return result;
+ }
+
+ /**
+ * Produces JSON object for a intent event.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param intentEvent the intent event with the data
+ * @return JSON object for the intent event
+ */
+ private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) {
+ ObjectNode result = mapper.createObjectNode();
+
+ result.put("time", intentEvent.time())
+ .put("type", intentEvent.type().toString())
+ .put("event", intentEvent.toString());
+ return result;
+ }
+}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
new file mode 100644
index 0000000..204cfd6
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
@@ -0,0 +1,132 @@
+package org.onlab.onos.metrics.intent.cli;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.json.MetricsModule;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.metrics.intent.IntentMetricsService;
+
+/**
+ * Command to show the intent events metrics.
+ */
+@Command(scope = "onos", name = "intents-events-metrics",
+ description = "Lists intent events metrics")
+public class IntentEventsMetricsCommand extends AbstractShellCommand {
+
+ private static final String FORMAT_GAUGE =
+ "Intent %s Event Timestamp (ms from epoch)=%d";
+ private static final String FORMAT_METER =
+ "Intent %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
+
+ @Override
+ protected void execute() {
+ IntentMetricsService service = get(IntentMetricsService.class);
+ Gauge<Long> gauge;
+ Meter meter;
+
+ if (outputJson()) {
+ ObjectMapper mapper = new ObjectMapper()
+ .registerModule(new MetricsModule(TimeUnit.SECONDS,
+ TimeUnit.MILLISECONDS,
+ false));
+ ObjectNode result = mapper.createObjectNode();
+ //
+ gauge = service.intentSubmittedTimestampEpochMsGauge();
+ result.put("intentSubmittedTimestamp", json(mapper, gauge));
+ gauge = service.intentInstalledTimestampEpochMsGauge();
+ result.put("intentInstalledTimestamp", json(mapper, gauge));
+ gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
+ result.put("intentWithdrawRequestedTimestamp",
+ json(mapper, gauge));
+ gauge = service.intentWithdrawnTimestampEpochMsGauge();
+ result.put("intentWithdrawnTimestamp", json(mapper, gauge));
+ //
+ meter = service.intentSubmittedRateMeter();
+ result.put("intentSubmittedRate", json(mapper, meter));
+ meter = service.intentInstalledRateMeter();
+ result.put("intentInstalledRate", json(mapper, meter));
+ meter = service.intentWithdrawRequestedRateMeter();
+ result.put("intentWithdrawRequestedRate", json(mapper, meter));
+ meter = service.intentWithdrawnRateMeter();
+ result.put("intentWithdrawnRate", json(mapper, meter));
+ //
+ print("%s", result);
+ } else {
+ gauge = service.intentSubmittedTimestampEpochMsGauge();
+ printGauge("Submitted", gauge);
+ gauge = service.intentInstalledTimestampEpochMsGauge();
+ printGauge("Installed", gauge);
+ gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
+ printGauge("Withdraw Requested", gauge);
+ gauge = service.intentWithdrawnTimestampEpochMsGauge();
+ printGauge("Withdrawn", gauge);
+ //
+ meter = service.intentSubmittedRateMeter();
+ printMeter("Submitted", meter);
+ meter = service.intentInstalledRateMeter();
+ printMeter("Installed", meter);
+ meter = service.intentWithdrawRequestedRateMeter();
+ printMeter("Withdraw Requested", meter);
+ meter = service.intentWithdrawnRateMeter();
+ printMeter("Withdrawn", meter);
+ }
+ }
+
+ /**
+ * Produces JSON node for an Object.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param object the Object with the data
+ * @return JSON node for the Object
+ */
+ private JsonNode json(ObjectMapper mapper, Object object) {
+ //
+ // NOTE: The API for custom serializers is incomplete,
+ // hence we have to parse the JSON string to create JsonNode.
+ //
+ try {
+ final String objectJson = mapper.writeValueAsString(object);
+ JsonNode objectNode = mapper.readTree(objectJson);
+ return objectNode;
+ } catch (JsonProcessingException e) {
+ log.error("Error writing value as JSON string", e);
+ } catch (IOException e) {
+ log.error("Error writing value as JSON string", e);
+ }
+ return null;
+ }
+
+ /**
+ * Prints a Gauge.
+ *
+ * @param operationStr the string with the intent operation to print
+ * @param gauge the Gauge to print
+ */
+ private void printGauge(String operationStr, Gauge<Long> gauge) {
+ print(FORMAT_GAUGE, operationStr, gauge.getValue());
+ }
+
+ /**
+ * Prints a Meter.
+ *
+ * @param operationStr the string with the intent operation to print
+ * @param meter the Meter to print
+ */
+ private void printMeter(String operationStr, Meter meter) {
+ TimeUnit rateUnit = TimeUnit.SECONDS;
+ double rateFactor = rateUnit.toSeconds(1);
+ print(FORMAT_METER, operationStr, meter.getCount(),
+ meter.getMeanRate() * rateFactor,
+ meter.getOneMinuteRate() * rateFactor,
+ meter.getFiveMinuteRate() * rateFactor,
+ meter.getFifteenMinuteRate() * rateFactor);
+ }
+}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/package-info.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/package-info.java
new file mode 100644
index 0000000..5bba126
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * ONOS Intent Metrics Application that collects intent-related metrics.
+ */
+package org.onlab.onos.metrics.intent;
diff --git a/apps/metrics/intent/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/metrics/intent/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..04f179a
--- /dev/null
+++ b/apps/metrics/intent/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,12 @@
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onlab.onos.metrics.intent.cli.IntentEventsListCommand"/>
+ </command>
+ <command>
+ <action class="org.onlab.onos.metrics.intent.cli.IntentEventsMetricsCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/apps/metrics/pom.xml b/apps/metrics/pom.xml
index 0ce3913..67085c2 100644
--- a/apps/metrics/pom.xml
+++ b/apps/metrics/pom.xml
@@ -17,6 +17,7 @@
<description>ONOS metrics applications</description>
<modules>
+ <module>intent</module>
<module>topology</module>
</modules>
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
index e2a4532..32cf0cf 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
@@ -18,6 +18,15 @@
import org.onlab.metrics.MetricsFeature;
import org.onlab.metrics.MetricsService;
import org.onlab.onos.event.Event;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceListener;
+import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.host.HostEvent;
+import org.onlab.onos.net.host.HostListener;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.link.LinkListener;
+import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.topology.TopologyEvent;
import org.onlab.onos.net.topology.TopologyListener;
import org.onlab.onos.net.topology.TopologyService;
@@ -28,14 +37,26 @@
*/
@Component(immediate = true)
@Service
-public class TopologyMetrics implements TopologyMetricsService,
- TopologyListener {
+public class TopologyMetrics implements TopologyMetricsService {
private static final Logger log = getLogger(TopologyMetrics.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
- private LinkedList<TopologyEvent> lastEvents = new LinkedList<>();
- private static final int LAST_EVENTS_MAX_N = 10;
+
+ private LinkedList<Event> lastEvents = new LinkedList<>();
+ private static final int LAST_EVENTS_MAX_N = 100;
+
+ private final DeviceListener deviceListener = new InnerDeviceListener();
+ private final HostListener hostListener = new InnerHostListener();
+ private final LinkListener linkListener = new InnerLinkListener();
+ private final TopologyListener topologyListener =
+ new InnerTopologyListener();
//
// Metrics
@@ -61,22 +82,33 @@
protected void activate() {
clear();
registerMetrics();
- topologyService.addListener(this);
+
+ // Register for all topology-related events
+ deviceService.addListener(deviceListener);
+ hostService.addListener(hostListener);
+ linkService.addListener(linkListener);
+ topologyService.addListener(topologyListener);
+
log.info("ONOS Topology Metrics started.");
}
@Deactivate
public void deactivate() {
- topologyService.removeListener(this);
+ // De-register from all topology-related events
+ deviceService.removeListener(deviceListener);
+ hostService.removeListener(hostListener);
+ linkService.removeListener(linkListener);
+ topologyService.removeListener(topologyListener);
+
removeMetrics();
clear();
log.info("ONOS Topology Metrics stopped.");
}
@Override
- public List<TopologyEvent> getEvents() {
+ public List<Event> getEvents() {
synchronized (lastEvents) {
- return ImmutableList.<TopologyEvent>copyOf(lastEvents);
+ return ImmutableList.<Event>copyOf(lastEvents);
}
}
@@ -90,27 +122,22 @@
return eventRateMeter;
}
- @Override
- public void event(TopologyEvent event) {
- lastEventTimestampEpochMs = System.currentTimeMillis();
- //
- // NOTE: If we want to count each "reason" as a separate event,
- // then we should use 'event.reason().size()' instead of '1' to
- // mark the meter below.
- //
- eventRateMeter.mark(1);
-
- log.debug("Topology Event: time = {} type = {} subject = {}",
- event.time(), event.type(), event.subject());
- for (Event reason : event.reasons()) {
- log.debug("Topology Event Reason: time = {} type = {} subject = {}",
- reason.time(), reason.type(), reason.subject());
- }
-
- //
- // Keep only the last N events, where N = LAST_EVENTS_MAX_N
- //
+ /**
+ * Records an event.
+ *
+ * @param event the event to record
+ * @param updateEventRateMeter if true, update the Event Rate Meter
+ */
+ private void recordEvent(Event event, boolean updateEventRateMeter) {
synchronized (lastEvents) {
+ lastEventTimestampEpochMs = System.currentTimeMillis();
+ if (updateEventRateMeter) {
+ eventRateMeter.mark(1);
+ }
+
+ //
+ // Keep only the last N events, where N = LAST_EVENTS_MAX_N
+ //
while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
lastEvents.remove();
}
@@ -119,11 +146,67 @@
}
/**
+ * Inner Device Event Listener class.
+ */
+ private class InnerDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ recordEvent(event, true);
+ log.debug("Device Event: time = {} type = {} event = {}",
+ event.time(), event.type(), event);
+ }
+ }
+
+ /**
+ * Inner Host Event Listener class.
+ */
+ private class InnerHostListener implements HostListener {
+ @Override
+ public void event(HostEvent event) {
+ recordEvent(event, true);
+ log.debug("Host Event: time = {} type = {} event = {}",
+ event.time(), event.type(), event);
+ }
+ }
+
+ /**
+ * Inner Link Event Listener class.
+ */
+ private class InnerLinkListener implements LinkListener {
+ @Override
+ public void event(LinkEvent event) {
+ recordEvent(event, true);
+ log.debug("Link Event: time = {} type = {} event = {}",
+ event.time(), event.type(), event);
+ }
+ }
+
+ /**
+ * Inner Topology Event Listener class.
+ */
+ private class InnerTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ //
+ // NOTE: Don't update the eventRateMeter, because the real
+ // events are already captured/counted.
+ //
+ recordEvent(event, false);
+ log.debug("Topology Event: time = {} type = {} event = {}",
+ event.time(), event.type(), event);
+ for (Event reason : event.reasons()) {
+ log.debug("Topology Event Reason: time = {} type = {} event = {}",
+ reason.time(), reason.type(), reason);
+ }
+ }
+ }
+
+ /**
* Clears the internal state.
*/
private void clear() {
- lastEventTimestampEpochMs = 0;
synchronized (lastEvents) {
+ lastEventTimestampEpochMs = 0;
lastEvents.clear();
}
}
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
index cc370fa..aeb2e32 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
@@ -4,7 +4,7 @@
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
-import org.onlab.onos.net.topology.TopologyEvent;
+import org.onlab.onos.event.Event;
/**
* Service interface exported by TopologyMetrics.
@@ -15,7 +15,7 @@
*
* @return the last saved topology events.
*/
- public List<TopologyEvent> getEvents();
+ public List<Event> getEvents();
/**
* Gets the Metrics' Gauge for the last topology event timestamp
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsListCommand.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsListCommand.java
index 8bab4d0..f8d0c1a 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsListCommand.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsListCommand.java
@@ -19,10 +19,8 @@
description = "Lists the last topology events")
public class TopologyEventsListCommand extends AbstractShellCommand {
- private static final String FORMAT_EVENT =
- "Topology Event time=%d type=%s subject=%s";
- private static final String FORMAT_REASON =
- " Reason time=%d type=%s subject=%s";
+ private static final String FORMAT_EVENT = "Event=%s";
+ private static final String FORMAT_REASON = " Reason=%s";
@Override
protected void execute() {
@@ -31,12 +29,13 @@
if (outputJson()) {
print("%s", json(service.getEvents()));
} else {
- for (TopologyEvent event : service.getEvents()) {
- print(FORMAT_EVENT, event.time(), event.type(),
- event.subject());
- for (Event reason : event.reasons()) {
- print(FORMAT_REASON, reason.time(), reason.type(),
- reason.subject());
+ for (Event event : service.getEvents()) {
+ print(FORMAT_EVENT, event);
+ if (event instanceof TopologyEvent) {
+ TopologyEvent topologyEvent = (TopologyEvent) event;
+ for (Event reason : topologyEvent.reasons()) {
+ print(FORMAT_REASON, reason);
+ }
}
print(""); // Extra empty line for clarity
}
@@ -46,14 +45,14 @@
/**
* Produces a JSON array of topology events.
*
- * @param topologyEvents the topology events with the data
+ * @param events the topology events with the data
* @return JSON array with the topology events
*/
- private JsonNode json(List<TopologyEvent> topologyEvents) {
+ private JsonNode json(List<Event> events) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
- for (TopologyEvent event : topologyEvents) {
+ for (Event event : events) {
result.add(json(mapper, event));
}
return result;
@@ -66,32 +65,23 @@
* @param topologyEvent the topology event with the data
* @return JSON object for the topology event
*/
- private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) {
- ObjectNode result = mapper.createObjectNode();
- ArrayNode reasons = mapper.createArrayNode();
-
- for (Event reason : topologyEvent.reasons()) {
- reasons.add(json(mapper, reason));
- }
- result.put("time", topologyEvent.time())
- .put("type", topologyEvent.type().toString())
- .put("subject", topologyEvent.subject().toString())
- .put("reasons", reasons);
- return result;
- }
-
- /**
- * Produces JSON object for a generic event.
- *
- * @param event the generic event with the data
- * @return JSON object for the generic event
- */
private ObjectNode json(ObjectMapper mapper, Event event) {
ObjectNode result = mapper.createObjectNode();
result.put("time", event.time())
.put("type", event.type().toString())
- .put("subject", event.subject().toString());
+ .put("event", event.toString());
+
+ // Add the reasons if a TopologyEvent
+ if (event instanceof TopologyEvent) {
+ TopologyEvent topologyEvent = (TopologyEvent) event;
+ ArrayNode reasons = mapper.createArrayNode();
+ for (Event reason : topologyEvent.reasons()) {
+ reasons.add(json(mapper, reason));
+ }
+ result.put("reasons", reasons);
+ }
+
return result;
}
}
diff --git a/features/features.xml b/features/features.xml
index e6c4beb..e363094 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -199,9 +199,16 @@
<feature name="onos-app-metrics" version="1.0.0"
description="ONOS metrics applications">
+ <feature>onos-app-metrics-intent</feature>
<feature>onos-app-metrics-topology</feature>
</feature>
+ <feature name="onos-app-metrics-intent" version="1.0.0"
+ description="ONOS intent metrics application">
+ <feature>onos-api</feature>
+ <bundle>mvn:org.onlab.onos/onos-app-metrics-intent/1.0.0-SNAPSHOT</bundle>
+ </feature>
+
<feature name="onos-app-metrics-topology" version="1.0.0"
description="ONOS topology metrics application">
<feature>onos-api</feature>