blob: 6d2295ac6e038edb19ddaf3a450a6717fad2efa0 [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;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070070import java.util.concurrent.ExecutorService;
Thomas Vachuska329af532015-03-10 02:08:33 -070071
72import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070073import static java.util.concurrent.Executors.newSingleThreadExecutor;
74import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska329af532015-03-10 02:08:33 -070075import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
76import static org.onosproject.net.DeviceId.deviceId;
77import static org.onosproject.net.HostId.hostId;
Thomas Vachuskacb5016f2015-05-18 14:11:43 -070078import static org.onosproject.net.device.DeviceEvent.Type.*;
Thomas Vachuska329af532015-03-10 02:08:33 -070079import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
80import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
81
82/**
83 * Web socket capable of interacting with the GUI topology view.
84 */
85public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
86
Simon Huntd2747a02015-04-30 22:41:16 -070087 private static final String REQ_DETAILS = "requestDetails";
88 private static final String UPDATE_META = "updateMeta";
89 private static final String ADD_HOST_INTENT = "addHostIntent";
90 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
91 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
92 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
93 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
94 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070095 private static final String REQ_ALL_FLOW_TRAFFIC = "requestAllFlowTraffic";
96 private static final String REQ_ALL_PORT_TRAFFIC = "requestAllPortTraffic";
Simon Huntd2747a02015-04-30 22:41:16 -070097 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
98 private static final String CANCEL_TRAFFIC = "cancelTraffic";
99 private static final String REQ_SUMMARY = "requestSummary";
100 private static final String CANCEL_SUMMARY = "cancelSummary";
101 private static final String EQ_MASTERS = "equalizeMasters";
102 private static final String SPRITE_LIST_REQ = "spriteListRequest";
103 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
104 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700105 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Hunte05cae42015-07-23 17:35:24 -0700106 private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
Simon Huntd2747a02015-04-30 22:41:16 -0700107 private static final String TOPO_STOP = "topoStop";
108
109
Thomas Vachuska329af532015-03-10 02:08:33 -0700110 private static final String APP_ID = "org.onosproject.gui";
111
112 private static final long TRAFFIC_FREQUENCY = 5000;
113 private static final long SUMMARY_FREQUENCY = 30000;
114
115 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700116 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700117
118
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700119 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700120
121 private static final int MAX_EVENTS = 1000;
122 private static final int MAX_BATCH_MS = 5000;
123 private static final int MAX_IDLE_MS = 1000;
124
125 private ApplicationId appId;
126
127 private final ClusterEventListener clusterListener = new InternalClusterListener();
128 private final MastershipListener mastershipListener = new InternalMastershipListener();
129 private final DeviceListener deviceListener = new InternalDeviceListener();
130 private final LinkListener linkListener = new InternalLinkListener();
131 private final HostListener hostListener = new InternalHostListener();
132 private final IntentListener intentListener = new InternalIntentListener();
133 private final FlowRuleListener flowListener = new InternalFlowListener();
134
135 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700136 private final ExecutorService msgSender =
137 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700138
Simon Hunte05cae42015-07-23 17:35:24 -0700139 private TopoOverlayCache overlayCache;
140
Simon Huntd2747a02015-04-30 22:41:16 -0700141 private TimerTask trafficTask = null;
142 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700143
Simon Huntd2747a02015-04-30 22:41:16 -0700144 private TimerTask summaryTask = null;
145 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700146
147 private boolean listenersRemoved = false;
148
149 private TopologyViewIntentFilter intentFilter;
150
151 // Current selection context
152 private Set<Host> selectedHosts;
153 private Set<Device> selectedDevices;
154 private List<Intent> selectedIntents;
155 private int currentIntentIndex = -1;
156
Thomas Vachuska329af532015-03-10 02:08:33 -0700157
158 @Override
159 public void init(UiConnection connection, ServiceDirectory directory) {
160 super.init(connection, directory);
161 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
162 hostService, linkService);
163 appId = directory.get(CoreService.class).registerApplication(APP_ID);
164 }
165
166 @Override
167 public void destroy() {
168 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700169 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700170 super.destroy();
171 }
172
Thomas Vachuska329af532015-03-10 02:08:33 -0700173 @Override
Simon Huntda580882015-05-12 20:58:18 -0700174 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700175 return ImmutableSet.of(
176 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700177 new TopoHeartbeat(),
Simon Hunte05cae42015-07-23 17:35:24 -0700178 new TopoSelectOverlay(),
Simon Huntd2747a02015-04-30 22:41:16 -0700179 new TopoStop(),
180 new ReqSummary(),
181 new CancelSummary(),
182 new SpriteListReq(),
183 new SpriteDataReq(),
184 new RequestDetails(),
185 new UpdateMeta(),
186 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700187
Simon Huntd2747a02015-04-30 22:41:16 -0700188 // TODO: migrate traffic related to separate app
189 new AddHostIntent(),
190 new AddMultiSourceIntent(),
191 new ReqRelatedIntents(),
192 new ReqNextIntent(),
193 new ReqPrevIntent(),
194 new ReqSelectedIntentTraffic(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700195 new ReqAllFlowTraffic(),
196 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700197 new ReqDevLinkFlows(),
198 new CancelTraffic()
199 );
200 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700201
Simon Hunte05cae42015-07-23 17:35:24 -0700202 /**
203 * Injects the topology overlay cache.
204 *
205 * @param overlayCache injected cache
206 */
207 void setOverlayCache(TopoOverlayCache overlayCache) {
208 this.overlayCache = overlayCache;
209 }
210
Simon Huntd2747a02015-04-30 22:41:16 -0700211 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700212
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700213 /**
214 * @deprecated in Cardinal Release
215 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700216 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700217 private final class TopoStart extends RequestHandler {
218 private TopoStart() {
219 super(TOPO_START);
220 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700221
Simon Huntd2747a02015-04-30 22:41:16 -0700222 @Override
223 public void process(long sid, ObjectNode payload) {
224 addListeners();
225 sendAllInstances(null);
226 sendAllDevices();
227 sendAllLinks();
228 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700229 }
230 }
231
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700232 /**
233 * @deprecated in Cardinal Release
234 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700235 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700236 private final class TopoHeartbeat extends RequestHandler {
237 private TopoHeartbeat() {
238 super(TOPO_HEARTBEAT);
239 }
240
241 @Override
242 public void process(long sid, ObjectNode payload) {
243 // place holder for now
244 }
245 }
246
Simon Hunte05cae42015-07-23 17:35:24 -0700247 private final class TopoSelectOverlay extends RequestHandler {
248 private TopoSelectOverlay() {
249 super(TOPO_SELECT_OVERLAY);
250 }
251
252 @Override
253 public void process(long sid, ObjectNode payload) {
254 String deact = string(payload, "deactivate");
255 String act = string(payload, "activate");
256 overlayCache.switchOverlay(deact, act);
257 }
258 }
259
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700260 /**
261 * @deprecated in Cardinal Release
262 */
Simon Hunt732bb2e2015-05-13 18:32:16 -0700263 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700264 private final class TopoStop extends RequestHandler {
265 private TopoStop() {
266 super(TOPO_STOP);
267 }
268
269 @Override
270 public void process(long sid, ObjectNode payload) {
271 stopSummaryMonitoring();
272 stopTrafficMonitoring();
273 }
274 }
275
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700276 /**
277 * @deprecated in Cardinal Release
278 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700279 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700280 private final class ReqSummary extends RequestHandler {
281 private ReqSummary() {
282 super(REQ_SUMMARY);
283 }
284
285 @Override
286 public void process(long sid, ObjectNode payload) {
287 requestSummary(sid);
288 startSummaryMonitoring();
289 }
290 }
291
292 private final class CancelSummary extends RequestHandler {
293 private CancelSummary() {
294 super(CANCEL_SUMMARY);
295 }
296
297 @Override
298 public void process(long sid, ObjectNode payload) {
299 stopSummaryMonitoring();
300 }
301 }
302
303 private final class SpriteListReq extends RequestHandler {
304 private SpriteListReq() {
305 super(SPRITE_LIST_REQ);
306 }
307
308 @Override
309 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700310 ObjectNode root = objectNode();
311 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700312 get(SpriteService.class).getNames().forEach(names::add);
313 root.set("names", names);
314 sendMessage("spriteListResponse", sid, root);
315 }
316 }
317
318 private final class SpriteDataReq extends RequestHandler {
319 private SpriteDataReq() {
320 super(SPRITE_DATA_REQ);
321 }
322
323 @Override
324 public void process(long sid, ObjectNode payload) {
325 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700326 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700327 root.set("data", get(SpriteService.class).get(name));
328 sendMessage("spriteDataResponse", sid, root);
329 }
330 }
331
332 private final class RequestDetails extends RequestHandler {
333 private RequestDetails() {
334 super(REQ_DETAILS);
335 }
336
337 @Override
338 public void process(long sid, ObjectNode payload) {
339 String type = string(payload, "class", "unknown");
Simon Hunte05cae42015-07-23 17:35:24 -0700340 String id = string(payload, "id");
Simon Huntd2747a02015-04-30 22:41:16 -0700341
342 if (type.equals("device")) {
343 sendMessage(deviceDetails(deviceId(id), sid));
344 } else if (type.equals("host")) {
345 sendMessage(hostDetails(hostId(id), sid));
346 }
347 }
348 }
349
350 private final class UpdateMeta extends RequestHandler {
351 private UpdateMeta() {
352 super(UPDATE_META);
353 }
354
355 @Override
356 public void process(long sid, ObjectNode payload) {
357 updateMetaUi(payload);
358 }
359 }
360
361 private final class EqMasters extends RequestHandler {
362 private EqMasters() {
363 super(EQ_MASTERS);
364 }
365
366 @Override
367 public void process(long sid, ObjectNode payload) {
368 directory.get(MastershipAdminService.class).balanceRoles();
369 }
370 }
371
372 // === TODO: move traffic related classes to traffic app
373
374 private final class AddHostIntent extends RequestHandler {
375 private AddHostIntent() {
376 super(ADD_HOST_INTENT);
377 }
378
379 @Override
380 public void process(long sid, ObjectNode payload) {
381 // TODO: add protection against device ids and non-existent hosts.
382 HostId one = hostId(string(payload, "one"));
383 HostId two = hostId(string(payload, "two"));
384
385 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700386 .appId(appId)
387 .one(one)
388 .two(two)
389 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700390
391 intentService.submit(intent);
392 startMonitoringIntent(intent);
393 }
394 }
395
396 private final class AddMultiSourceIntent extends RequestHandler {
397 private AddMultiSourceIntent() {
398 super(ADD_MULTI_SRC_INTENT);
399 }
400
401 @Override
402 public void process(long sid, ObjectNode payload) {
403 // TODO: add protection against device ids and non-existent hosts.
404 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
405 HostId dst = hostId(string(payload, "dst"));
406 Host dstHost = hostService.getHost(dst);
407
408 Set<ConnectPoint> ingressPoints = getHostLocations(src);
409
410 // FIXME: clearly, this is not enough
411 TrafficSelector selector = DefaultTrafficSelector.builder()
412 .matchEthDst(dstHost.mac()).build();
413 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
414
415 MultiPointToSinglePointIntent intent =
416 MultiPointToSinglePointIntent.builder()
417 .appId(appId)
418 .selector(selector)
419 .treatment(treatment)
420 .ingressPoints(ingressPoints)
421 .egressPoint(dstHost.location())
422 .build();
423
424 intentService.submit(intent);
425 startMonitoringIntent(intent);
426 }
427 }
428
429 private final class ReqRelatedIntents extends RequestHandler {
430 private ReqRelatedIntents() {
431 super(REQ_RELATED_INTENTS);
432 }
433
434 @Override
435 public void process(long sid, ObjectNode payload) {
436 // Cancel any other traffic monitoring mode.
437 stopTrafficMonitoring();
438
439 if (!payload.has("ids")) {
440 return;
441 }
442
443 // Get the set of selected hosts and their intents.
444 ArrayNode ids = (ArrayNode) payload.path("ids");
445 selectedHosts = getHosts(ids);
446 selectedDevices = getDevices(ids);
447 selectedIntents = intentFilter.findPathIntents(
448 selectedHosts, selectedDevices, intentService.getIntents());
449 currentIntentIndex = -1;
450
451 if (haveSelectedIntents()) {
452 // Send a message to highlight all links of all monitored intents.
453 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
454 }
455
456 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
457// String hover = string(payload, "hover");
458// if (!isNullOrEmpty(hover)) {
459// // If there is a hover node, include it in the selection and find intents.
460// processHoverExtendedSelection(sid, hover);
461// }
462 }
463 }
464
465 private final class ReqNextIntent extends RequestHandler {
466 private ReqNextIntent() {
467 super(REQ_NEXT_INTENT);
468 }
469
470 @Override
471 public void process(long sid, ObjectNode payload) {
472 stopTrafficMonitoring();
473 requestAnotherRelatedIntent(+1);
474 }
475 }
476
477 private final class ReqPrevIntent extends RequestHandler {
478 private ReqPrevIntent() {
479 super(REQ_PREV_INTENT);
480 }
481
482 @Override
483 public void process(long sid, ObjectNode payload) {
484 stopTrafficMonitoring();
485 requestAnotherRelatedIntent(-1);
486 }
487 }
488
489 private final class ReqSelectedIntentTraffic extends RequestHandler {
490 private ReqSelectedIntentTraffic() {
491 super(REQ_SEL_INTENT_TRAFFIC);
492 }
493
494 @Override
495 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700496 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700497 requestSelectedIntentTraffic();
498 startTrafficMonitoring();
499 }
500 }
501
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700502 private final class ReqAllFlowTraffic extends RequestHandler {
503 private ReqAllFlowTraffic() {
504 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700505 }
506
507 @Override
508 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700509 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
510 requestAllFlowTraffic();
511 }
512 }
513
514 private final class ReqAllPortTraffic extends RequestHandler {
515 private ReqAllPortTraffic() {
516 super(REQ_ALL_PORT_TRAFFIC);
517 }
518
519 @Override
520 public void process(long sid, ObjectNode payload) {
521 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
522 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700523 }
524 }
525
526 private final class ReqDevLinkFlows extends RequestHandler {
527 private ReqDevLinkFlows() {
528 super(REQ_DEV_LINK_FLOWS);
529 }
530
531 @Override
532 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700533 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700534 requestDeviceLinkFlows(payload);
535 }
536 }
537
538 private final class CancelTraffic extends RequestHandler {
539 private CancelTraffic() {
540 super(CANCEL_TRAFFIC);
541 }
542
543 @Override
544 public void process(long sid, ObjectNode payload) {
545 selectedIntents = null;
546 sendMessage(trafficMessage());
547 stopTrafficMonitoring();
548 }
549 }
550
551 //=======================================================================
552
553
Thomas Vachuska329af532015-03-10 02:08:33 -0700554 // Sends the specified data to the client.
555 protected synchronized void sendMessage(ObjectNode data) {
556 UiConnection connection = connection();
557 if (connection != null) {
558 connection.sendMessage(data);
559 }
560 }
561
Simon Huntd2747a02015-04-30 22:41:16 -0700562 // Subscribes for summary messages.
563 private synchronized void requestSummary(long sid) {
564 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700565 }
566
Simon Huntd2747a02015-04-30 22:41:16 -0700567
Thomas Vachuska329af532015-03-10 02:08:33 -0700568 private void cancelAllRequests() {
569 stopSummaryMonitoring();
570 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700571 }
572
573 // Sends all controller nodes to the client as node-added messages.
574 private void sendAllInstances(String messageType) {
575 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
576 Collections.sort(nodes, NODE_COMPARATOR);
577 for (ControllerNode node : nodes) {
578 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
579 messageType));
580 }
581 }
582
583 // Sends all devices to the client as device-added messages.
584 private void sendAllDevices() {
585 // Send optical first, others later for layered rendering
586 for (Device device : deviceService.getDevices()) {
587 if (device.type() == Device.Type.ROADM) {
588 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
589 }
590 }
591 for (Device device : deviceService.getDevices()) {
592 if (device.type() != Device.Type.ROADM) {
593 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
594 }
595 }
596 }
597
598 // Sends all links to the client as link-added messages.
599 private void sendAllLinks() {
600 // Send optical first, others later for layered rendering
601 for (Link link : linkService.getLinks()) {
602 if (link.type() == Link.Type.OPTICAL) {
603 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
604 }
605 }
606 for (Link link : linkService.getLinks()) {
607 if (link.type() != Link.Type.OPTICAL) {
608 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
609 }
610 }
611 }
612
613 // Sends all hosts to the client as host-added messages.
614 private void sendAllHosts() {
615 for (Host host : hostService.getHosts()) {
616 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
617 }
618 }
619
Thomas Vachuska329af532015-03-10 02:08:33 -0700620
Simon Huntd2747a02015-04-30 22:41:16 -0700621 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700622 selectedHosts = new HashSet<>();
623 selectedDevices = new HashSet<>();
624 selectedIntents = new ArrayList<>();
625 selectedIntents.add(intent);
626 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700627 requestAnotherRelatedIntent(+1);
628 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700629 }
630
631
632 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
633 Set<ConnectPoint> points = new HashSet<>();
634 for (HostId hostId : hostIds) {
635 points.add(getHostLocation(hostId));
636 }
637 return points;
638 }
639
640 private HostLocation getHostLocation(HostId hostId) {
641 return hostService.getHost(hostId).location();
642 }
643
644 // Produces a list of host ids from the specified JSON array.
645 private Set<HostId> getHostIds(ArrayNode ids) {
646 Set<HostId> hostIds = new HashSet<>();
647 for (JsonNode id : ids) {
648 hostIds.add(hostId(id.asText()));
649 }
650 return hostIds;
651 }
652
653
Simon Huntd2747a02015-04-30 22:41:16 -0700654 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700655 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700656 trafficTask = new TrafficMonitor();
657 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700658 }
659
660 private synchronized void stopTrafficMonitoring() {
661 if (trafficTask != null) {
662 trafficTask.cancel();
663 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700664 }
665 }
666
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700667 // Subscribes for flow traffic messages.
668 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700669 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700670 sendMessage(trafficSummaryMessage(StatsType.FLOW));
671 }
672
673 // Subscribes for port traffic messages.
674 private synchronized void requestAllPortTraffic() {
675 startTrafficMonitoring();
676 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700677 }
678
Simon Huntd2747a02015-04-30 22:41:16 -0700679 private void requestDeviceLinkFlows(ObjectNode payload) {
680 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700681
682 // Get the set of selected hosts and their intents.
683 ArrayNode ids = (ArrayNode) payload.path("ids");
684 Set<Host> hosts = new HashSet<>();
685 Set<Device> devices = getDevices(ids);
686
687 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700688 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700689 if (!isNullOrEmpty(hover)) {
690 addHover(hosts, devices, hover);
691 }
Simon Huntd2747a02015-04-30 22:41:16 -0700692 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700693 }
694
695
Thomas Vachuska329af532015-03-10 02:08:33 -0700696 private boolean haveSelectedIntents() {
697 return selectedIntents != null && !selectedIntents.isEmpty();
698 }
699
700 // Processes the selection extended with hovered item to segregate items
701 // into primary (those including the hover) vs secondary highlights.
702 private void processHoverExtendedSelection(long sid, String hover) {
703 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
704 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
705 addHover(hoverSelHosts, hoverSelDevices, hover);
706
707 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
708 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
709 selectedIntents);
710 Set<Intent> secondary = new HashSet<>(selectedIntents);
711 secondary.removeAll(primary);
712
713 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700714 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700715 new TrafficClass("secondary", secondary)));
716 }
717
718 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700719 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700720 if (haveSelectedIntents()) {
721 currentIntentIndex = currentIntentIndex + offset;
722 if (currentIntentIndex < 0) {
723 currentIntentIndex = selectedIntents.size() - 1;
724 } else if (currentIntentIndex >= selectedIntents.size()) {
725 currentIntentIndex = 0;
726 }
Simon Huntd2747a02015-04-30 22:41:16 -0700727 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700728 }
729 }
730
731 // Sends traffic information on the related intents with the currently
732 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700733 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700734 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
735 log.info("Requested next intent {}", selectedIntent.id());
736
737 Set<Intent> primary = new HashSet<>();
738 primary.add(selectedIntent);
739
740 Set<Intent> secondary = new HashSet<>(selectedIntents);
741 secondary.remove(selectedIntent);
742
743 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700744 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700745 new TrafficClass("secondary", secondary)));
746 }
747
748 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700749 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700750 if (haveSelectedIntents()) {
751 if (currentIntentIndex < 0) {
752 currentIntentIndex = 0;
753 }
754 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
755 log.info("Requested traffic for selected {}", selectedIntent.id());
756
757 Set<Intent> primary = new HashSet<>();
758 primary.add(selectedIntent);
759
760 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700761 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700762 }
763 }
764
Simon Huntd2747a02015-04-30 22:41:16 -0700765 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700766 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700767 summaryTask = new SummaryMonitor();
768 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700769 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700770 }
771
772 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700773 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700774 summaryTask.cancel();
775 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700776 }
Simon Huntd2747a02015-04-30 22:41:16 -0700777 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700778 }
779
Thomas Vachuska329af532015-03-10 02:08:33 -0700780
781 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700782 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700783 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700784 clusterService.addListener(clusterListener);
785 mastershipService.addListener(mastershipListener);
786 deviceService.addListener(deviceListener);
787 linkService.addListener(linkListener);
788 hostService.addListener(hostListener);
789 intentService.addListener(intentListener);
790 flowService.addListener(flowListener);
791 }
792
793 // Removes all internal listeners.
794 private synchronized void removeListeners() {
795 if (!listenersRemoved) {
796 listenersRemoved = true;
797 clusterService.removeListener(clusterListener);
798 mastershipService.removeListener(mastershipListener);
799 deviceService.removeListener(deviceListener);
800 linkService.removeListener(linkListener);
801 hostService.removeListener(hostListener);
802 intentService.removeListener(intentListener);
803 flowService.removeListener(flowListener);
804 }
805 }
806
807 // Cluster event listener.
808 private class InternalClusterListener implements ClusterEventListener {
809 @Override
810 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700811 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700812 }
813 }
814
815 // Mastership change listener
816 private class InternalMastershipListener implements MastershipListener {
817 @Override
818 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700819 msgSender.execute(() -> {
820 sendAllInstances("updateInstance");
821 Device device = deviceService.getDevice(event.subject());
822 if (device != null) {
823 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
824 }
825 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700826 }
827 }
828
829 // Device event listener.
830 private class InternalDeviceListener implements DeviceListener {
831 @Override
832 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700833 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700834 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700835 eventAccummulator.add(event);
836 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700837 }
838 }
839
840 // Link event listener.
841 private class InternalLinkListener implements LinkListener {
842 @Override
843 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700844 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700845 eventAccummulator.add(event);
846 }
847 }
848
849 // Host event listener.
850 private class InternalHostListener implements HostListener {
851 @Override
852 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700853 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700854 eventAccummulator.add(event);
855 }
856 }
857
858 // Intent event listener.
859 private class InternalIntentListener implements IntentListener {
860 @Override
861 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700862 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700863 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700864 }
865 eventAccummulator.add(event);
866 }
867 }
868
869 // Intent event listener.
870 private class InternalFlowListener implements FlowRuleListener {
871 @Override
872 public void event(FlowRuleEvent event) {
873 eventAccummulator.add(event);
874 }
875 }
876
Simon Huntd2747a02015-04-30 22:41:16 -0700877 // encapsulate
878 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700879 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700880 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700881 }
Simon Huntd2747a02015-04-30 22:41:16 -0700882
883 private final Type type;
884 private final ObjectNode payload;
885
886 TrafficEvent(Type type, ObjectNode payload) {
887 this.type = type;
888 this.payload = payload;
889 }
890 }
891
Thomas Vachuska329af532015-03-10 02:08:33 -0700892 // Periodic update of the traffic information
893 private class TrafficMonitor extends TimerTask {
894 @Override
895 public void run() {
896 try {
897 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700898 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700899 case ALL_FLOW_TRAFFIC:
900 requestAllFlowTraffic();
901 break;
902 case ALL_PORT_TRAFFIC:
903 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700904 break;
905 case DEV_LINK_FLOWS:
906 requestDeviceLinkFlows(trafficEvent.payload);
907 break;
908 case SEL_INTENT:
909 requestSelectedIntentTraffic();
910 break;
911 default:
912 // nothing to do
913 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700914 }
915 }
916 } catch (Exception e) {
917 log.warn("Unable to handle traffic request due to {}", e.getMessage());
918 log.warn("Boom!", e);
919 }
920 }
921 }
922
923 // Periodic update of the summary information
924 private class SummaryMonitor extends TimerTask {
925 @Override
926 public void run() {
927 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700928 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700929 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700930 }
931 } catch (Exception e) {
932 log.warn("Unable to handle summary request due to {}", e.getMessage());
933 log.warn("Boom!", e);
934 }
935 }
936 }
937
938 // Accumulates events to drive methodic update of the summary pane.
939 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
940 protected InternalEventAccummulator() {
941 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
942 }
943
944 @Override
945 public void processItems(List<Event> items) {
946 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700947 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700948 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700949 }
950 } catch (Exception e) {
951 log.warn("Unable to handle summary request due to {}", e.getMessage());
952 log.debug("Boom!", e);
953 }
954 }
955 }
956}