blob: b88ee4f47831bc11c13311810a26d47ead3c4001 [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 Huntb745ca62015-07-28 15:37:11 -070088 // incoming event types
Simon Huntd2747a02015-04-30 22:41:16 -070089 private static final String REQ_DETAILS = "requestDetails";
90 private static final String UPDATE_META = "updateMeta";
91 private static final String ADD_HOST_INTENT = "addHostIntent";
92 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
93 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
94 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
95 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
96 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070097 private static final String REQ_ALL_FLOW_TRAFFIC = "requestAllFlowTraffic";
98 private static final String REQ_ALL_PORT_TRAFFIC = "requestAllPortTraffic";
Simon Huntd2747a02015-04-30 22:41:16 -070099 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
100 private static final String CANCEL_TRAFFIC = "cancelTraffic";
101 private static final String REQ_SUMMARY = "requestSummary";
102 private static final String CANCEL_SUMMARY = "cancelSummary";
103 private static final String EQ_MASTERS = "equalizeMasters";
104 private static final String SPRITE_LIST_REQ = "spriteListRequest";
105 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
106 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700107 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Hunte05cae42015-07-23 17:35:24 -0700108 private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
Simon Huntd2747a02015-04-30 22:41:16 -0700109 private static final String TOPO_STOP = "topoStop";
110
Simon Huntb745ca62015-07-28 15:37:11 -0700111 // outgoing event types
112 private static final String SHOW_SUMMARY = "showSummary";
113 private static final String SHOW_DETAILS = "showDetails";
114 private static final String SPRITE_LIST_RESPONSE = "spriteListResponse";
115 private static final String SPRITE_DATA_RESPONSE = "spriteDataResponse";
116 private static final String UPDATE_INSTANCE = "updateInstance";
117
118 // fields
119 private static final String ID = "id";
120 private static final String IDS = "ids";
121 private static final String HOVER = "hover";
122 private static final String DEVICE = "device";
123 private static final String HOST = "host";
124 private static final String CLASS = "class";
125 private static final String UNKNOWN = "unknown";
126 private static final String ONE = "one";
127 private static final String TWO = "two";
128 private static final String SRC = "src";
129 private static final String DST = "dst";
130 private static final String DATA = "data";
131 private static final String NAME = "name";
132 private static final String NAMES = "names";
133 private static final String ACTIVATE = "activate";
134 private static final String DEACTIVATE = "deactivate";
135 private static final String PRIMARY = "primary";
136 private static final String SECONDARY = "secondary";
137
Simon Huntd2747a02015-04-30 22:41:16 -0700138
Thomas Vachuska329af532015-03-10 02:08:33 -0700139 private static final String APP_ID = "org.onosproject.gui";
140
141 private static final long TRAFFIC_FREQUENCY = 5000;
142 private static final long SUMMARY_FREQUENCY = 30000;
143
144 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700145 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700146
147
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700148 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700149
150 private static final int MAX_EVENTS = 1000;
151 private static final int MAX_BATCH_MS = 5000;
152 private static final int MAX_IDLE_MS = 1000;
153
154 private ApplicationId appId;
155
156 private final ClusterEventListener clusterListener = new InternalClusterListener();
157 private final MastershipListener mastershipListener = new InternalMastershipListener();
158 private final DeviceListener deviceListener = new InternalDeviceListener();
159 private final LinkListener linkListener = new InternalLinkListener();
160 private final HostListener hostListener = new InternalHostListener();
161 private final IntentListener intentListener = new InternalIntentListener();
162 private final FlowRuleListener flowListener = new InternalFlowListener();
163
164 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700165 private final ExecutorService msgSender =
166 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700167
Simon Hunte05cae42015-07-23 17:35:24 -0700168 private TopoOverlayCache overlayCache;
169
Simon Huntd2747a02015-04-30 22:41:16 -0700170 private TimerTask trafficTask = null;
171 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700172
Simon Huntd2747a02015-04-30 22:41:16 -0700173 private TimerTask summaryTask = null;
174 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700175
176 private boolean listenersRemoved = false;
177
178 private TopologyViewIntentFilter intentFilter;
179
180 // Current selection context
181 private Set<Host> selectedHosts;
182 private Set<Device> selectedDevices;
183 private List<Intent> selectedIntents;
184 private int currentIntentIndex = -1;
185
Thomas Vachuska329af532015-03-10 02:08:33 -0700186
187 @Override
188 public void init(UiConnection connection, ServiceDirectory directory) {
189 super.init(connection, directory);
190 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
191 hostService, linkService);
192 appId = directory.get(CoreService.class).registerApplication(APP_ID);
193 }
194
195 @Override
196 public void destroy() {
197 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700198 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700199 super.destroy();
200 }
201
Thomas Vachuska329af532015-03-10 02:08:33 -0700202 @Override
Simon Huntda580882015-05-12 20:58:18 -0700203 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700204 return ImmutableSet.of(
205 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700206 new TopoHeartbeat(),
Simon Hunte05cae42015-07-23 17:35:24 -0700207 new TopoSelectOverlay(),
Simon Huntd2747a02015-04-30 22:41:16 -0700208 new TopoStop(),
209 new ReqSummary(),
210 new CancelSummary(),
211 new SpriteListReq(),
212 new SpriteDataReq(),
213 new RequestDetails(),
214 new UpdateMeta(),
215 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700216
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700217 // TODO: implement "showHighlights" event (replaces "showTraffic")
218
Simon Huntd2747a02015-04-30 22:41:16 -0700219 // TODO: migrate traffic related to separate app
220 new AddHostIntent(),
221 new AddMultiSourceIntent(),
222 new ReqRelatedIntents(),
223 new ReqNextIntent(),
224 new ReqPrevIntent(),
225 new ReqSelectedIntentTraffic(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700226 new ReqAllFlowTraffic(),
227 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700228 new ReqDevLinkFlows(),
229 new CancelTraffic()
230 );
231 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700232
Simon Hunte05cae42015-07-23 17:35:24 -0700233 /**
234 * Injects the topology overlay cache.
235 *
236 * @param overlayCache injected cache
237 */
238 void setOverlayCache(TopoOverlayCache overlayCache) {
239 this.overlayCache = overlayCache;
240 }
241
Simon Huntd2747a02015-04-30 22:41:16 -0700242 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700243
Simon Huntd2747a02015-04-30 22:41:16 -0700244 private final class TopoStart extends RequestHandler {
245 private TopoStart() {
246 super(TOPO_START);
247 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700248
Simon Huntd2747a02015-04-30 22:41:16 -0700249 @Override
250 public void process(long sid, ObjectNode payload) {
251 addListeners();
252 sendAllInstances(null);
253 sendAllDevices();
254 sendAllLinks();
255 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700256 }
257 }
258
Simon Hunt732bb2e2015-05-13 18:32:16 -0700259 private final class TopoHeartbeat extends RequestHandler {
260 private TopoHeartbeat() {
261 super(TOPO_HEARTBEAT);
262 }
263
264 @Override
265 public void process(long sid, ObjectNode payload) {
266 // place holder for now
267 }
268 }
269
Simon Hunte05cae42015-07-23 17:35:24 -0700270 private final class TopoSelectOverlay extends RequestHandler {
271 private TopoSelectOverlay() {
272 super(TOPO_SELECT_OVERLAY);
273 }
274
275 @Override
276 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700277 String deact = string(payload, DEACTIVATE);
278 String act = string(payload, ACTIVATE);
Simon Hunte05cae42015-07-23 17:35:24 -0700279 overlayCache.switchOverlay(deact, act);
280 }
281 }
282
Simon Huntd2747a02015-04-30 22:41:16 -0700283 private final class TopoStop extends RequestHandler {
284 private TopoStop() {
285 super(TOPO_STOP);
286 }
287
288 @Override
289 public void process(long sid, ObjectNode payload) {
290 stopSummaryMonitoring();
291 stopTrafficMonitoring();
292 }
293 }
294
295 private final class ReqSummary extends RequestHandler {
296 private ReqSummary() {
297 super(REQ_SUMMARY);
298 }
299
300 @Override
301 public void process(long sid, ObjectNode payload) {
302 requestSummary(sid);
303 startSummaryMonitoring();
304 }
305 }
306
307 private final class CancelSummary extends RequestHandler {
308 private CancelSummary() {
309 super(CANCEL_SUMMARY);
310 }
311
312 @Override
313 public void process(long sid, ObjectNode payload) {
314 stopSummaryMonitoring();
315 }
316 }
317
318 private final class SpriteListReq extends RequestHandler {
319 private SpriteListReq() {
320 super(SPRITE_LIST_REQ);
321 }
322
323 @Override
324 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700325 ObjectNode root = objectNode();
326 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700327 get(SpriteService.class).getNames().forEach(names::add);
Simon Huntb745ca62015-07-28 15:37:11 -0700328 root.set(NAMES, names);
329 sendMessage(SPRITE_LIST_RESPONSE, sid, root);
Simon Huntd2747a02015-04-30 22:41:16 -0700330 }
331 }
332
333 private final class SpriteDataReq extends RequestHandler {
334 private SpriteDataReq() {
335 super(SPRITE_DATA_REQ);
336 }
337
338 @Override
339 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700340 String name = string(payload, NAME);
Simon Huntda580882015-05-12 20:58:18 -0700341 ObjectNode root = objectNode();
Simon Huntb745ca62015-07-28 15:37:11 -0700342 root.set(DATA, get(SpriteService.class).get(name));
343 sendMessage(SPRITE_DATA_RESPONSE, sid, root);
Simon Huntd2747a02015-04-30 22:41:16 -0700344 }
345 }
346
347 private final class RequestDetails extends RequestHandler {
348 private RequestDetails() {
349 super(REQ_DETAILS);
350 }
351
352 @Override
353 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700354 String type = string(payload, CLASS, UNKNOWN);
355 String id = string(payload, ID);
356 PropertyPanel pp = null;
Simon Huntd2747a02015-04-30 22:41:16 -0700357
Simon Huntb745ca62015-07-28 15:37:11 -0700358 if (type.equals(DEVICE)) {
359 pp = deviceDetails(deviceId(id), sid);
360 overlayCache.currentOverlay().modifyDeviceDetails(pp);
361 } else if (type.equals(HOST)) {
362 pp = hostDetails(hostId(id), sid);
363 overlayCache.currentOverlay().modifyHostDetails(pp);
Simon Huntd2747a02015-04-30 22:41:16 -0700364 }
Simon Huntb745ca62015-07-28 15:37:11 -0700365
366 ObjectNode json = JsonUtils.envelope(SHOW_DETAILS, sid, json(pp));
367 sendMessage(json);
Simon Huntd2747a02015-04-30 22:41:16 -0700368 }
369 }
370
371 private final class UpdateMeta extends RequestHandler {
372 private UpdateMeta() {
373 super(UPDATE_META);
374 }
375
376 @Override
377 public void process(long sid, ObjectNode payload) {
378 updateMetaUi(payload);
379 }
380 }
381
382 private final class EqMasters extends RequestHandler {
383 private EqMasters() {
384 super(EQ_MASTERS);
385 }
386
387 @Override
388 public void process(long sid, ObjectNode payload) {
389 directory.get(MastershipAdminService.class).balanceRoles();
390 }
391 }
392
393 // === TODO: move traffic related classes to traffic app
394
395 private final class AddHostIntent extends RequestHandler {
396 private AddHostIntent() {
397 super(ADD_HOST_INTENT);
398 }
399
400 @Override
401 public void process(long sid, ObjectNode payload) {
402 // TODO: add protection against device ids and non-existent hosts.
Simon Huntb745ca62015-07-28 15:37:11 -0700403 HostId one = hostId(string(payload, ONE));
404 HostId two = hostId(string(payload, TWO));
Simon Huntd2747a02015-04-30 22:41:16 -0700405
406 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700407 .appId(appId)
408 .one(one)
409 .two(two)
410 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700411
412 intentService.submit(intent);
413 startMonitoringIntent(intent);
414 }
415 }
416
417 private final class AddMultiSourceIntent extends RequestHandler {
418 private AddMultiSourceIntent() {
419 super(ADD_MULTI_SRC_INTENT);
420 }
421
422 @Override
423 public void process(long sid, ObjectNode payload) {
424 // TODO: add protection against device ids and non-existent hosts.
Simon Huntb745ca62015-07-28 15:37:11 -0700425 Set<HostId> src = getHostIds((ArrayNode) payload.path(SRC));
426 HostId dst = hostId(string(payload, DST));
Simon Huntd2747a02015-04-30 22:41:16 -0700427 Host dstHost = hostService.getHost(dst);
428
429 Set<ConnectPoint> ingressPoints = getHostLocations(src);
430
431 // FIXME: clearly, this is not enough
432 TrafficSelector selector = DefaultTrafficSelector.builder()
433 .matchEthDst(dstHost.mac()).build();
434 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
435
436 MultiPointToSinglePointIntent intent =
437 MultiPointToSinglePointIntent.builder()
438 .appId(appId)
439 .selector(selector)
440 .treatment(treatment)
441 .ingressPoints(ingressPoints)
442 .egressPoint(dstHost.location())
443 .build();
444
445 intentService.submit(intent);
446 startMonitoringIntent(intent);
447 }
448 }
449
450 private final class ReqRelatedIntents extends RequestHandler {
451 private ReqRelatedIntents() {
452 super(REQ_RELATED_INTENTS);
453 }
454
455 @Override
456 public void process(long sid, ObjectNode payload) {
457 // Cancel any other traffic monitoring mode.
458 stopTrafficMonitoring();
459
Simon Huntb745ca62015-07-28 15:37:11 -0700460 if (!payload.has(IDS)) {
Simon Huntd2747a02015-04-30 22:41:16 -0700461 return;
462 }
463
464 // Get the set of selected hosts and their intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700465 ArrayNode ids = (ArrayNode) payload.path(IDS);
Simon Huntd2747a02015-04-30 22:41:16 -0700466 selectedHosts = getHosts(ids);
467 selectedDevices = getDevices(ids);
468 selectedIntents = intentFilter.findPathIntents(
469 selectedHosts, selectedDevices, intentService.getIntents());
470 currentIntentIndex = -1;
471
472 if (haveSelectedIntents()) {
473 // Send a message to highlight all links of all monitored intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700474 sendMessage(trafficMessage(new TrafficClass(PRIMARY, selectedIntents)));
Simon Huntd2747a02015-04-30 22:41:16 -0700475 }
476
477 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
478// String hover = string(payload, "hover");
479// if (!isNullOrEmpty(hover)) {
480// // If there is a hover node, include it in the selection and find intents.
481// processHoverExtendedSelection(sid, hover);
482// }
483 }
484 }
485
486 private final class ReqNextIntent extends RequestHandler {
487 private ReqNextIntent() {
488 super(REQ_NEXT_INTENT);
489 }
490
491 @Override
492 public void process(long sid, ObjectNode payload) {
493 stopTrafficMonitoring();
494 requestAnotherRelatedIntent(+1);
495 }
496 }
497
498 private final class ReqPrevIntent extends RequestHandler {
499 private ReqPrevIntent() {
500 super(REQ_PREV_INTENT);
501 }
502
503 @Override
504 public void process(long sid, ObjectNode payload) {
505 stopTrafficMonitoring();
506 requestAnotherRelatedIntent(-1);
507 }
508 }
509
510 private final class ReqSelectedIntentTraffic extends RequestHandler {
511 private ReqSelectedIntentTraffic() {
512 super(REQ_SEL_INTENT_TRAFFIC);
513 }
514
515 @Override
516 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700517 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700518 requestSelectedIntentTraffic();
519 startTrafficMonitoring();
520 }
521 }
522
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700523 private final class ReqAllFlowTraffic extends RequestHandler {
524 private ReqAllFlowTraffic() {
525 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700526 }
527
528 @Override
529 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700530 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
531 requestAllFlowTraffic();
532 }
533 }
534
535 private final class ReqAllPortTraffic extends RequestHandler {
536 private ReqAllPortTraffic() {
537 super(REQ_ALL_PORT_TRAFFIC);
538 }
539
540 @Override
541 public void process(long sid, ObjectNode payload) {
542 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
543 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700544 }
545 }
546
547 private final class ReqDevLinkFlows extends RequestHandler {
548 private ReqDevLinkFlows() {
549 super(REQ_DEV_LINK_FLOWS);
550 }
551
552 @Override
553 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700554 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700555 requestDeviceLinkFlows(payload);
556 }
557 }
558
559 private final class CancelTraffic extends RequestHandler {
560 private CancelTraffic() {
561 super(CANCEL_TRAFFIC);
562 }
563
564 @Override
565 public void process(long sid, ObjectNode payload) {
566 selectedIntents = null;
567 sendMessage(trafficMessage());
568 stopTrafficMonitoring();
569 }
570 }
571
572 //=======================================================================
573
574
Thomas Vachuska329af532015-03-10 02:08:33 -0700575 // Sends the specified data to the client.
576 protected synchronized void sendMessage(ObjectNode data) {
577 UiConnection connection = connection();
578 if (connection != null) {
579 connection.sendMessage(data);
580 }
581 }
582
Simon Huntd2747a02015-04-30 22:41:16 -0700583 // Subscribes for summary messages.
584 private synchronized void requestSummary(long sid) {
Simon Hunt0af1ec32015-07-24 12:17:55 -0700585 PropertyPanel pp = summmaryMessage(sid);
586 overlayCache.currentOverlay().modifySummary(pp);
Simon Huntb745ca62015-07-28 15:37:11 -0700587 ObjectNode json = JsonUtils.envelope(SHOW_SUMMARY, sid, json(pp));
Simon Hunt0af1ec32015-07-24 12:17:55 -0700588 sendMessage(json);
Thomas Vachuska329af532015-03-10 02:08:33 -0700589 }
590
Simon Huntd2747a02015-04-30 22:41:16 -0700591
Thomas Vachuska329af532015-03-10 02:08:33 -0700592 private void cancelAllRequests() {
593 stopSummaryMonitoring();
594 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700595 }
596
597 // Sends all controller nodes to the client as node-added messages.
598 private void sendAllInstances(String messageType) {
599 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
600 Collections.sort(nodes, NODE_COMPARATOR);
601 for (ControllerNode node : nodes) {
602 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
603 messageType));
604 }
605 }
606
607 // Sends all devices to the client as device-added messages.
608 private void sendAllDevices() {
609 // Send optical first, others later for layered rendering
610 for (Device device : deviceService.getDevices()) {
611 if (device.type() == Device.Type.ROADM) {
612 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
613 }
614 }
615 for (Device device : deviceService.getDevices()) {
616 if (device.type() != Device.Type.ROADM) {
617 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
618 }
619 }
620 }
621
622 // Sends all links to the client as link-added messages.
623 private void sendAllLinks() {
624 // Send optical first, others later for layered rendering
625 for (Link link : linkService.getLinks()) {
626 if (link.type() == Link.Type.OPTICAL) {
627 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
628 }
629 }
630 for (Link link : linkService.getLinks()) {
631 if (link.type() != Link.Type.OPTICAL) {
632 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
633 }
634 }
635 }
636
637 // Sends all hosts to the client as host-added messages.
638 private void sendAllHosts() {
639 for (Host host : hostService.getHosts()) {
640 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
641 }
642 }
643
Thomas Vachuska329af532015-03-10 02:08:33 -0700644
Simon Huntd2747a02015-04-30 22:41:16 -0700645 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700646 selectedHosts = new HashSet<>();
647 selectedDevices = new HashSet<>();
648 selectedIntents = new ArrayList<>();
649 selectedIntents.add(intent);
650 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700651 requestAnotherRelatedIntent(+1);
652 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700653 }
654
655
656 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
657 Set<ConnectPoint> points = new HashSet<>();
658 for (HostId hostId : hostIds) {
659 points.add(getHostLocation(hostId));
660 }
661 return points;
662 }
663
664 private HostLocation getHostLocation(HostId hostId) {
665 return hostService.getHost(hostId).location();
666 }
667
668 // Produces a list of host ids from the specified JSON array.
669 private Set<HostId> getHostIds(ArrayNode ids) {
670 Set<HostId> hostIds = new HashSet<>();
671 for (JsonNode id : ids) {
672 hostIds.add(hostId(id.asText()));
673 }
674 return hostIds;
675 }
676
677
Simon Huntd2747a02015-04-30 22:41:16 -0700678 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700679 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700680 trafficTask = new TrafficMonitor();
681 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700682 }
683
684 private synchronized void stopTrafficMonitoring() {
685 if (trafficTask != null) {
686 trafficTask.cancel();
687 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700688 }
689 }
690
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700691 // Subscribes for flow traffic messages.
692 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700693 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700694 sendMessage(trafficSummaryMessage(StatsType.FLOW));
695 }
696
697 // Subscribes for port traffic messages.
698 private synchronized void requestAllPortTraffic() {
699 startTrafficMonitoring();
700 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700701 }
702
Simon Huntd2747a02015-04-30 22:41:16 -0700703 private void requestDeviceLinkFlows(ObjectNode payload) {
704 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700705
706 // Get the set of selected hosts and their intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700707 ArrayNode ids = (ArrayNode) payload.path(IDS);
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 Set<Host> hosts = new HashSet<>();
709 Set<Device> devices = getDevices(ids);
710
711 // If there is a hover node, include it in the hosts and find intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700712 String hover = JsonUtils.string(payload, HOVER);
Thomas Vachuska329af532015-03-10 02:08:33 -0700713 if (!isNullOrEmpty(hover)) {
714 addHover(hosts, devices, hover);
715 }
Simon Huntd2747a02015-04-30 22:41:16 -0700716 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700717 }
718
719
Thomas Vachuska329af532015-03-10 02:08:33 -0700720 private boolean haveSelectedIntents() {
721 return selectedIntents != null && !selectedIntents.isEmpty();
722 }
723
724 // Processes the selection extended with hovered item to segregate items
725 // into primary (those including the hover) vs secondary highlights.
726 private void processHoverExtendedSelection(long sid, String hover) {
727 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
728 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
729 addHover(hoverSelHosts, hoverSelDevices, hover);
730
731 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
732 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
733 selectedIntents);
734 Set<Intent> secondary = new HashSet<>(selectedIntents);
735 secondary.removeAll(primary);
736
737 // Send a message to highlight all links of all monitored intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700738 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary),
739 new TrafficClass(SECONDARY, secondary)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700740 }
741
742 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700743 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700744 if (haveSelectedIntents()) {
745 currentIntentIndex = currentIntentIndex + offset;
746 if (currentIntentIndex < 0) {
747 currentIntentIndex = selectedIntents.size() - 1;
748 } else if (currentIntentIndex >= selectedIntents.size()) {
749 currentIntentIndex = 0;
750 }
Simon Huntd2747a02015-04-30 22:41:16 -0700751 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700752 }
753 }
754
755 // Sends traffic information on the related intents with the currently
756 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700757 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700758 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
Simon Huntb745ca62015-07-28 15:37:11 -0700759 log.debug("Requested next intent {}", selectedIntent.id());
Thomas Vachuska329af532015-03-10 02:08:33 -0700760
761 Set<Intent> primary = new HashSet<>();
762 primary.add(selectedIntent);
763
764 Set<Intent> secondary = new HashSet<>(selectedIntents);
765 secondary.remove(selectedIntent);
766
767 // Send a message to highlight all links of the selected intent.
Simon Huntb745ca62015-07-28 15:37:11 -0700768 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary),
769 new TrafficClass(SECONDARY, secondary)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700770 }
771
772 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700773 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700774 if (haveSelectedIntents()) {
775 if (currentIntentIndex < 0) {
776 currentIntentIndex = 0;
777 }
778 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
Simon Huntb745ca62015-07-28 15:37:11 -0700779 log.debug("Requested traffic for selected {}", selectedIntent.id());
Thomas Vachuska329af532015-03-10 02:08:33 -0700780
781 Set<Intent> primary = new HashSet<>();
782 primary.add(selectedIntent);
783
784 // Send a message to highlight all links of the selected intent.
Simon Huntb745ca62015-07-28 15:37:11 -0700785 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700786 }
787 }
788
Simon Huntd2747a02015-04-30 22:41:16 -0700789 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700790 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700791 summaryTask = new SummaryMonitor();
792 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700793 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700794 }
795
796 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700797 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700798 summaryTask.cancel();
799 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700800 }
Simon Huntd2747a02015-04-30 22:41:16 -0700801 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700802 }
803
Thomas Vachuska329af532015-03-10 02:08:33 -0700804
805 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700806 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700807 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700808 clusterService.addListener(clusterListener);
809 mastershipService.addListener(mastershipListener);
810 deviceService.addListener(deviceListener);
811 linkService.addListener(linkListener);
812 hostService.addListener(hostListener);
813 intentService.addListener(intentListener);
814 flowService.addListener(flowListener);
815 }
816
817 // Removes all internal listeners.
818 private synchronized void removeListeners() {
819 if (!listenersRemoved) {
820 listenersRemoved = true;
821 clusterService.removeListener(clusterListener);
822 mastershipService.removeListener(mastershipListener);
823 deviceService.removeListener(deviceListener);
824 linkService.removeListener(linkListener);
825 hostService.removeListener(hostListener);
826 intentService.removeListener(intentListener);
827 flowService.removeListener(flowListener);
828 }
829 }
830
831 // Cluster event listener.
832 private class InternalClusterListener implements ClusterEventListener {
833 @Override
834 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700835 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700836 }
837 }
838
839 // Mastership change listener
840 private class InternalMastershipListener implements MastershipListener {
841 @Override
842 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700843 msgSender.execute(() -> {
Simon Huntb745ca62015-07-28 15:37:11 -0700844 sendAllInstances(UPDATE_INSTANCE);
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700845 Device device = deviceService.getDevice(event.subject());
846 if (device != null) {
847 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
848 }
849 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700850 }
851 }
852
853 // Device event listener.
854 private class InternalDeviceListener implements DeviceListener {
855 @Override
856 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700857 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700858 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700859 eventAccummulator.add(event);
860 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700861 }
862 }
863
864 // Link event listener.
865 private class InternalLinkListener implements LinkListener {
866 @Override
867 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700868 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700869 eventAccummulator.add(event);
870 }
871 }
872
873 // Host event listener.
874 private class InternalHostListener implements HostListener {
875 @Override
876 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700877 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700878 eventAccummulator.add(event);
879 }
880 }
881
882 // Intent event listener.
883 private class InternalIntentListener implements IntentListener {
884 @Override
885 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700886 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700887 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700888 }
889 eventAccummulator.add(event);
890 }
891 }
892
893 // Intent event listener.
894 private class InternalFlowListener implements FlowRuleListener {
895 @Override
896 public void event(FlowRuleEvent event) {
897 eventAccummulator.add(event);
898 }
899 }
900
Simon Huntd2747a02015-04-30 22:41:16 -0700901 // encapsulate
902 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700903 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700904 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700905 }
Simon Huntd2747a02015-04-30 22:41:16 -0700906
907 private final Type type;
908 private final ObjectNode payload;
909
910 TrafficEvent(Type type, ObjectNode payload) {
911 this.type = type;
912 this.payload = payload;
913 }
914 }
915
Thomas Vachuska329af532015-03-10 02:08:33 -0700916 // Periodic update of the traffic information
917 private class TrafficMonitor extends TimerTask {
918 @Override
919 public void run() {
920 try {
921 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700922 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700923 case ALL_FLOW_TRAFFIC:
924 requestAllFlowTraffic();
925 break;
926 case ALL_PORT_TRAFFIC:
927 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700928 break;
929 case DEV_LINK_FLOWS:
930 requestDeviceLinkFlows(trafficEvent.payload);
931 break;
932 case SEL_INTENT:
933 requestSelectedIntentTraffic();
934 break;
935 default:
936 // nothing to do
937 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700938 }
939 }
940 } catch (Exception e) {
941 log.warn("Unable to handle traffic request due to {}", e.getMessage());
942 log.warn("Boom!", e);
943 }
944 }
945 }
946
947 // Periodic update of the summary information
948 private class SummaryMonitor extends TimerTask {
949 @Override
950 public void run() {
951 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700952 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700953 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700954 }
955 } catch (Exception e) {
956 log.warn("Unable to handle summary request due to {}", e.getMessage());
957 log.warn("Boom!", e);
958 }
959 }
960 }
961
962 // Accumulates events to drive methodic update of the summary pane.
963 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
964 protected InternalEventAccummulator() {
965 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
966 }
967
968 @Override
969 public void processItems(List<Event> items) {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700970 // Start-of-Debugging
971 long now = System.currentTimeMillis();
972 String me = this.toString();
973 String miniMe = me.replaceAll("^.*@", "me@");
974 log.debug("Time: {}; this: {}, processing items ({} events)",
975 now, miniMe, items.size());
976 // End-of-Debugging
977
Thomas Vachuska329af532015-03-10 02:08:33 -0700978 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700979 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700980 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700981 }
982 } catch (Exception e) {
983 log.warn("Unable to handle summary request due to {}", e.getMessage());
984 log.debug("Boom!", e);
985 }
986 }
987 }
988}