Pavlin Radoslavov

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
...@@ -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.
95 - lastEventTimestampEpochMs = System.currentTimeMillis(); 127 + *
96 - // 128 + * @param event the event to record
97 - // NOTE: If we want to count each "reason" as a separate event, 129 + * @param updateEventRateMeter if true, update the Event Rate Meter
98 - // then we should use 'event.reason().size()' instead of '1' to 130 + */
99 - // mark the meter below. 131 + private void recordEvent(Event event, boolean updateEventRateMeter) {
100 - //
101 - 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 - }
109 -
110 - //
111 - // Keep only the last N events, where N = LAST_EVENTS_MAX_N
112 - //
113 synchronized (lastEvents) { 132 synchronized (lastEvents) {
133 + lastEventTimestampEpochMs = System.currentTimeMillis();
134 + if (updateEventRateMeter) {
135 + eventRateMeter.mark(1);
136 + }
137 +
138 + //
139 + // Keep only the last N events, where N = LAST_EVENTS_MAX_N
140 + //
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) {
70 - ObjectNode result = mapper.createObjectNode();
71 - ArrayNode reasons = mapper.createArrayNode();
72 -
73 - for (Event reason : topologyEvent.reasons()) {
74 - reasons.add(json(mapper, reason));
75 - }
76 - result.put("time", topologyEvent.time())
77 - .put("type", topologyEvent.type().toString())
78 - .put("subject", topologyEvent.subject().toString())
79 - .put("reasons", reasons);
80 - return result;
81 - }
82 -
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) { 68 private ObjectNode json(ObjectMapper mapper, Event event) {
90 ObjectNode result = mapper.createObjectNode(); 69 ObjectNode result = mapper.createObjectNode();
91 70
92 result.put("time", event.time()) 71 result.put("time", event.time())
93 .put("type", event.type().toString()) 72 .put("type", event.type().toString())
94 - .put("subject", event.subject().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();
79 + for (Event reason : topologyEvent.reasons()) {
80 + reasons.add(json(mapper, reason));
81 + }
82 + result.put("reasons", reasons);
83 + }
84 +
95 return result; 85 return result;
96 } 86 }
97 } 87 }
......