Update the TopologyMetrics module to listen for all topology-related events:

Devices, Hosts, Links, TopologyEvent

Now the semantics for updating the metrics are:
 * Any topology-related event (DeviceEvent, HostEvent, LinkEvent,
   TopologyEvent) will update the Lost Topology Event Timestamp
 * Only the DeviceEvent, HostEvent and LinkEvent will be counted in
   measuring the event rate; TopologyEvent is excluded, because it
   is generated as a result of some of those events

Also, increased the number of saved events from 10 to 100.

Change-Id: Ie759ee69869cddc617d7ad5b8b75a622e2571620
This commit is contained in:
Pavlin Radoslavov 2014-10-23 01:03:10 -07:00
parent 558e893766
commit 5ba8b28e76
3 changed files with 138 additions and 65 deletions

View File

@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent;
import org.onlab.metrics.MetricsFeature; import org.onlab.metrics.MetricsFeature;
import org.onlab.metrics.MetricsService; import org.onlab.metrics.MetricsService;
import org.onlab.onos.event.Event; 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.TopologyEvent;
import org.onlab.onos.net.topology.TopologyListener; import org.onlab.onos.net.topology.TopologyListener;
import org.onlab.onos.net.topology.TopologyService; import org.onlab.onos.net.topology.TopologyService;
@ -28,14 +37,26 @@ import org.slf4j.Logger;
*/ */
@Component(immediate = true) @Component(immediate = true)
@Service @Service
public class TopologyMetrics implements TopologyMetricsService, public class TopologyMetrics implements TopologyMetricsService {
TopologyListener {
private static final Logger log = getLogger(TopologyMetrics.class); 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) @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService; 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 // Metrics
@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService,
protected void activate() { protected void activate() {
clear(); clear();
registerMetrics(); 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."); log.info("ONOS Topology Metrics started.");
} }
@Deactivate @Deactivate
public void 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(); removeMetrics();
clear(); clear();
log.info("ONOS Topology Metrics stopped."); log.info("ONOS Topology Metrics stopped.");
} }
@Override @Override
public List<TopologyEvent> getEvents() { public List<Event> getEvents() {
synchronized (lastEvents) { synchronized (lastEvents) {
return ImmutableList.<TopologyEvent>copyOf(lastEvents); return ImmutableList.<Event>copyOf(lastEvents);
} }
} }
@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService,
return eventRateMeter; return eventRateMeter;
} }
@Override /**
public void event(TopologyEvent event) { * Records an event.
lastEventTimestampEpochMs = System.currentTimeMillis(); *
// * @param event the event to record
// NOTE: If we want to count each "reason" as a separate event, * @param updateEventRateMeter if true, update the Event Rate Meter
// then we should use 'event.reason().size()' instead of '1' to */
// mark the meter below. private void recordEvent(Event event, boolean updateEventRateMeter) {
//
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
//
synchronized (lastEvents) { 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) { while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
lastEvents.remove(); 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. * Clears the internal state.
*/ */
private void clear() { private void clear() {
lastEventTimestampEpochMs = 0;
synchronized (lastEvents) { synchronized (lastEvents) {
lastEventTimestampEpochMs = 0;
lastEvents.clear(); lastEvents.clear();
} }
} }

View File

@ -4,7 +4,7 @@ import java.util.List;
import com.codahale.metrics.Gauge; import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import org.onlab.onos.net.topology.TopologyEvent; import org.onlab.onos.event.Event;
/** /**
* Service interface exported by TopologyMetrics. * Service interface exported by TopologyMetrics.
@ -15,7 +15,7 @@ public interface TopologyMetricsService {
* *
* @return the last saved topology events. * @return the last saved topology events.
*/ */
public List<TopologyEvent> getEvents(); public List<Event> getEvents();
/** /**
* Gets the Metrics' Gauge for the last topology event timestamp * Gets the Metrics' Gauge for the last topology event timestamp

View File

@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent;
description = "Lists the last topology events") description = "Lists the last topology events")
public class TopologyEventsListCommand extends AbstractShellCommand { public class TopologyEventsListCommand extends AbstractShellCommand {
private static final String FORMAT_EVENT = private static final String FORMAT_EVENT = "Event=%s";
"Topology Event time=%d type=%s subject=%s"; private static final String FORMAT_REASON = " Reason=%s";
private static final String FORMAT_REASON =
" Reason time=%d type=%s subject=%s";
@Override @Override
protected void execute() { protected void execute() {
@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
if (outputJson()) { if (outputJson()) {
print("%s", json(service.getEvents())); print("%s", json(service.getEvents()));
} else { } else {
for (TopologyEvent event : service.getEvents()) { for (Event event : service.getEvents()) {
print(FORMAT_EVENT, event.time(), event.type(), print(FORMAT_EVENT, event);
event.subject()); if (event instanceof TopologyEvent) {
for (Event reason : event.reasons()) { TopologyEvent topologyEvent = (TopologyEvent) event;
print(FORMAT_REASON, reason.time(), reason.type(), for (Event reason : topologyEvent.reasons()) {
reason.subject()); print(FORMAT_REASON, reason);
}
} }
print(""); // Extra empty line for clarity print(""); // Extra empty line for clarity
} }
@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
/** /**
* Produces a JSON array of topology events. * 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 * @return JSON array with the topology events
*/ */
private JsonNode json(List<TopologyEvent> topologyEvents) { private JsonNode json(List<Event> events) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode(); ArrayNode result = mapper.createArrayNode();
for (TopologyEvent event : topologyEvents) { for (Event event : events) {
result.add(json(mapper, event)); result.add(json(mapper, event));
} }
return result; return result;
@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
* @param topologyEvent the topology event with the data * @param topologyEvent the topology event with the data
* @return JSON object for the topology event * @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) { private ObjectNode json(ObjectMapper mapper, Event event) {
ObjectNode result = mapper.createObjectNode(); ObjectNode result = mapper.createObjectNode();
result.put("time", event.time()) result.put("time", event.time())
.put("type", event.type().toString()) .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; return result;
} }
} }