blob: ddc61f89bf0c0eb4d5a123885e8bea64250dff9d [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;
Simon Hunt0af1ec32015-07-24 12:17:55 -070060import org.onosproject.ui.topo.PropertyPanel;
Thomas Vachuska329af532015-03-10 02:08:33 -070061
62import java.util.ArrayList;
Simon Huntd2747a02015-04-30 22:41:16 -070063import java.util.Collection;
Thomas Vachuska329af532015-03-10 02:08:33 -070064import java.util.Collections;
65import java.util.Comparator;
66import java.util.HashSet;
67import java.util.List;
68import java.util.Set;
69import java.util.Timer;
70import java.util.TimerTask;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070071import java.util.concurrent.ExecutorService;
Thomas Vachuska329af532015-03-10 02:08:33 -070072
73import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070074import static java.util.concurrent.Executors.newSingleThreadExecutor;
75import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska329af532015-03-10 02:08:33 -070076import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
77import static org.onosproject.net.DeviceId.deviceId;
78import static org.onosproject.net.HostId.hostId;
Thomas Vachuskacb5016f2015-05-18 14:11:43 -070079import static org.onosproject.net.device.DeviceEvent.Type.*;
Thomas Vachuska329af532015-03-10 02:08:33 -070080import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
81import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
82
83/**
84 * Web socket capable of interacting with the GUI topology view.
85 */
86public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
87
Simon Huntd2747a02015-04-30 22:41:16 -070088 private static final String REQ_DETAILS = "requestDetails";
89 private static final String UPDATE_META = "updateMeta";
90 private static final String ADD_HOST_INTENT = "addHostIntent";
91 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
92 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
93 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
94 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
95 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070096 private static final String REQ_ALL_FLOW_TRAFFIC = "requestAllFlowTraffic";
97 private static final String REQ_ALL_PORT_TRAFFIC = "requestAllPortTraffic";
Simon Huntd2747a02015-04-30 22:41:16 -070098 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
99 private static final String CANCEL_TRAFFIC = "cancelTraffic";
100 private static final String REQ_SUMMARY = "requestSummary";
101 private static final String CANCEL_SUMMARY = "cancelSummary";
102 private static final String EQ_MASTERS = "equalizeMasters";
103 private static final String SPRITE_LIST_REQ = "spriteListRequest";
104 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
105 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700106 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Hunte05cae42015-07-23 17:35:24 -0700107 private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
Simon Huntd2747a02015-04-30 22:41:16 -0700108 private static final String TOPO_STOP = "topoStop";
109
110
Thomas Vachuska329af532015-03-10 02:08:33 -0700111 private static final String APP_ID = "org.onosproject.gui";
112
113 private static final long TRAFFIC_FREQUENCY = 5000;
114 private static final long SUMMARY_FREQUENCY = 30000;
115
116 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700117 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700118
119
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700120 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700121
122 private static final int MAX_EVENTS = 1000;
123 private static final int MAX_BATCH_MS = 5000;
124 private static final int MAX_IDLE_MS = 1000;
125
126 private ApplicationId appId;
127
128 private final ClusterEventListener clusterListener = new InternalClusterListener();
129 private final MastershipListener mastershipListener = new InternalMastershipListener();
130 private final DeviceListener deviceListener = new InternalDeviceListener();
131 private final LinkListener linkListener = new InternalLinkListener();
132 private final HostListener hostListener = new InternalHostListener();
133 private final IntentListener intentListener = new InternalIntentListener();
134 private final FlowRuleListener flowListener = new InternalFlowListener();
135
136 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700137 private final ExecutorService msgSender =
138 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700139
Simon Hunte05cae42015-07-23 17:35:24 -0700140 private TopoOverlayCache overlayCache;
141
Simon Huntd2747a02015-04-30 22:41:16 -0700142 private TimerTask trafficTask = null;
143 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700144
Simon Huntd2747a02015-04-30 22:41:16 -0700145 private TimerTask summaryTask = null;
146 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700147
148 private boolean listenersRemoved = false;
149
150 private TopologyViewIntentFilter intentFilter;
151
152 // Current selection context
153 private Set<Host> selectedHosts;
154 private Set<Device> selectedDevices;
155 private List<Intent> selectedIntents;
156 private int currentIntentIndex = -1;
157
Thomas Vachuska329af532015-03-10 02:08:33 -0700158
159 @Override
160 public void init(UiConnection connection, ServiceDirectory directory) {
161 super.init(connection, directory);
162 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
163 hostService, linkService);
164 appId = directory.get(CoreService.class).registerApplication(APP_ID);
165 }
166
167 @Override
168 public void destroy() {
169 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700170 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700171 super.destroy();
172 }
173
Thomas Vachuska329af532015-03-10 02:08:33 -0700174 @Override
Simon Huntda580882015-05-12 20:58:18 -0700175 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700176 return ImmutableSet.of(
177 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700178 new TopoHeartbeat(),
Simon Hunte05cae42015-07-23 17:35:24 -0700179 new TopoSelectOverlay(),
Simon Huntd2747a02015-04-30 22:41:16 -0700180 new TopoStop(),
181 new ReqSummary(),
182 new CancelSummary(),
183 new SpriteListReq(),
184 new SpriteDataReq(),
185 new RequestDetails(),
186 new UpdateMeta(),
187 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700188
Simon Huntd2747a02015-04-30 22:41:16 -0700189 // TODO: migrate traffic related to separate app
190 new AddHostIntent(),
191 new AddMultiSourceIntent(),
192 new ReqRelatedIntents(),
193 new ReqNextIntent(),
194 new ReqPrevIntent(),
195 new ReqSelectedIntentTraffic(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700196 new ReqAllFlowTraffic(),
197 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700198 new ReqDevLinkFlows(),
199 new CancelTraffic()
200 );
201 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700202
Simon Hunte05cae42015-07-23 17:35:24 -0700203 /**
204 * Injects the topology overlay cache.
205 *
206 * @param overlayCache injected cache
207 */
208 void setOverlayCache(TopoOverlayCache overlayCache) {
209 this.overlayCache = overlayCache;
210 }
211
Simon Huntd2747a02015-04-30 22:41:16 -0700212 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700213
Simon Huntd2747a02015-04-30 22:41:16 -0700214 private final class TopoStart extends RequestHandler {
215 private TopoStart() {
216 super(TOPO_START);
217 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700218
Simon Huntd2747a02015-04-30 22:41:16 -0700219 @Override
220 public void process(long sid, ObjectNode payload) {
221 addListeners();
222 sendAllInstances(null);
223 sendAllDevices();
224 sendAllLinks();
225 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700226 }
227 }
228
Simon Hunt732bb2e2015-05-13 18:32:16 -0700229 private final class TopoHeartbeat extends RequestHandler {
230 private TopoHeartbeat() {
231 super(TOPO_HEARTBEAT);
232 }
233
234 @Override
235 public void process(long sid, ObjectNode payload) {
236 // place holder for now
237 }
238 }
239
Simon Hunte05cae42015-07-23 17:35:24 -0700240 private final class TopoSelectOverlay extends RequestHandler {
241 private TopoSelectOverlay() {
242 super(TOPO_SELECT_OVERLAY);
243 }
244
245 @Override
246 public void process(long sid, ObjectNode payload) {
247 String deact = string(payload, "deactivate");
248 String act = string(payload, "activate");
249 overlayCache.switchOverlay(deact, act);
250 }
251 }
252
Simon Huntd2747a02015-04-30 22:41:16 -0700253 private final class TopoStop extends RequestHandler {
254 private TopoStop() {
255 super(TOPO_STOP);
256 }
257
258 @Override
259 public void process(long sid, ObjectNode payload) {
260 stopSummaryMonitoring();
261 stopTrafficMonitoring();
262 }
263 }
264
265 private final class ReqSummary extends RequestHandler {
266 private ReqSummary() {
267 super(REQ_SUMMARY);
268 }
269
270 @Override
271 public void process(long sid, ObjectNode payload) {
272 requestSummary(sid);
273 startSummaryMonitoring();
274 }
275 }
276
277 private final class CancelSummary extends RequestHandler {
278 private CancelSummary() {
279 super(CANCEL_SUMMARY);
280 }
281
282 @Override
283 public void process(long sid, ObjectNode payload) {
284 stopSummaryMonitoring();
285 }
286 }
287
288 private final class SpriteListReq extends RequestHandler {
289 private SpriteListReq() {
290 super(SPRITE_LIST_REQ);
291 }
292
293 @Override
294 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700295 ObjectNode root = objectNode();
296 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700297 get(SpriteService.class).getNames().forEach(names::add);
298 root.set("names", names);
299 sendMessage("spriteListResponse", sid, root);
300 }
301 }
302
303 private final class SpriteDataReq extends RequestHandler {
304 private SpriteDataReq() {
305 super(SPRITE_DATA_REQ);
306 }
307
308 @Override
309 public void process(long sid, ObjectNode payload) {
310 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700311 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700312 root.set("data", get(SpriteService.class).get(name));
313 sendMessage("spriteDataResponse", sid, root);
314 }
315 }
316
317 private final class RequestDetails extends RequestHandler {
318 private RequestDetails() {
319 super(REQ_DETAILS);
320 }
321
322 @Override
323 public void process(long sid, ObjectNode payload) {
324 String type = string(payload, "class", "unknown");
Simon Hunte05cae42015-07-23 17:35:24 -0700325 String id = string(payload, "id");
Simon Huntd2747a02015-04-30 22:41:16 -0700326
327 if (type.equals("device")) {
328 sendMessage(deviceDetails(deviceId(id), sid));
329 } else if (type.equals("host")) {
330 sendMessage(hostDetails(hostId(id), sid));
331 }
332 }
333 }
334
335 private final class UpdateMeta extends RequestHandler {
336 private UpdateMeta() {
337 super(UPDATE_META);
338 }
339
340 @Override
341 public void process(long sid, ObjectNode payload) {
342 updateMetaUi(payload);
343 }
344 }
345
346 private final class EqMasters extends RequestHandler {
347 private EqMasters() {
348 super(EQ_MASTERS);
349 }
350
351 @Override
352 public void process(long sid, ObjectNode payload) {
353 directory.get(MastershipAdminService.class).balanceRoles();
354 }
355 }
356
357 // === TODO: move traffic related classes to traffic app
358
359 private final class AddHostIntent extends RequestHandler {
360 private AddHostIntent() {
361 super(ADD_HOST_INTENT);
362 }
363
364 @Override
365 public void process(long sid, ObjectNode payload) {
366 // TODO: add protection against device ids and non-existent hosts.
367 HostId one = hostId(string(payload, "one"));
368 HostId two = hostId(string(payload, "two"));
369
370 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700371 .appId(appId)
372 .one(one)
373 .two(two)
374 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700375
376 intentService.submit(intent);
377 startMonitoringIntent(intent);
378 }
379 }
380
381 private final class AddMultiSourceIntent extends RequestHandler {
382 private AddMultiSourceIntent() {
383 super(ADD_MULTI_SRC_INTENT);
384 }
385
386 @Override
387 public void process(long sid, ObjectNode payload) {
388 // TODO: add protection against device ids and non-existent hosts.
389 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
390 HostId dst = hostId(string(payload, "dst"));
391 Host dstHost = hostService.getHost(dst);
392
393 Set<ConnectPoint> ingressPoints = getHostLocations(src);
394
395 // FIXME: clearly, this is not enough
396 TrafficSelector selector = DefaultTrafficSelector.builder()
397 .matchEthDst(dstHost.mac()).build();
398 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
399
400 MultiPointToSinglePointIntent intent =
401 MultiPointToSinglePointIntent.builder()
402 .appId(appId)
403 .selector(selector)
404 .treatment(treatment)
405 .ingressPoints(ingressPoints)
406 .egressPoint(dstHost.location())
407 .build();
408
409 intentService.submit(intent);
410 startMonitoringIntent(intent);
411 }
412 }
413
414 private final class ReqRelatedIntents extends RequestHandler {
415 private ReqRelatedIntents() {
416 super(REQ_RELATED_INTENTS);
417 }
418
419 @Override
420 public void process(long sid, ObjectNode payload) {
421 // Cancel any other traffic monitoring mode.
422 stopTrafficMonitoring();
423
424 if (!payload.has("ids")) {
425 return;
426 }
427
428 // Get the set of selected hosts and their intents.
429 ArrayNode ids = (ArrayNode) payload.path("ids");
430 selectedHosts = getHosts(ids);
431 selectedDevices = getDevices(ids);
432 selectedIntents = intentFilter.findPathIntents(
433 selectedHosts, selectedDevices, intentService.getIntents());
434 currentIntentIndex = -1;
435
436 if (haveSelectedIntents()) {
437 // Send a message to highlight all links of all monitored intents.
438 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
439 }
440
441 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
442// String hover = string(payload, "hover");
443// if (!isNullOrEmpty(hover)) {
444// // If there is a hover node, include it in the selection and find intents.
445// processHoverExtendedSelection(sid, hover);
446// }
447 }
448 }
449
450 private final class ReqNextIntent extends RequestHandler {
451 private ReqNextIntent() {
452 super(REQ_NEXT_INTENT);
453 }
454
455 @Override
456 public void process(long sid, ObjectNode payload) {
457 stopTrafficMonitoring();
458 requestAnotherRelatedIntent(+1);
459 }
460 }
461
462 private final class ReqPrevIntent extends RequestHandler {
463 private ReqPrevIntent() {
464 super(REQ_PREV_INTENT);
465 }
466
467 @Override
468 public void process(long sid, ObjectNode payload) {
469 stopTrafficMonitoring();
470 requestAnotherRelatedIntent(-1);
471 }
472 }
473
474 private final class ReqSelectedIntentTraffic extends RequestHandler {
475 private ReqSelectedIntentTraffic() {
476 super(REQ_SEL_INTENT_TRAFFIC);
477 }
478
479 @Override
480 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700481 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700482 requestSelectedIntentTraffic();
483 startTrafficMonitoring();
484 }
485 }
486
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700487 private final class ReqAllFlowTraffic extends RequestHandler {
488 private ReqAllFlowTraffic() {
489 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700490 }
491
492 @Override
493 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700494 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
495 requestAllFlowTraffic();
496 }
497 }
498
499 private final class ReqAllPortTraffic extends RequestHandler {
500 private ReqAllPortTraffic() {
501 super(REQ_ALL_PORT_TRAFFIC);
502 }
503
504 @Override
505 public void process(long sid, ObjectNode payload) {
506 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
507 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700508 }
509 }
510
511 private final class ReqDevLinkFlows extends RequestHandler {
512 private ReqDevLinkFlows() {
513 super(REQ_DEV_LINK_FLOWS);
514 }
515
516 @Override
517 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700518 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700519 requestDeviceLinkFlows(payload);
520 }
521 }
522
523 private final class CancelTraffic extends RequestHandler {
524 private CancelTraffic() {
525 super(CANCEL_TRAFFIC);
526 }
527
528 @Override
529 public void process(long sid, ObjectNode payload) {
530 selectedIntents = null;
531 sendMessage(trafficMessage());
532 stopTrafficMonitoring();
533 }
534 }
535
536 //=======================================================================
537
538
Thomas Vachuska329af532015-03-10 02:08:33 -0700539 // Sends the specified data to the client.
540 protected synchronized void sendMessage(ObjectNode data) {
541 UiConnection connection = connection();
542 if (connection != null) {
543 connection.sendMessage(data);
544 }
545 }
546
Simon Huntd2747a02015-04-30 22:41:16 -0700547 // Subscribes for summary messages.
548 private synchronized void requestSummary(long sid) {
Simon Hunt0af1ec32015-07-24 12:17:55 -0700549 PropertyPanel pp = summmaryMessage(sid);
550 overlayCache.currentOverlay().modifySummary(pp);
551 ObjectNode json = JsonUtils.envelope("showSummary", sid, json(pp));
552 sendMessage(json);
Thomas Vachuska329af532015-03-10 02:08:33 -0700553 }
554
Simon Huntd2747a02015-04-30 22:41:16 -0700555
Thomas Vachuska329af532015-03-10 02:08:33 -0700556 private void cancelAllRequests() {
557 stopSummaryMonitoring();
558 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700559 }
560
561 // Sends all controller nodes to the client as node-added messages.
562 private void sendAllInstances(String messageType) {
563 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
564 Collections.sort(nodes, NODE_COMPARATOR);
565 for (ControllerNode node : nodes) {
566 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
567 messageType));
568 }
569 }
570
571 // Sends all devices to the client as device-added messages.
572 private void sendAllDevices() {
573 // Send optical first, others later for layered rendering
574 for (Device device : deviceService.getDevices()) {
575 if (device.type() == Device.Type.ROADM) {
576 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
577 }
578 }
579 for (Device device : deviceService.getDevices()) {
580 if (device.type() != Device.Type.ROADM) {
581 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
582 }
583 }
584 }
585
586 // Sends all links to the client as link-added messages.
587 private void sendAllLinks() {
588 // Send optical first, others later for layered rendering
589 for (Link link : linkService.getLinks()) {
590 if (link.type() == Link.Type.OPTICAL) {
591 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
592 }
593 }
594 for (Link link : linkService.getLinks()) {
595 if (link.type() != Link.Type.OPTICAL) {
596 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
597 }
598 }
599 }
600
601 // Sends all hosts to the client as host-added messages.
602 private void sendAllHosts() {
603 for (Host host : hostService.getHosts()) {
604 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
605 }
606 }
607
Thomas Vachuska329af532015-03-10 02:08:33 -0700608
Simon Huntd2747a02015-04-30 22:41:16 -0700609 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700610 selectedHosts = new HashSet<>();
611 selectedDevices = new HashSet<>();
612 selectedIntents = new ArrayList<>();
613 selectedIntents.add(intent);
614 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700615 requestAnotherRelatedIntent(+1);
616 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700617 }
618
619
620 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
621 Set<ConnectPoint> points = new HashSet<>();
622 for (HostId hostId : hostIds) {
623 points.add(getHostLocation(hostId));
624 }
625 return points;
626 }
627
628 private HostLocation getHostLocation(HostId hostId) {
629 return hostService.getHost(hostId).location();
630 }
631
632 // Produces a list of host ids from the specified JSON array.
633 private Set<HostId> getHostIds(ArrayNode ids) {
634 Set<HostId> hostIds = new HashSet<>();
635 for (JsonNode id : ids) {
636 hostIds.add(hostId(id.asText()));
637 }
638 return hostIds;
639 }
640
641
Simon Huntd2747a02015-04-30 22:41:16 -0700642 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700643 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700644 trafficTask = new TrafficMonitor();
645 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700646 }
647
648 private synchronized void stopTrafficMonitoring() {
649 if (trafficTask != null) {
650 trafficTask.cancel();
651 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700652 }
653 }
654
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700655 // Subscribes for flow traffic messages.
656 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700657 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700658 sendMessage(trafficSummaryMessage(StatsType.FLOW));
659 }
660
661 // Subscribes for port traffic messages.
662 private synchronized void requestAllPortTraffic() {
663 startTrafficMonitoring();
664 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700665 }
666
Simon Huntd2747a02015-04-30 22:41:16 -0700667 private void requestDeviceLinkFlows(ObjectNode payload) {
668 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700669
670 // Get the set of selected hosts and their intents.
671 ArrayNode ids = (ArrayNode) payload.path("ids");
672 Set<Host> hosts = new HashSet<>();
673 Set<Device> devices = getDevices(ids);
674
675 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700676 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700677 if (!isNullOrEmpty(hover)) {
678 addHover(hosts, devices, hover);
679 }
Simon Huntd2747a02015-04-30 22:41:16 -0700680 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700681 }
682
683
Thomas Vachuska329af532015-03-10 02:08:33 -0700684 private boolean haveSelectedIntents() {
685 return selectedIntents != null && !selectedIntents.isEmpty();
686 }
687
688 // Processes the selection extended with hovered item to segregate items
689 // into primary (those including the hover) vs secondary highlights.
690 private void processHoverExtendedSelection(long sid, String hover) {
691 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
692 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
693 addHover(hoverSelHosts, hoverSelDevices, hover);
694
695 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
696 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
697 selectedIntents);
698 Set<Intent> secondary = new HashSet<>(selectedIntents);
699 secondary.removeAll(primary);
700
701 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700702 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700703 new TrafficClass("secondary", secondary)));
704 }
705
706 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700707 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 if (haveSelectedIntents()) {
709 currentIntentIndex = currentIntentIndex + offset;
710 if (currentIntentIndex < 0) {
711 currentIntentIndex = selectedIntents.size() - 1;
712 } else if (currentIntentIndex >= selectedIntents.size()) {
713 currentIntentIndex = 0;
714 }
Simon Huntd2747a02015-04-30 22:41:16 -0700715 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 }
717 }
718
719 // Sends traffic information on the related intents with the currently
720 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700721 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700722 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
723 log.info("Requested next intent {}", selectedIntent.id());
724
725 Set<Intent> primary = new HashSet<>();
726 primary.add(selectedIntent);
727
728 Set<Intent> secondary = new HashSet<>(selectedIntents);
729 secondary.remove(selectedIntent);
730
731 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700732 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700733 new TrafficClass("secondary", secondary)));
734 }
735
736 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700737 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700738 if (haveSelectedIntents()) {
739 if (currentIntentIndex < 0) {
740 currentIntentIndex = 0;
741 }
742 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
743 log.info("Requested traffic for selected {}", selectedIntent.id());
744
745 Set<Intent> primary = new HashSet<>();
746 primary.add(selectedIntent);
747
748 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700749 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700750 }
751 }
752
Simon Huntd2747a02015-04-30 22:41:16 -0700753 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700754 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700755 summaryTask = new SummaryMonitor();
756 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700757 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700758 }
759
760 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700761 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700762 summaryTask.cancel();
763 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700764 }
Simon Huntd2747a02015-04-30 22:41:16 -0700765 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700766 }
767
Thomas Vachuska329af532015-03-10 02:08:33 -0700768
769 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700770 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700771 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700772 clusterService.addListener(clusterListener);
773 mastershipService.addListener(mastershipListener);
774 deviceService.addListener(deviceListener);
775 linkService.addListener(linkListener);
776 hostService.addListener(hostListener);
777 intentService.addListener(intentListener);
778 flowService.addListener(flowListener);
779 }
780
781 // Removes all internal listeners.
782 private synchronized void removeListeners() {
783 if (!listenersRemoved) {
784 listenersRemoved = true;
785 clusterService.removeListener(clusterListener);
786 mastershipService.removeListener(mastershipListener);
787 deviceService.removeListener(deviceListener);
788 linkService.removeListener(linkListener);
789 hostService.removeListener(hostListener);
790 intentService.removeListener(intentListener);
791 flowService.removeListener(flowListener);
792 }
793 }
794
795 // Cluster event listener.
796 private class InternalClusterListener implements ClusterEventListener {
797 @Override
798 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700799 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700800 }
801 }
802
803 // Mastership change listener
804 private class InternalMastershipListener implements MastershipListener {
805 @Override
806 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700807 msgSender.execute(() -> {
808 sendAllInstances("updateInstance");
809 Device device = deviceService.getDevice(event.subject());
810 if (device != null) {
811 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
812 }
813 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700814 }
815 }
816
817 // Device event listener.
818 private class InternalDeviceListener implements DeviceListener {
819 @Override
820 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700821 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700822 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700823 eventAccummulator.add(event);
824 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700825 }
826 }
827
828 // Link event listener.
829 private class InternalLinkListener implements LinkListener {
830 @Override
831 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700832 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700833 eventAccummulator.add(event);
834 }
835 }
836
837 // Host event listener.
838 private class InternalHostListener implements HostListener {
839 @Override
840 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700841 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700842 eventAccummulator.add(event);
843 }
844 }
845
846 // Intent event listener.
847 private class InternalIntentListener implements IntentListener {
848 @Override
849 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700850 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700851 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700852 }
853 eventAccummulator.add(event);
854 }
855 }
856
857 // Intent event listener.
858 private class InternalFlowListener implements FlowRuleListener {
859 @Override
860 public void event(FlowRuleEvent event) {
861 eventAccummulator.add(event);
862 }
863 }
864
Simon Huntd2747a02015-04-30 22:41:16 -0700865 // encapsulate
866 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700867 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700868 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700869 }
Simon Huntd2747a02015-04-30 22:41:16 -0700870
871 private final Type type;
872 private final ObjectNode payload;
873
874 TrafficEvent(Type type, ObjectNode payload) {
875 this.type = type;
876 this.payload = payload;
877 }
878 }
879
Thomas Vachuska329af532015-03-10 02:08:33 -0700880 // Periodic update of the traffic information
881 private class TrafficMonitor extends TimerTask {
882 @Override
883 public void run() {
884 try {
885 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700886 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700887 case ALL_FLOW_TRAFFIC:
888 requestAllFlowTraffic();
889 break;
890 case ALL_PORT_TRAFFIC:
891 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700892 break;
893 case DEV_LINK_FLOWS:
894 requestDeviceLinkFlows(trafficEvent.payload);
895 break;
896 case SEL_INTENT:
897 requestSelectedIntentTraffic();
898 break;
899 default:
900 // nothing to do
901 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700902 }
903 }
904 } catch (Exception e) {
905 log.warn("Unable to handle traffic request due to {}", e.getMessage());
906 log.warn("Boom!", e);
907 }
908 }
909 }
910
911 // Periodic update of the summary information
912 private class SummaryMonitor extends TimerTask {
913 @Override
914 public void run() {
915 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700916 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700917 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700918 }
919 } catch (Exception e) {
920 log.warn("Unable to handle summary request due to {}", e.getMessage());
921 log.warn("Boom!", e);
922 }
923 }
924 }
925
926 // Accumulates events to drive methodic update of the summary pane.
927 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
928 protected InternalEventAccummulator() {
929 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
930 }
931
932 @Override
933 public void processItems(List<Event> items) {
934 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700935 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700936 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700937 }
938 } catch (Exception e) {
939 log.warn("Unable to handle summary request due to {}", e.getMessage());
940 log.debug("Boom!", e);
941 }
942 }
943 }
944}