blob: 02b94eb9b85f2dc37bd39f03a6e785745a86f2bc [file] [log] [blame]
Thomas Vachuska329af532015-03-10 02:08:33 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ui.impl;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import com.google.common.collect.ImmutableSet;
22import org.onlab.osgi.ServiceDirectory;
23import org.onlab.util.AbstractAccumulator;
24import org.onlab.util.Accumulator;
25import org.onosproject.cluster.ClusterEvent;
26import org.onosproject.cluster.ClusterEventListener;
27import org.onosproject.cluster.ControllerNode;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.event.Event;
31import org.onosproject.mastership.MastershipAdminService;
32import org.onosproject.mastership.MastershipEvent;
33import org.onosproject.mastership.MastershipListener;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.Device;
36import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
38import org.onosproject.net.HostLocation;
39import org.onosproject.net.Link;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowRuleEvent;
45import org.onosproject.net.flow.FlowRuleListener;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.host.HostEvent;
49import org.onosproject.net.host.HostListener;
50import org.onosproject.net.intent.HostToHostIntent;
51import org.onosproject.net.intent.Intent;
52import org.onosproject.net.intent.IntentEvent;
53import org.onosproject.net.intent.IntentListener;
54import org.onosproject.net.intent.MultiPointToSinglePointIntent;
55import org.onosproject.net.link.LinkEvent;
56import org.onosproject.net.link.LinkListener;
Simon Huntd2747a02015-04-30 22:41:16 -070057import org.onosproject.ui.JsonUtils;
58import org.onosproject.ui.RequestHandler;
Thomas Vachuska329af532015-03-10 02:08:33 -070059import org.onosproject.ui.UiConnection;
60
61import java.util.ArrayList;
Simon Huntd2747a02015-04-30 22:41:16 -070062import java.util.Collection;
Thomas Vachuska329af532015-03-10 02:08:33 -070063import java.util.Collections;
64import java.util.Comparator;
65import java.util.HashSet;
66import java.util.List;
67import java.util.Set;
68import java.util.Timer;
69import java.util.TimerTask;
70
71import static com.google.common.base.Strings.isNullOrEmpty;
72import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
73import static org.onosproject.net.DeviceId.deviceId;
74import static org.onosproject.net.HostId.hostId;
Thomas Vachuskacb5016f2015-05-18 14:11:43 -070075import static org.onosproject.net.device.DeviceEvent.Type.*;
Thomas Vachuska329af532015-03-10 02:08:33 -070076import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
77import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
78
79/**
80 * Web socket capable of interacting with the GUI topology view.
81 */
82public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
83
Simon Huntd2747a02015-04-30 22:41:16 -070084 private static final String REQ_DETAILS = "requestDetails";
85 private static final String UPDATE_META = "updateMeta";
86 private static final String ADD_HOST_INTENT = "addHostIntent";
87 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
88 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
89 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
90 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
91 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
92 private static final String REQ_ALL_TRAFFIC = "requestAllTraffic";
93 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
94 private static final String CANCEL_TRAFFIC = "cancelTraffic";
95 private static final String REQ_SUMMARY = "requestSummary";
96 private static final String CANCEL_SUMMARY = "cancelSummary";
97 private static final String EQ_MASTERS = "equalizeMasters";
98 private static final String SPRITE_LIST_REQ = "spriteListRequest";
99 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
100 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700101 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd2747a02015-04-30 22:41:16 -0700102 private static final String TOPO_STOP = "topoStop";
103
104
Thomas Vachuska329af532015-03-10 02:08:33 -0700105 private static final String APP_ID = "org.onosproject.gui";
106
107 private static final long TRAFFIC_FREQUENCY = 5000;
108 private static final long SUMMARY_FREQUENCY = 30000;
109
110 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700111 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700112
113
114 private final Timer timer = new Timer("topology-view");
115
116 private static final int MAX_EVENTS = 1000;
117 private static final int MAX_BATCH_MS = 5000;
118 private static final int MAX_IDLE_MS = 1000;
119
120 private ApplicationId appId;
121
122 private final ClusterEventListener clusterListener = new InternalClusterListener();
123 private final MastershipListener mastershipListener = new InternalMastershipListener();
124 private final DeviceListener deviceListener = new InternalDeviceListener();
125 private final LinkListener linkListener = new InternalLinkListener();
126 private final HostListener hostListener = new InternalHostListener();
127 private final IntentListener intentListener = new InternalIntentListener();
128 private final FlowRuleListener flowListener = new InternalFlowListener();
129
130 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
131
Simon Huntd2747a02015-04-30 22:41:16 -0700132 private TimerTask trafficTask = null;
133 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700134
Simon Huntd2747a02015-04-30 22:41:16 -0700135 private TimerTask summaryTask = null;
136 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700137
138 private boolean listenersRemoved = false;
139
140 private TopologyViewIntentFilter intentFilter;
141
142 // Current selection context
143 private Set<Host> selectedHosts;
144 private Set<Device> selectedDevices;
145 private List<Intent> selectedIntents;
146 private int currentIntentIndex = -1;
147
Thomas Vachuska329af532015-03-10 02:08:33 -0700148
149 @Override
150 public void init(UiConnection connection, ServiceDirectory directory) {
151 super.init(connection, directory);
152 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
153 hostService, linkService);
154 appId = directory.get(CoreService.class).registerApplication(APP_ID);
155 }
156
157 @Override
158 public void destroy() {
159 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700160 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700161 super.destroy();
162 }
163
Thomas Vachuska329af532015-03-10 02:08:33 -0700164 @Override
Simon Huntda580882015-05-12 20:58:18 -0700165 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700166 return ImmutableSet.of(
167 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700168 new TopoHeartbeat(),
Simon Huntd2747a02015-04-30 22:41:16 -0700169 new TopoStop(),
170 new ReqSummary(),
171 new CancelSummary(),
172 new SpriteListReq(),
173 new SpriteDataReq(),
174 new RequestDetails(),
175 new UpdateMeta(),
176 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700177
Simon Huntd2747a02015-04-30 22:41:16 -0700178 // TODO: migrate traffic related to separate app
179 new AddHostIntent(),
180 new AddMultiSourceIntent(),
181 new ReqRelatedIntents(),
182 new ReqNextIntent(),
183 new ReqPrevIntent(),
184 new ReqSelectedIntentTraffic(),
185 new ReqAllTraffic(),
186 new ReqDevLinkFlows(),
187 new CancelTraffic()
188 );
189 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700190
Simon Huntd2747a02015-04-30 22:41:16 -0700191 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700192
Simon Huntc54cd1b2015-05-11 13:43:44 -0700193 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700194 private final class TopoStart extends RequestHandler {
195 private TopoStart() {
196 super(TOPO_START);
197 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700198
Simon Huntd2747a02015-04-30 22:41:16 -0700199 @Override
200 public void process(long sid, ObjectNode payload) {
201 addListeners();
202 sendAllInstances(null);
203 sendAllDevices();
204 sendAllLinks();
205 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700206 }
207 }
208
Simon Huntc54cd1b2015-05-11 13:43:44 -0700209 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700210 private final class TopoHeartbeat extends RequestHandler {
211 private TopoHeartbeat() {
212 super(TOPO_HEARTBEAT);
213 }
214
215 @Override
216 public void process(long sid, ObjectNode payload) {
217 // place holder for now
218 }
219 }
220
221 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700222 private final class TopoStop extends RequestHandler {
223 private TopoStop() {
224 super(TOPO_STOP);
225 }
226
227 @Override
228 public void process(long sid, ObjectNode payload) {
229 stopSummaryMonitoring();
230 stopTrafficMonitoring();
231 }
232 }
233
Simon Huntc54cd1b2015-05-11 13:43:44 -0700234 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700235 private final class ReqSummary extends RequestHandler {
236 private ReqSummary() {
237 super(REQ_SUMMARY);
238 }
239
240 @Override
241 public void process(long sid, ObjectNode payload) {
242 requestSummary(sid);
243 startSummaryMonitoring();
244 }
245 }
246
247 private final class CancelSummary extends RequestHandler {
248 private CancelSummary() {
249 super(CANCEL_SUMMARY);
250 }
251
252 @Override
253 public void process(long sid, ObjectNode payload) {
254 stopSummaryMonitoring();
255 }
256 }
257
258 private final class SpriteListReq extends RequestHandler {
259 private SpriteListReq() {
260 super(SPRITE_LIST_REQ);
261 }
262
263 @Override
264 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700265 ObjectNode root = objectNode();
266 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700267 get(SpriteService.class).getNames().forEach(names::add);
268 root.set("names", names);
269 sendMessage("spriteListResponse", sid, root);
270 }
271 }
272
273 private final class SpriteDataReq extends RequestHandler {
274 private SpriteDataReq() {
275 super(SPRITE_DATA_REQ);
276 }
277
278 @Override
279 public void process(long sid, ObjectNode payload) {
280 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700281 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700282 root.set("data", get(SpriteService.class).get(name));
283 sendMessage("spriteDataResponse", sid, root);
284 }
285 }
286
287 private final class RequestDetails extends RequestHandler {
288 private RequestDetails() {
289 super(REQ_DETAILS);
290 }
291
292 @Override
293 public void process(long sid, ObjectNode payload) {
294 String type = string(payload, "class", "unknown");
295 String id = JsonUtils.string(payload, "id");
296
297 if (type.equals("device")) {
298 sendMessage(deviceDetails(deviceId(id), sid));
299 } else if (type.equals("host")) {
300 sendMessage(hostDetails(hostId(id), sid));
301 }
302 }
303 }
304
305 private final class UpdateMeta extends RequestHandler {
306 private UpdateMeta() {
307 super(UPDATE_META);
308 }
309
310 @Override
311 public void process(long sid, ObjectNode payload) {
312 updateMetaUi(payload);
313 }
314 }
315
316 private final class EqMasters extends RequestHandler {
317 private EqMasters() {
318 super(EQ_MASTERS);
319 }
320
321 @Override
322 public void process(long sid, ObjectNode payload) {
323 directory.get(MastershipAdminService.class).balanceRoles();
324 }
325 }
326
327 // === TODO: move traffic related classes to traffic app
328
329 private final class AddHostIntent extends RequestHandler {
330 private AddHostIntent() {
331 super(ADD_HOST_INTENT);
332 }
333
334 @Override
335 public void process(long sid, ObjectNode payload) {
336 // TODO: add protection against device ids and non-existent hosts.
337 HostId one = hostId(string(payload, "one"));
338 HostId two = hostId(string(payload, "two"));
339
340 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700341 .appId(appId)
342 .one(one)
343 .two(two)
344 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700345
346 intentService.submit(intent);
347 startMonitoringIntent(intent);
348 }
349 }
350
351 private final class AddMultiSourceIntent extends RequestHandler {
352 private AddMultiSourceIntent() {
353 super(ADD_MULTI_SRC_INTENT);
354 }
355
356 @Override
357 public void process(long sid, ObjectNode payload) {
358 // TODO: add protection against device ids and non-existent hosts.
359 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
360 HostId dst = hostId(string(payload, "dst"));
361 Host dstHost = hostService.getHost(dst);
362
363 Set<ConnectPoint> ingressPoints = getHostLocations(src);
364
365 // FIXME: clearly, this is not enough
366 TrafficSelector selector = DefaultTrafficSelector.builder()
367 .matchEthDst(dstHost.mac()).build();
368 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
369
370 MultiPointToSinglePointIntent intent =
371 MultiPointToSinglePointIntent.builder()
372 .appId(appId)
373 .selector(selector)
374 .treatment(treatment)
375 .ingressPoints(ingressPoints)
376 .egressPoint(dstHost.location())
377 .build();
378
379 intentService.submit(intent);
380 startMonitoringIntent(intent);
381 }
382 }
383
384 private final class ReqRelatedIntents extends RequestHandler {
385 private ReqRelatedIntents() {
386 super(REQ_RELATED_INTENTS);
387 }
388
389 @Override
390 public void process(long sid, ObjectNode payload) {
391 // Cancel any other traffic monitoring mode.
392 stopTrafficMonitoring();
393
394 if (!payload.has("ids")) {
395 return;
396 }
397
398 // Get the set of selected hosts and their intents.
399 ArrayNode ids = (ArrayNode) payload.path("ids");
400 selectedHosts = getHosts(ids);
401 selectedDevices = getDevices(ids);
402 selectedIntents = intentFilter.findPathIntents(
403 selectedHosts, selectedDevices, intentService.getIntents());
404 currentIntentIndex = -1;
405
406 if (haveSelectedIntents()) {
407 // Send a message to highlight all links of all monitored intents.
408 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
409 }
410
411 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
412// String hover = string(payload, "hover");
413// if (!isNullOrEmpty(hover)) {
414// // If there is a hover node, include it in the selection and find intents.
415// processHoverExtendedSelection(sid, hover);
416// }
417 }
418 }
419
420 private final class ReqNextIntent extends RequestHandler {
421 private ReqNextIntent() {
422 super(REQ_NEXT_INTENT);
423 }
424
425 @Override
426 public void process(long sid, ObjectNode payload) {
427 stopTrafficMonitoring();
428 requestAnotherRelatedIntent(+1);
429 }
430 }
431
432 private final class ReqPrevIntent extends RequestHandler {
433 private ReqPrevIntent() {
434 super(REQ_PREV_INTENT);
435 }
436
437 @Override
438 public void process(long sid, ObjectNode payload) {
439 stopTrafficMonitoring();
440 requestAnotherRelatedIntent(-1);
441 }
442 }
443
444 private final class ReqSelectedIntentTraffic extends RequestHandler {
445 private ReqSelectedIntentTraffic() {
446 super(REQ_SEL_INTENT_TRAFFIC);
447 }
448
449 @Override
450 public void process(long sid, ObjectNode payload) {
451 trafficEvent =
452 new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
453 requestSelectedIntentTraffic();
454 startTrafficMonitoring();
455 }
456 }
457
458 private final class ReqAllTraffic extends RequestHandler {
459 private ReqAllTraffic() {
460 super(REQ_ALL_TRAFFIC);
461 }
462
463 @Override
464 public void process(long sid, ObjectNode payload) {
465 trafficEvent =
466 new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
467 requestAllTraffic();
468 }
469 }
470
471 private final class ReqDevLinkFlows extends RequestHandler {
472 private ReqDevLinkFlows() {
473 super(REQ_DEV_LINK_FLOWS);
474 }
475
476 @Override
477 public void process(long sid, ObjectNode payload) {
478 trafficEvent =
479 new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
480 requestDeviceLinkFlows(payload);
481 }
482 }
483
484 private final class CancelTraffic extends RequestHandler {
485 private CancelTraffic() {
486 super(CANCEL_TRAFFIC);
487 }
488
489 @Override
490 public void process(long sid, ObjectNode payload) {
491 selectedIntents = null;
492 sendMessage(trafficMessage());
493 stopTrafficMonitoring();
494 }
495 }
496
497 //=======================================================================
498
499
Thomas Vachuska329af532015-03-10 02:08:33 -0700500 // Sends the specified data to the client.
501 protected synchronized void sendMessage(ObjectNode data) {
502 UiConnection connection = connection();
503 if (connection != null) {
504 connection.sendMessage(data);
505 }
506 }
507
Simon Huntd2747a02015-04-30 22:41:16 -0700508 // Subscribes for summary messages.
509 private synchronized void requestSummary(long sid) {
510 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700511 }
512
Simon Huntd2747a02015-04-30 22:41:16 -0700513
Thomas Vachuska329af532015-03-10 02:08:33 -0700514 private void cancelAllRequests() {
515 stopSummaryMonitoring();
516 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700517 }
518
519 // Sends all controller nodes to the client as node-added messages.
520 private void sendAllInstances(String messageType) {
521 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
522 Collections.sort(nodes, NODE_COMPARATOR);
523 for (ControllerNode node : nodes) {
524 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
525 messageType));
526 }
527 }
528
529 // Sends all devices to the client as device-added messages.
530 private void sendAllDevices() {
531 // Send optical first, others later for layered rendering
532 for (Device device : deviceService.getDevices()) {
533 if (device.type() == Device.Type.ROADM) {
534 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
535 }
536 }
537 for (Device device : deviceService.getDevices()) {
538 if (device.type() != Device.Type.ROADM) {
539 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
540 }
541 }
542 }
543
544 // Sends all links to the client as link-added messages.
545 private void sendAllLinks() {
546 // Send optical first, others later for layered rendering
547 for (Link link : linkService.getLinks()) {
548 if (link.type() == Link.Type.OPTICAL) {
549 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
550 }
551 }
552 for (Link link : linkService.getLinks()) {
553 if (link.type() != Link.Type.OPTICAL) {
554 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
555 }
556 }
557 }
558
559 // Sends all hosts to the client as host-added messages.
560 private void sendAllHosts() {
561 for (Host host : hostService.getHosts()) {
562 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
563 }
564 }
565
Thomas Vachuska329af532015-03-10 02:08:33 -0700566
Simon Huntd2747a02015-04-30 22:41:16 -0700567 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700568 selectedHosts = new HashSet<>();
569 selectedDevices = new HashSet<>();
570 selectedIntents = new ArrayList<>();
571 selectedIntents.add(intent);
572 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700573 requestAnotherRelatedIntent(+1);
574 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700575 }
576
577
578 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
579 Set<ConnectPoint> points = new HashSet<>();
580 for (HostId hostId : hostIds) {
581 points.add(getHostLocation(hostId));
582 }
583 return points;
584 }
585
586 private HostLocation getHostLocation(HostId hostId) {
587 return hostService.getHost(hostId).location();
588 }
589
590 // Produces a list of host ids from the specified JSON array.
591 private Set<HostId> getHostIds(ArrayNode ids) {
592 Set<HostId> hostIds = new HashSet<>();
593 for (JsonNode id : ids) {
594 hostIds.add(hostId(id.asText()));
595 }
596 return hostIds;
597 }
598
599
Simon Huntd2747a02015-04-30 22:41:16 -0700600 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700601 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700602 trafficTask = new TrafficMonitor();
603 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700604 }
605
606 private synchronized void stopTrafficMonitoring() {
607 if (trafficTask != null) {
608 trafficTask.cancel();
609 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700610 }
611 }
612
613 // Subscribes for host traffic messages.
Simon Huntd2747a02015-04-30 22:41:16 -0700614 private synchronized void requestAllTraffic() {
615 startTrafficMonitoring();
616 sendMessage(trafficSummaryMessage());
Thomas Vachuska329af532015-03-10 02:08:33 -0700617 }
618
Simon Huntd2747a02015-04-30 22:41:16 -0700619 private void requestDeviceLinkFlows(ObjectNode payload) {
620 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700621
622 // Get the set of selected hosts and their intents.
623 ArrayNode ids = (ArrayNode) payload.path("ids");
624 Set<Host> hosts = new HashSet<>();
625 Set<Device> devices = getDevices(ids);
626
627 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700628 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700629 if (!isNullOrEmpty(hover)) {
630 addHover(hosts, devices, hover);
631 }
Simon Huntd2747a02015-04-30 22:41:16 -0700632 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700633 }
634
635
Thomas Vachuska329af532015-03-10 02:08:33 -0700636 private boolean haveSelectedIntents() {
637 return selectedIntents != null && !selectedIntents.isEmpty();
638 }
639
640 // Processes the selection extended with hovered item to segregate items
641 // into primary (those including the hover) vs secondary highlights.
642 private void processHoverExtendedSelection(long sid, String hover) {
643 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
644 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
645 addHover(hoverSelHosts, hoverSelDevices, hover);
646
647 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
648 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
649 selectedIntents);
650 Set<Intent> secondary = new HashSet<>(selectedIntents);
651 secondary.removeAll(primary);
652
653 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700654 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700655 new TrafficClass("secondary", secondary)));
656 }
657
658 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700659 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700660 if (haveSelectedIntents()) {
661 currentIntentIndex = currentIntentIndex + offset;
662 if (currentIntentIndex < 0) {
663 currentIntentIndex = selectedIntents.size() - 1;
664 } else if (currentIntentIndex >= selectedIntents.size()) {
665 currentIntentIndex = 0;
666 }
Simon Huntd2747a02015-04-30 22:41:16 -0700667 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700668 }
669 }
670
671 // Sends traffic information on the related intents with the currently
672 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700673 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700674 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
675 log.info("Requested next intent {}", selectedIntent.id());
676
677 Set<Intent> primary = new HashSet<>();
678 primary.add(selectedIntent);
679
680 Set<Intent> secondary = new HashSet<>(selectedIntents);
681 secondary.remove(selectedIntent);
682
683 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700684 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700685 new TrafficClass("secondary", secondary)));
686 }
687
688 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700689 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700690 if (haveSelectedIntents()) {
691 if (currentIntentIndex < 0) {
692 currentIntentIndex = 0;
693 }
694 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
695 log.info("Requested traffic for selected {}", selectedIntent.id());
696
697 Set<Intent> primary = new HashSet<>();
698 primary.add(selectedIntent);
699
700 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700701 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700702 }
703 }
704
Simon Huntd2747a02015-04-30 22:41:16 -0700705 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700706 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700707 summaryTask = new SummaryMonitor();
708 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700709 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700710 }
711
712 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700713 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700714 summaryTask.cancel();
715 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 }
Simon Huntd2747a02015-04-30 22:41:16 -0700717 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700718 }
719
Thomas Vachuska329af532015-03-10 02:08:33 -0700720
721 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700722 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700723 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700724 clusterService.addListener(clusterListener);
725 mastershipService.addListener(mastershipListener);
726 deviceService.addListener(deviceListener);
727 linkService.addListener(linkListener);
728 hostService.addListener(hostListener);
729 intentService.addListener(intentListener);
730 flowService.addListener(flowListener);
731 }
732
733 // Removes all internal listeners.
734 private synchronized void removeListeners() {
735 if (!listenersRemoved) {
736 listenersRemoved = true;
737 clusterService.removeListener(clusterListener);
738 mastershipService.removeListener(mastershipListener);
739 deviceService.removeListener(deviceListener);
740 linkService.removeListener(linkListener);
741 hostService.removeListener(hostListener);
742 intentService.removeListener(intentListener);
743 flowService.removeListener(flowListener);
744 }
745 }
746
747 // Cluster event listener.
748 private class InternalClusterListener implements ClusterEventListener {
749 @Override
750 public void event(ClusterEvent event) {
751 sendMessage(instanceMessage(event, null));
752 }
753 }
754
755 // Mastership change listener
756 private class InternalMastershipListener implements MastershipListener {
757 @Override
758 public void event(MastershipEvent event) {
759 sendAllInstances("updateInstance");
760 Device device = deviceService.getDevice(event.subject());
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700761 if (device != null) {
762 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
763 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700764 }
765 }
766
767 // Device event listener.
768 private class InternalDeviceListener implements DeviceListener {
769 @Override
770 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700771 if (event.type() != PORT_STATS_UPDATED) {
772 sendMessage(deviceMessage(event));
773 eventAccummulator.add(event);
774 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700775 }
776 }
777
778 // Link event listener.
779 private class InternalLinkListener implements LinkListener {
780 @Override
781 public void event(LinkEvent event) {
782 sendMessage(linkMessage(event));
783 eventAccummulator.add(event);
784 }
785 }
786
787 // Host event listener.
788 private class InternalHostListener implements HostListener {
789 @Override
790 public void event(HostEvent event) {
791 sendMessage(hostMessage(event));
792 eventAccummulator.add(event);
793 }
794 }
795
796 // Intent event listener.
797 private class InternalIntentListener implements IntentListener {
798 @Override
799 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700800 if (trafficTask != null) {
801 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700802 }
803 eventAccummulator.add(event);
804 }
805 }
806
807 // Intent event listener.
808 private class InternalFlowListener implements FlowRuleListener {
809 @Override
810 public void event(FlowRuleEvent event) {
811 eventAccummulator.add(event);
812 }
813 }
814
Simon Huntd2747a02015-04-30 22:41:16 -0700815 // encapsulate
816 private static class TrafficEvent {
817 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
818
819 private final Type type;
820 private final ObjectNode payload;
821
822 TrafficEvent(Type type, ObjectNode payload) {
823 this.type = type;
824 this.payload = payload;
825 }
826 }
827
Thomas Vachuska329af532015-03-10 02:08:33 -0700828 // Periodic update of the traffic information
829 private class TrafficMonitor extends TimerTask {
830 @Override
831 public void run() {
832 try {
833 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700834 switch (trafficEvent.type) {
835 case ALL_TRAFFIC:
836 requestAllTraffic();
837 break;
838 case DEV_LINK_FLOWS:
839 requestDeviceLinkFlows(trafficEvent.payload);
840 break;
841 case SEL_INTENT:
842 requestSelectedIntentTraffic();
843 break;
844 default:
845 // nothing to do
846 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700847 }
848 }
849 } catch (Exception e) {
850 log.warn("Unable to handle traffic request due to {}", e.getMessage());
851 log.warn("Boom!", e);
852 }
853 }
854 }
855
856 // Periodic update of the summary information
857 private class SummaryMonitor extends TimerTask {
858 @Override
859 public void run() {
860 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700861 if (summaryRunning) {
862 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700863 }
864 } catch (Exception e) {
865 log.warn("Unable to handle summary request due to {}", e.getMessage());
866 log.warn("Boom!", e);
867 }
868 }
869 }
870
871 // Accumulates events to drive methodic update of the summary pane.
872 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
873 protected InternalEventAccummulator() {
874 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
875 }
876
877 @Override
878 public void processItems(List<Event> items) {
879 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700880 if (summaryRunning) {
881 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700882 }
883 } catch (Exception e) {
884 log.warn("Unable to handle summary request due to {}", e.getMessage());
885 log.debug("Boom!", e);
886 }
887 }
888 }
889}