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 e2a4532167..32cf0cf51a 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.MetricsComponent; 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 @@ import org.slf4j.Logger; */ @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 lastEvents = new LinkedList<>(); - private static final int LAST_EVENTS_MAX_N = 10; + + private LinkedList 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 @@ public class TopologyMetrics implements TopologyMetricsService, 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 getEvents() { + public List getEvents() { synchronized (lastEvents) { - return ImmutableList.copyOf(lastEvents); + return ImmutableList.copyOf(lastEvents); } } @@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService, 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(); } @@ -118,12 +145,68 @@ public class TopologyMetrics implements TopologyMetricsService, } } + /** + * 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 cc370fa362..aeb2e32f52 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 java.util.List; 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 @@ public interface TopologyMetricsService { * * @return the last saved topology events. */ - public List getEvents(); + public List 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 8bab4d0a70..f8d0c1a4e0 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 @@ import org.onlab.onos.net.topology.TopologyEvent; 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 @@ public class TopologyEventsListCommand extends AbstractShellCommand { 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 @@ public class TopologyEventsListCommand extends AbstractShellCommand { /** * 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 topologyEvents) { + private JsonNode json(List 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 @@ public class TopologyEventsListCommand extends AbstractShellCommand { * @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; } }