blob: 11839dfdcc3bbbc760ae76ab084c94a86c2c1654 [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
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700214 /**
215 * @deprecated in Cardinal Release
216 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700217 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700218 private final class TopoStart extends RequestHandler {
219 private TopoStart() {
220 super(TOPO_START);
221 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700222
Simon Huntd2747a02015-04-30 22:41:16 -0700223 @Override
224 public void process(long sid, ObjectNode payload) {
225 addListeners();
226 sendAllInstances(null);
227 sendAllDevices();
228 sendAllLinks();
229 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700230 }
231 }
232
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700233 /**
234 * @deprecated in Cardinal Release
235 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700236 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700237 private final class TopoHeartbeat extends RequestHandler {
238 private TopoHeartbeat() {
239 super(TOPO_HEARTBEAT);
240 }
241
242 @Override
243 public void process(long sid, ObjectNode payload) {
244 // place holder for now
245 }
246 }
247
Simon Hunte05cae42015-07-23 17:35:24 -0700248 private final class TopoSelectOverlay extends RequestHandler {
249 private TopoSelectOverlay() {
250 super(TOPO_SELECT_OVERLAY);
251 }
252
253 @Override
254 public void process(long sid, ObjectNode payload) {
255 String deact = string(payload, "deactivate");
256 String act = string(payload, "activate");
257 overlayCache.switchOverlay(deact, act);
258 }
259 }
260
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700261 /**
262 * @deprecated in Cardinal Release
263 */
Simon Hunt732bb2e2015-05-13 18:32:16 -0700264 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700265 private final class TopoStop extends RequestHandler {
266 private TopoStop() {
267 super(TOPO_STOP);
268 }
269
270 @Override
271 public void process(long sid, ObjectNode payload) {
272 stopSummaryMonitoring();
273 stopTrafficMonitoring();
274 }
275 }
276
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700277 /**
278 * @deprecated in Cardinal Release
279 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700280 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700281 private final class ReqSummary extends RequestHandler {
282 private ReqSummary() {
283 super(REQ_SUMMARY);
284 }
285
286 @Override
287 public void process(long sid, ObjectNode payload) {
288 requestSummary(sid);
289 startSummaryMonitoring();
290 }
291 }
292
293 private final class CancelSummary extends RequestHandler {
294 private CancelSummary() {
295 super(CANCEL_SUMMARY);
296 }
297
298 @Override
299 public void process(long sid, ObjectNode payload) {
300 stopSummaryMonitoring();
301 }
302 }
303
304 private final class SpriteListReq extends RequestHandler {
305 private SpriteListReq() {
306 super(SPRITE_LIST_REQ);
307 }
308
309 @Override
310 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700311 ObjectNode root = objectNode();
312 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700313 get(SpriteService.class).getNames().forEach(names::add);
314 root.set("names", names);
315 sendMessage("spriteListResponse", sid, root);
316 }
317 }
318
319 private final class SpriteDataReq extends RequestHandler {
320 private SpriteDataReq() {
321 super(SPRITE_DATA_REQ);
322 }
323
324 @Override
325 public void process(long sid, ObjectNode payload) {
326 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700327 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700328 root.set("data", get(SpriteService.class).get(name));
329 sendMessage("spriteDataResponse", sid, root);
330 }
331 }
332
333 private final class RequestDetails extends RequestHandler {
334 private RequestDetails() {
335 super(REQ_DETAILS);
336 }
337
338 @Override
339 public void process(long sid, ObjectNode payload) {
340 String type = string(payload, "class", "unknown");
Simon Hunte05cae42015-07-23 17:35:24 -0700341 String id = string(payload, "id");
Simon Huntd2747a02015-04-30 22:41:16 -0700342
343 if (type.equals("device")) {
344 sendMessage(deviceDetails(deviceId(id), sid));
345 } else if (type.equals("host")) {
346 sendMessage(hostDetails(hostId(id), sid));
347 }
348 }
349 }
350
351 private final class UpdateMeta extends RequestHandler {
352 private UpdateMeta() {
353 super(UPDATE_META);
354 }
355
356 @Override
357 public void process(long sid, ObjectNode payload) {
358 updateMetaUi(payload);
359 }
360 }
361
362 private final class EqMasters extends RequestHandler {
363 private EqMasters() {
364 super(EQ_MASTERS);
365 }
366
367 @Override
368 public void process(long sid, ObjectNode payload) {
369 directory.get(MastershipAdminService.class).balanceRoles();
370 }
371 }
372
373 // === TODO: move traffic related classes to traffic app
374
375 private final class AddHostIntent extends RequestHandler {
376 private AddHostIntent() {
377 super(ADD_HOST_INTENT);
378 }
379
380 @Override
381 public void process(long sid, ObjectNode payload) {
382 // TODO: add protection against device ids and non-existent hosts.
383 HostId one = hostId(string(payload, "one"));
384 HostId two = hostId(string(payload, "two"));
385
386 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700387 .appId(appId)
388 .one(one)
389 .two(two)
390 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700391
392 intentService.submit(intent);
393 startMonitoringIntent(intent);
394 }
395 }
396
397 private final class AddMultiSourceIntent extends RequestHandler {
398 private AddMultiSourceIntent() {
399 super(ADD_MULTI_SRC_INTENT);
400 }
401
402 @Override
403 public void process(long sid, ObjectNode payload) {
404 // TODO: add protection against device ids and non-existent hosts.
405 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
406 HostId dst = hostId(string(payload, "dst"));
407 Host dstHost = hostService.getHost(dst);
408
409 Set<ConnectPoint> ingressPoints = getHostLocations(src);
410
411 // FIXME: clearly, this is not enough
412 TrafficSelector selector = DefaultTrafficSelector.builder()
413 .matchEthDst(dstHost.mac()).build();
414 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
415
416 MultiPointToSinglePointIntent intent =
417 MultiPointToSinglePointIntent.builder()
418 .appId(appId)
419 .selector(selector)
420 .treatment(treatment)
421 .ingressPoints(ingressPoints)
422 .egressPoint(dstHost.location())
423 .build();
424
425 intentService.submit(intent);
426 startMonitoringIntent(intent);
427 }
428 }
429
430 private final class ReqRelatedIntents extends RequestHandler {
431 private ReqRelatedIntents() {
432 super(REQ_RELATED_INTENTS);
433 }
434
435 @Override
436 public void process(long sid, ObjectNode payload) {
437 // Cancel any other traffic monitoring mode.
438 stopTrafficMonitoring();
439
440 if (!payload.has("ids")) {
441 return;
442 }
443
444 // Get the set of selected hosts and their intents.
445 ArrayNode ids = (ArrayNode) payload.path("ids");
446 selectedHosts = getHosts(ids);
447 selectedDevices = getDevices(ids);
448 selectedIntents = intentFilter.findPathIntents(
449 selectedHosts, selectedDevices, intentService.getIntents());
450 currentIntentIndex = -1;
451
452 if (haveSelectedIntents()) {
453 // Send a message to highlight all links of all monitored intents.
454 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
455 }
456
457 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
458// String hover = string(payload, "hover");
459// if (!isNullOrEmpty(hover)) {
460// // If there is a hover node, include it in the selection and find intents.
461// processHoverExtendedSelection(sid, hover);
462// }
463 }
464 }
465
466 private final class ReqNextIntent extends RequestHandler {
467 private ReqNextIntent() {
468 super(REQ_NEXT_INTENT);
469 }
470
471 @Override
472 public void process(long sid, ObjectNode payload) {
473 stopTrafficMonitoring();
474 requestAnotherRelatedIntent(+1);
475 }
476 }
477
478 private final class ReqPrevIntent extends RequestHandler {
479 private ReqPrevIntent() {
480 super(REQ_PREV_INTENT);
481 }
482
483 @Override
484 public void process(long sid, ObjectNode payload) {
485 stopTrafficMonitoring();
486 requestAnotherRelatedIntent(-1);
487 }
488 }
489
490 private final class ReqSelectedIntentTraffic extends RequestHandler {
491 private ReqSelectedIntentTraffic() {
492 super(REQ_SEL_INTENT_TRAFFIC);
493 }
494
495 @Override
496 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700497 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700498 requestSelectedIntentTraffic();
499 startTrafficMonitoring();
500 }
501 }
502
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700503 private final class ReqAllFlowTraffic extends RequestHandler {
504 private ReqAllFlowTraffic() {
505 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700506 }
507
508 @Override
509 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700510 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
511 requestAllFlowTraffic();
512 }
513 }
514
515 private final class ReqAllPortTraffic extends RequestHandler {
516 private ReqAllPortTraffic() {
517 super(REQ_ALL_PORT_TRAFFIC);
518 }
519
520 @Override
521 public void process(long sid, ObjectNode payload) {
522 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
523 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700524 }
525 }
526
527 private final class ReqDevLinkFlows extends RequestHandler {
528 private ReqDevLinkFlows() {
529 super(REQ_DEV_LINK_FLOWS);
530 }
531
532 @Override
533 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700534 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700535 requestDeviceLinkFlows(payload);
536 }
537 }
538
539 private final class CancelTraffic extends RequestHandler {
540 private CancelTraffic() {
541 super(CANCEL_TRAFFIC);
542 }
543
544 @Override
545 public void process(long sid, ObjectNode payload) {
546 selectedIntents = null;
547 sendMessage(trafficMessage());
548 stopTrafficMonitoring();
549 }
550 }
551
552 //=======================================================================
553
554
Thomas Vachuska329af532015-03-10 02:08:33 -0700555 // Sends the specified data to the client.
556 protected synchronized void sendMessage(ObjectNode data) {
557 UiConnection connection = connection();
558 if (connection != null) {
559 connection.sendMessage(data);
560 }
561 }
562
Simon Huntd2747a02015-04-30 22:41:16 -0700563 // Subscribes for summary messages.
564 private synchronized void requestSummary(long sid) {
Simon Hunt0af1ec32015-07-24 12:17:55 -0700565 PropertyPanel pp = summmaryMessage(sid);
566 overlayCache.currentOverlay().modifySummary(pp);
567 ObjectNode json = JsonUtils.envelope("showSummary", sid, json(pp));
568 sendMessage(json);
Thomas Vachuska329af532015-03-10 02:08:33 -0700569 }
570
Simon Huntd2747a02015-04-30 22:41:16 -0700571
Thomas Vachuska329af532015-03-10 02:08:33 -0700572 private void cancelAllRequests() {
573 stopSummaryMonitoring();
574 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700575 }
576
577 // Sends all controller nodes to the client as node-added messages.
578 private void sendAllInstances(String messageType) {
579 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
580 Collections.sort(nodes, NODE_COMPARATOR);
581 for (ControllerNode node : nodes) {
582 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
583 messageType));
584 }
585 }
586
587 // Sends all devices to the client as device-added messages.
588 private void sendAllDevices() {
589 // Send optical first, others later for layered rendering
590 for (Device device : deviceService.getDevices()) {
591 if (device.type() == Device.Type.ROADM) {
592 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
593 }
594 }
595 for (Device device : deviceService.getDevices()) {
596 if (device.type() != Device.Type.ROADM) {
597 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
598 }
599 }
600 }
601
602 // Sends all links to the client as link-added messages.
603 private void sendAllLinks() {
604 // Send optical first, others later for layered rendering
605 for (Link link : linkService.getLinks()) {
606 if (link.type() == Link.Type.OPTICAL) {
607 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
608 }
609 }
610 for (Link link : linkService.getLinks()) {
611 if (link.type() != Link.Type.OPTICAL) {
612 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
613 }
614 }
615 }
616
617 // Sends all hosts to the client as host-added messages.
618 private void sendAllHosts() {
619 for (Host host : hostService.getHosts()) {
620 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
621 }
622 }
623
Thomas Vachuska329af532015-03-10 02:08:33 -0700624
Simon Huntd2747a02015-04-30 22:41:16 -0700625 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700626 selectedHosts = new HashSet<>();
627 selectedDevices = new HashSet<>();
628 selectedIntents = new ArrayList<>();
629 selectedIntents.add(intent);
630 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700631 requestAnotherRelatedIntent(+1);
632 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700633 }
634
635
636 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
637 Set<ConnectPoint> points = new HashSet<>();
638 for (HostId hostId : hostIds) {
639 points.add(getHostLocation(hostId));
640 }
641 return points;
642 }
643
644 private HostLocation getHostLocation(HostId hostId) {
645 return hostService.getHost(hostId).location();
646 }
647
648 // Produces a list of host ids from the specified JSON array.
649 private Set<HostId> getHostIds(ArrayNode ids) {
650 Set<HostId> hostIds = new HashSet<>();
651 for (JsonNode id : ids) {
652 hostIds.add(hostId(id.asText()));
653 }
654 return hostIds;
655 }
656
657
Simon Huntd2747a02015-04-30 22:41:16 -0700658 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700659 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700660 trafficTask = new TrafficMonitor();
661 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700662 }
663
664 private synchronized void stopTrafficMonitoring() {
665 if (trafficTask != null) {
666 trafficTask.cancel();
667 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700668 }
669 }
670
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700671 // Subscribes for flow traffic messages.
672 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700673 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700674 sendMessage(trafficSummaryMessage(StatsType.FLOW));
675 }
676
677 // Subscribes for port traffic messages.
678 private synchronized void requestAllPortTraffic() {
679 startTrafficMonitoring();
680 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700681 }
682
Simon Huntd2747a02015-04-30 22:41:16 -0700683 private void requestDeviceLinkFlows(ObjectNode payload) {
684 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700685
686 // Get the set of selected hosts and their intents.
687 ArrayNode ids = (ArrayNode) payload.path("ids");
688 Set<Host> hosts = new HashSet<>();
689 Set<Device> devices = getDevices(ids);
690
691 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700692 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700693 if (!isNullOrEmpty(hover)) {
694 addHover(hosts, devices, hover);
695 }
Simon Huntd2747a02015-04-30 22:41:16 -0700696 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700697 }
698
699
Thomas Vachuska329af532015-03-10 02:08:33 -0700700 private boolean haveSelectedIntents() {
701 return selectedIntents != null && !selectedIntents.isEmpty();
702 }
703
704 // Processes the selection extended with hovered item to segregate items
705 // into primary (those including the hover) vs secondary highlights.
706 private void processHoverExtendedSelection(long sid, String hover) {
707 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
708 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
709 addHover(hoverSelHosts, hoverSelDevices, hover);
710
711 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
712 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
713 selectedIntents);
714 Set<Intent> secondary = new HashSet<>(selectedIntents);
715 secondary.removeAll(primary);
716
717 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700718 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700719 new TrafficClass("secondary", secondary)));
720 }
721
722 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700723 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700724 if (haveSelectedIntents()) {
725 currentIntentIndex = currentIntentIndex + offset;
726 if (currentIntentIndex < 0) {
727 currentIntentIndex = selectedIntents.size() - 1;
728 } else if (currentIntentIndex >= selectedIntents.size()) {
729 currentIntentIndex = 0;
730 }
Simon Huntd2747a02015-04-30 22:41:16 -0700731 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700732 }
733 }
734
735 // Sends traffic information on the related intents with the currently
736 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700737 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700738 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
739 log.info("Requested next intent {}", selectedIntent.id());
740
741 Set<Intent> primary = new HashSet<>();
742 primary.add(selectedIntent);
743
744 Set<Intent> secondary = new HashSet<>(selectedIntents);
745 secondary.remove(selectedIntent);
746
747 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700748 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700749 new TrafficClass("secondary", secondary)));
750 }
751
752 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700753 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700754 if (haveSelectedIntents()) {
755 if (currentIntentIndex < 0) {
756 currentIntentIndex = 0;
757 }
758 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
759 log.info("Requested traffic for selected {}", selectedIntent.id());
760
761 Set<Intent> primary = new HashSet<>();
762 primary.add(selectedIntent);
763
764 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700765 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700766 }
767 }
768
Simon Huntd2747a02015-04-30 22:41:16 -0700769 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700770 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700771 summaryTask = new SummaryMonitor();
772 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700773 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700774 }
775
776 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700777 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700778 summaryTask.cancel();
779 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700780 }
Simon Huntd2747a02015-04-30 22:41:16 -0700781 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700782 }
783
Thomas Vachuska329af532015-03-10 02:08:33 -0700784
785 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700786 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700787 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700788 clusterService.addListener(clusterListener);
789 mastershipService.addListener(mastershipListener);
790 deviceService.addListener(deviceListener);
791 linkService.addListener(linkListener);
792 hostService.addListener(hostListener);
793 intentService.addListener(intentListener);
794 flowService.addListener(flowListener);
795 }
796
797 // Removes all internal listeners.
798 private synchronized void removeListeners() {
799 if (!listenersRemoved) {
800 listenersRemoved = true;
801 clusterService.removeListener(clusterListener);
802 mastershipService.removeListener(mastershipListener);
803 deviceService.removeListener(deviceListener);
804 linkService.removeListener(linkListener);
805 hostService.removeListener(hostListener);
806 intentService.removeListener(intentListener);
807 flowService.removeListener(flowListener);
808 }
809 }
810
811 // Cluster event listener.
812 private class InternalClusterListener implements ClusterEventListener {
813 @Override
814 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700815 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700816 }
817 }
818
819 // Mastership change listener
820 private class InternalMastershipListener implements MastershipListener {
821 @Override
822 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700823 msgSender.execute(() -> {
824 sendAllInstances("updateInstance");
825 Device device = deviceService.getDevice(event.subject());
826 if (device != null) {
827 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
828 }
829 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700830 }
831 }
832
833 // Device event listener.
834 private class InternalDeviceListener implements DeviceListener {
835 @Override
836 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700837 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700838 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700839 eventAccummulator.add(event);
840 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700841 }
842 }
843
844 // Link event listener.
845 private class InternalLinkListener implements LinkListener {
846 @Override
847 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700848 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700849 eventAccummulator.add(event);
850 }
851 }
852
853 // Host event listener.
854 private class InternalHostListener implements HostListener {
855 @Override
856 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700857 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700858 eventAccummulator.add(event);
859 }
860 }
861
862 // Intent event listener.
863 private class InternalIntentListener implements IntentListener {
864 @Override
865 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700866 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700867 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700868 }
869 eventAccummulator.add(event);
870 }
871 }
872
873 // Intent event listener.
874 private class InternalFlowListener implements FlowRuleListener {
875 @Override
876 public void event(FlowRuleEvent event) {
877 eventAccummulator.add(event);
878 }
879 }
880
Simon Huntd2747a02015-04-30 22:41:16 -0700881 // encapsulate
882 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700883 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700884 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700885 }
Simon Huntd2747a02015-04-30 22:41:16 -0700886
887 private final Type type;
888 private final ObjectNode payload;
889
890 TrafficEvent(Type type, ObjectNode payload) {
891 this.type = type;
892 this.payload = payload;
893 }
894 }
895
Thomas Vachuska329af532015-03-10 02:08:33 -0700896 // Periodic update of the traffic information
897 private class TrafficMonitor extends TimerTask {
898 @Override
899 public void run() {
900 try {
901 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700902 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700903 case ALL_FLOW_TRAFFIC:
904 requestAllFlowTraffic();
905 break;
906 case ALL_PORT_TRAFFIC:
907 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700908 break;
909 case DEV_LINK_FLOWS:
910 requestDeviceLinkFlows(trafficEvent.payload);
911 break;
912 case SEL_INTENT:
913 requestSelectedIntentTraffic();
914 break;
915 default:
916 // nothing to do
917 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700918 }
919 }
920 } catch (Exception e) {
921 log.warn("Unable to handle traffic request due to {}", e.getMessage());
922 log.warn("Boom!", e);
923 }
924 }
925 }
926
927 // Periodic update of the summary information
928 private class SummaryMonitor extends TimerTask {
929 @Override
930 public void run() {
931 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700932 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700933 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700934 }
935 } catch (Exception e) {
936 log.warn("Unable to handle summary request due to {}", e.getMessage());
937 log.warn("Boom!", e);
938 }
939 }
940 }
941
942 // Accumulates events to drive methodic update of the summary pane.
943 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
944 protected InternalEventAccummulator() {
945 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
946 }
947
948 @Override
949 public void processItems(List<Event> items) {
950 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700951 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700952 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700953 }
954 } catch (Exception e) {
955 log.warn("Unable to handle summary request due to {}", e.getMessage());
956 log.debug("Boom!", e);
957 }
958 }
959 }
960}