blob: 9a360bfd1a0c6a5fa997051a609954e173601189 [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 Huntd2747a02015-04-30 22:41:16 -0700217 // TODO: migrate traffic related to separate app
218 new AddHostIntent(),
219 new AddMultiSourceIntent(),
220 new ReqRelatedIntents(),
221 new ReqNextIntent(),
222 new ReqPrevIntent(),
223 new ReqSelectedIntentTraffic(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700224 new ReqAllFlowTraffic(),
225 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700226 new ReqDevLinkFlows(),
227 new CancelTraffic()
228 );
229 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700230
Simon Hunte05cae42015-07-23 17:35:24 -0700231 /**
232 * Injects the topology overlay cache.
233 *
234 * @param overlayCache injected cache
235 */
236 void setOverlayCache(TopoOverlayCache overlayCache) {
237 this.overlayCache = overlayCache;
238 }
239
Simon Huntd2747a02015-04-30 22:41:16 -0700240 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700241
Simon Huntd2747a02015-04-30 22:41:16 -0700242 private final class TopoStart extends RequestHandler {
243 private TopoStart() {
244 super(TOPO_START);
245 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700246
Simon Huntd2747a02015-04-30 22:41:16 -0700247 @Override
248 public void process(long sid, ObjectNode payload) {
249 addListeners();
250 sendAllInstances(null);
251 sendAllDevices();
252 sendAllLinks();
253 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700254 }
255 }
256
Simon Hunt732bb2e2015-05-13 18:32:16 -0700257 private final class TopoHeartbeat extends RequestHandler {
258 private TopoHeartbeat() {
259 super(TOPO_HEARTBEAT);
260 }
261
262 @Override
263 public void process(long sid, ObjectNode payload) {
264 // place holder for now
265 }
266 }
267
Simon Hunte05cae42015-07-23 17:35:24 -0700268 private final class TopoSelectOverlay extends RequestHandler {
269 private TopoSelectOverlay() {
270 super(TOPO_SELECT_OVERLAY);
271 }
272
273 @Override
274 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700275 String deact = string(payload, DEACTIVATE);
276 String act = string(payload, ACTIVATE);
Simon Hunte05cae42015-07-23 17:35:24 -0700277 overlayCache.switchOverlay(deact, act);
278 }
279 }
280
Simon Huntd2747a02015-04-30 22:41:16 -0700281 private final class TopoStop extends RequestHandler {
282 private TopoStop() {
283 super(TOPO_STOP);
284 }
285
286 @Override
287 public void process(long sid, ObjectNode payload) {
288 stopSummaryMonitoring();
289 stopTrafficMonitoring();
290 }
291 }
292
293 private final class ReqSummary extends RequestHandler {
294 private ReqSummary() {
295 super(REQ_SUMMARY);
296 }
297
298 @Override
299 public void process(long sid, ObjectNode payload) {
300 requestSummary(sid);
301 startSummaryMonitoring();
302 }
303 }
304
305 private final class CancelSummary extends RequestHandler {
306 private CancelSummary() {
307 super(CANCEL_SUMMARY);
308 }
309
310 @Override
311 public void process(long sid, ObjectNode payload) {
312 stopSummaryMonitoring();
313 }
314 }
315
316 private final class SpriteListReq extends RequestHandler {
317 private SpriteListReq() {
318 super(SPRITE_LIST_REQ);
319 }
320
321 @Override
322 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700323 ObjectNode root = objectNode();
324 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700325 get(SpriteService.class).getNames().forEach(names::add);
Simon Huntb745ca62015-07-28 15:37:11 -0700326 root.set(NAMES, names);
327 sendMessage(SPRITE_LIST_RESPONSE, sid, root);
Simon Huntd2747a02015-04-30 22:41:16 -0700328 }
329 }
330
331 private final class SpriteDataReq extends RequestHandler {
332 private SpriteDataReq() {
333 super(SPRITE_DATA_REQ);
334 }
335
336 @Override
337 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700338 String name = string(payload, NAME);
Simon Huntda580882015-05-12 20:58:18 -0700339 ObjectNode root = objectNode();
Simon Huntb745ca62015-07-28 15:37:11 -0700340 root.set(DATA, get(SpriteService.class).get(name));
341 sendMessage(SPRITE_DATA_RESPONSE, sid, root);
Simon Huntd2747a02015-04-30 22:41:16 -0700342 }
343 }
344
345 private final class RequestDetails extends RequestHandler {
346 private RequestDetails() {
347 super(REQ_DETAILS);
348 }
349
350 @Override
351 public void process(long sid, ObjectNode payload) {
Simon Huntb745ca62015-07-28 15:37:11 -0700352 String type = string(payload, CLASS, UNKNOWN);
353 String id = string(payload, ID);
354 PropertyPanel pp = null;
Simon Huntd2747a02015-04-30 22:41:16 -0700355
Simon Huntb745ca62015-07-28 15:37:11 -0700356 if (type.equals(DEVICE)) {
357 pp = deviceDetails(deviceId(id), sid);
358 overlayCache.currentOverlay().modifyDeviceDetails(pp);
359 } else if (type.equals(HOST)) {
360 pp = hostDetails(hostId(id), sid);
361 overlayCache.currentOverlay().modifyHostDetails(pp);
Simon Huntd2747a02015-04-30 22:41:16 -0700362 }
Simon Huntb745ca62015-07-28 15:37:11 -0700363
364 ObjectNode json = JsonUtils.envelope(SHOW_DETAILS, sid, json(pp));
365 sendMessage(json);
Simon Huntd2747a02015-04-30 22:41:16 -0700366 }
367 }
368
369 private final class UpdateMeta extends RequestHandler {
370 private UpdateMeta() {
371 super(UPDATE_META);
372 }
373
374 @Override
375 public void process(long sid, ObjectNode payload) {
376 updateMetaUi(payload);
377 }
378 }
379
380 private final class EqMasters extends RequestHandler {
381 private EqMasters() {
382 super(EQ_MASTERS);
383 }
384
385 @Override
386 public void process(long sid, ObjectNode payload) {
387 directory.get(MastershipAdminService.class).balanceRoles();
388 }
389 }
390
391 // === TODO: move traffic related classes to traffic app
392
393 private final class AddHostIntent extends RequestHandler {
394 private AddHostIntent() {
395 super(ADD_HOST_INTENT);
396 }
397
398 @Override
399 public void process(long sid, ObjectNode payload) {
400 // TODO: add protection against device ids and non-existent hosts.
Simon Huntb745ca62015-07-28 15:37:11 -0700401 HostId one = hostId(string(payload, ONE));
402 HostId two = hostId(string(payload, TWO));
Simon Huntd2747a02015-04-30 22:41:16 -0700403
404 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700405 .appId(appId)
406 .one(one)
407 .two(two)
408 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700409
410 intentService.submit(intent);
411 startMonitoringIntent(intent);
412 }
413 }
414
415 private final class AddMultiSourceIntent extends RequestHandler {
416 private AddMultiSourceIntent() {
417 super(ADD_MULTI_SRC_INTENT);
418 }
419
420 @Override
421 public void process(long sid, ObjectNode payload) {
422 // TODO: add protection against device ids and non-existent hosts.
Simon Huntb745ca62015-07-28 15:37:11 -0700423 Set<HostId> src = getHostIds((ArrayNode) payload.path(SRC));
424 HostId dst = hostId(string(payload, DST));
Simon Huntd2747a02015-04-30 22:41:16 -0700425 Host dstHost = hostService.getHost(dst);
426
427 Set<ConnectPoint> ingressPoints = getHostLocations(src);
428
429 // FIXME: clearly, this is not enough
430 TrafficSelector selector = DefaultTrafficSelector.builder()
431 .matchEthDst(dstHost.mac()).build();
432 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
433
434 MultiPointToSinglePointIntent intent =
435 MultiPointToSinglePointIntent.builder()
436 .appId(appId)
437 .selector(selector)
438 .treatment(treatment)
439 .ingressPoints(ingressPoints)
440 .egressPoint(dstHost.location())
441 .build();
442
443 intentService.submit(intent);
444 startMonitoringIntent(intent);
445 }
446 }
447
448 private final class ReqRelatedIntents extends RequestHandler {
449 private ReqRelatedIntents() {
450 super(REQ_RELATED_INTENTS);
451 }
452
453 @Override
454 public void process(long sid, ObjectNode payload) {
455 // Cancel any other traffic monitoring mode.
456 stopTrafficMonitoring();
457
Simon Huntb745ca62015-07-28 15:37:11 -0700458 if (!payload.has(IDS)) {
Simon Huntd2747a02015-04-30 22:41:16 -0700459 return;
460 }
461
462 // Get the set of selected hosts and their intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700463 ArrayNode ids = (ArrayNode) payload.path(IDS);
Simon Huntd2747a02015-04-30 22:41:16 -0700464 selectedHosts = getHosts(ids);
465 selectedDevices = getDevices(ids);
466 selectedIntents = intentFilter.findPathIntents(
467 selectedHosts, selectedDevices, intentService.getIntents());
468 currentIntentIndex = -1;
469
470 if (haveSelectedIntents()) {
471 // Send a message to highlight all links of all monitored intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700472 sendMessage(trafficMessage(new TrafficClass(PRIMARY, selectedIntents)));
Simon Huntd2747a02015-04-30 22:41:16 -0700473 }
474
475 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
476// String hover = string(payload, "hover");
477// if (!isNullOrEmpty(hover)) {
478// // If there is a hover node, include it in the selection and find intents.
479// processHoverExtendedSelection(sid, hover);
480// }
481 }
482 }
483
484 private final class ReqNextIntent extends RequestHandler {
485 private ReqNextIntent() {
486 super(REQ_NEXT_INTENT);
487 }
488
489 @Override
490 public void process(long sid, ObjectNode payload) {
491 stopTrafficMonitoring();
492 requestAnotherRelatedIntent(+1);
493 }
494 }
495
496 private final class ReqPrevIntent extends RequestHandler {
497 private ReqPrevIntent() {
498 super(REQ_PREV_INTENT);
499 }
500
501 @Override
502 public void process(long sid, ObjectNode payload) {
503 stopTrafficMonitoring();
504 requestAnotherRelatedIntent(-1);
505 }
506 }
507
508 private final class ReqSelectedIntentTraffic extends RequestHandler {
509 private ReqSelectedIntentTraffic() {
510 super(REQ_SEL_INTENT_TRAFFIC);
511 }
512
513 @Override
514 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700515 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700516 requestSelectedIntentTraffic();
517 startTrafficMonitoring();
518 }
519 }
520
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700521 private final class ReqAllFlowTraffic extends RequestHandler {
522 private ReqAllFlowTraffic() {
523 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700524 }
525
526 @Override
527 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700528 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
529 requestAllFlowTraffic();
530 }
531 }
532
533 private final class ReqAllPortTraffic extends RequestHandler {
534 private ReqAllPortTraffic() {
535 super(REQ_ALL_PORT_TRAFFIC);
536 }
537
538 @Override
539 public void process(long sid, ObjectNode payload) {
540 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
541 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700542 }
543 }
544
545 private final class ReqDevLinkFlows extends RequestHandler {
546 private ReqDevLinkFlows() {
547 super(REQ_DEV_LINK_FLOWS);
548 }
549
550 @Override
551 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700552 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700553 requestDeviceLinkFlows(payload);
554 }
555 }
556
557 private final class CancelTraffic extends RequestHandler {
558 private CancelTraffic() {
559 super(CANCEL_TRAFFIC);
560 }
561
562 @Override
563 public void process(long sid, ObjectNode payload) {
564 selectedIntents = null;
565 sendMessage(trafficMessage());
566 stopTrafficMonitoring();
567 }
568 }
569
570 //=======================================================================
571
572
Thomas Vachuska329af532015-03-10 02:08:33 -0700573 // Sends the specified data to the client.
574 protected synchronized void sendMessage(ObjectNode data) {
575 UiConnection connection = connection();
576 if (connection != null) {
577 connection.sendMessage(data);
578 }
579 }
580
Simon Huntd2747a02015-04-30 22:41:16 -0700581 // Subscribes for summary messages.
582 private synchronized void requestSummary(long sid) {
Simon Hunt0af1ec32015-07-24 12:17:55 -0700583 PropertyPanel pp = summmaryMessage(sid);
584 overlayCache.currentOverlay().modifySummary(pp);
Simon Huntb745ca62015-07-28 15:37:11 -0700585 ObjectNode json = JsonUtils.envelope(SHOW_SUMMARY, sid, json(pp));
Simon Hunt0af1ec32015-07-24 12:17:55 -0700586 sendMessage(json);
Thomas Vachuska329af532015-03-10 02:08:33 -0700587 }
588
Simon Huntd2747a02015-04-30 22:41:16 -0700589
Thomas Vachuska329af532015-03-10 02:08:33 -0700590 private void cancelAllRequests() {
591 stopSummaryMonitoring();
592 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700593 }
594
595 // Sends all controller nodes to the client as node-added messages.
596 private void sendAllInstances(String messageType) {
597 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
598 Collections.sort(nodes, NODE_COMPARATOR);
599 for (ControllerNode node : nodes) {
600 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
601 messageType));
602 }
603 }
604
605 // Sends all devices to the client as device-added messages.
606 private void sendAllDevices() {
607 // Send optical first, others later for layered rendering
608 for (Device device : deviceService.getDevices()) {
609 if (device.type() == Device.Type.ROADM) {
610 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
611 }
612 }
613 for (Device device : deviceService.getDevices()) {
614 if (device.type() != Device.Type.ROADM) {
615 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
616 }
617 }
618 }
619
620 // Sends all links to the client as link-added messages.
621 private void sendAllLinks() {
622 // Send optical first, others later for layered rendering
623 for (Link link : linkService.getLinks()) {
624 if (link.type() == Link.Type.OPTICAL) {
625 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
626 }
627 }
628 for (Link link : linkService.getLinks()) {
629 if (link.type() != Link.Type.OPTICAL) {
630 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
631 }
632 }
633 }
634
635 // Sends all hosts to the client as host-added messages.
636 private void sendAllHosts() {
637 for (Host host : hostService.getHosts()) {
638 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
639 }
640 }
641
Thomas Vachuska329af532015-03-10 02:08:33 -0700642
Simon Huntd2747a02015-04-30 22:41:16 -0700643 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700644 selectedHosts = new HashSet<>();
645 selectedDevices = new HashSet<>();
646 selectedIntents = new ArrayList<>();
647 selectedIntents.add(intent);
648 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700649 requestAnotherRelatedIntent(+1);
650 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700651 }
652
653
654 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
655 Set<ConnectPoint> points = new HashSet<>();
656 for (HostId hostId : hostIds) {
657 points.add(getHostLocation(hostId));
658 }
659 return points;
660 }
661
662 private HostLocation getHostLocation(HostId hostId) {
663 return hostService.getHost(hostId).location();
664 }
665
666 // Produces a list of host ids from the specified JSON array.
667 private Set<HostId> getHostIds(ArrayNode ids) {
668 Set<HostId> hostIds = new HashSet<>();
669 for (JsonNode id : ids) {
670 hostIds.add(hostId(id.asText()));
671 }
672 return hostIds;
673 }
674
675
Simon Huntd2747a02015-04-30 22:41:16 -0700676 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700677 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700678 trafficTask = new TrafficMonitor();
679 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700680 }
681
682 private synchronized void stopTrafficMonitoring() {
683 if (trafficTask != null) {
684 trafficTask.cancel();
685 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700686 }
687 }
688
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700689 // Subscribes for flow traffic messages.
690 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700691 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700692 sendMessage(trafficSummaryMessage(StatsType.FLOW));
693 }
694
695 // Subscribes for port traffic messages.
696 private synchronized void requestAllPortTraffic() {
697 startTrafficMonitoring();
698 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700699 }
700
Simon Huntd2747a02015-04-30 22:41:16 -0700701 private void requestDeviceLinkFlows(ObjectNode payload) {
702 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700703
704 // Get the set of selected hosts and their intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700705 ArrayNode ids = (ArrayNode) payload.path(IDS);
Thomas Vachuska329af532015-03-10 02:08:33 -0700706 Set<Host> hosts = new HashSet<>();
707 Set<Device> devices = getDevices(ids);
708
709 // If there is a hover node, include it in the hosts and find intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700710 String hover = JsonUtils.string(payload, HOVER);
Thomas Vachuska329af532015-03-10 02:08:33 -0700711 if (!isNullOrEmpty(hover)) {
712 addHover(hosts, devices, hover);
713 }
Simon Huntd2747a02015-04-30 22:41:16 -0700714 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700715 }
716
717
Thomas Vachuska329af532015-03-10 02:08:33 -0700718 private boolean haveSelectedIntents() {
719 return selectedIntents != null && !selectedIntents.isEmpty();
720 }
721
722 // Processes the selection extended with hovered item to segregate items
723 // into primary (those including the hover) vs secondary highlights.
724 private void processHoverExtendedSelection(long sid, String hover) {
725 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
726 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
727 addHover(hoverSelHosts, hoverSelDevices, hover);
728
729 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
730 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
731 selectedIntents);
732 Set<Intent> secondary = new HashSet<>(selectedIntents);
733 secondary.removeAll(primary);
734
735 // Send a message to highlight all links of all monitored intents.
Simon Huntb745ca62015-07-28 15:37:11 -0700736 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary),
737 new TrafficClass(SECONDARY, secondary)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700738 }
739
740 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700741 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700742 if (haveSelectedIntents()) {
743 currentIntentIndex = currentIntentIndex + offset;
744 if (currentIntentIndex < 0) {
745 currentIntentIndex = selectedIntents.size() - 1;
746 } else if (currentIntentIndex >= selectedIntents.size()) {
747 currentIntentIndex = 0;
748 }
Simon Huntd2747a02015-04-30 22:41:16 -0700749 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700750 }
751 }
752
753 // Sends traffic information on the related intents with the currently
754 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700755 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700756 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
Simon Huntb745ca62015-07-28 15:37:11 -0700757 log.debug("Requested next intent {}", selectedIntent.id());
Thomas Vachuska329af532015-03-10 02:08:33 -0700758
759 Set<Intent> primary = new HashSet<>();
760 primary.add(selectedIntent);
761
762 Set<Intent> secondary = new HashSet<>(selectedIntents);
763 secondary.remove(selectedIntent);
764
765 // Send a message to highlight all links of the selected intent.
Simon Huntb745ca62015-07-28 15:37:11 -0700766 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary),
767 new TrafficClass(SECONDARY, secondary)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700768 }
769
770 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700771 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700772 if (haveSelectedIntents()) {
773 if (currentIntentIndex < 0) {
774 currentIntentIndex = 0;
775 }
776 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
Simon Huntb745ca62015-07-28 15:37:11 -0700777 log.debug("Requested traffic for selected {}", selectedIntent.id());
Thomas Vachuska329af532015-03-10 02:08:33 -0700778
779 Set<Intent> primary = new HashSet<>();
780 primary.add(selectedIntent);
781
782 // Send a message to highlight all links of the selected intent.
Simon Huntb745ca62015-07-28 15:37:11 -0700783 sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700784 }
785 }
786
Simon Huntd2747a02015-04-30 22:41:16 -0700787 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700788 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700789 summaryTask = new SummaryMonitor();
790 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700791 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700792 }
793
794 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700795 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700796 summaryTask.cancel();
797 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700798 }
Simon Huntd2747a02015-04-30 22:41:16 -0700799 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700800 }
801
Thomas Vachuska329af532015-03-10 02:08:33 -0700802
803 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700804 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700805 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700806 clusterService.addListener(clusterListener);
807 mastershipService.addListener(mastershipListener);
808 deviceService.addListener(deviceListener);
809 linkService.addListener(linkListener);
810 hostService.addListener(hostListener);
811 intentService.addListener(intentListener);
812 flowService.addListener(flowListener);
813 }
814
815 // Removes all internal listeners.
816 private synchronized void removeListeners() {
817 if (!listenersRemoved) {
818 listenersRemoved = true;
819 clusterService.removeListener(clusterListener);
820 mastershipService.removeListener(mastershipListener);
821 deviceService.removeListener(deviceListener);
822 linkService.removeListener(linkListener);
823 hostService.removeListener(hostListener);
824 intentService.removeListener(intentListener);
825 flowService.removeListener(flowListener);
826 }
827 }
828
829 // Cluster event listener.
830 private class InternalClusterListener implements ClusterEventListener {
831 @Override
832 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700833 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700834 }
835 }
836
837 // Mastership change listener
838 private class InternalMastershipListener implements MastershipListener {
839 @Override
840 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700841 msgSender.execute(() -> {
Simon Huntb745ca62015-07-28 15:37:11 -0700842 sendAllInstances(UPDATE_INSTANCE);
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700843 Device device = deviceService.getDevice(event.subject());
844 if (device != null) {
845 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
846 }
847 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700848 }
849 }
850
851 // Device event listener.
852 private class InternalDeviceListener implements DeviceListener {
853 @Override
854 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700855 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700856 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700857 eventAccummulator.add(event);
858 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700859 }
860 }
861
862 // Link event listener.
863 private class InternalLinkListener implements LinkListener {
864 @Override
865 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700866 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700867 eventAccummulator.add(event);
868 }
869 }
870
871 // Host event listener.
872 private class InternalHostListener implements HostListener {
873 @Override
874 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700875 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700876 eventAccummulator.add(event);
877 }
878 }
879
880 // Intent event listener.
881 private class InternalIntentListener implements IntentListener {
882 @Override
883 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700884 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700885 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700886 }
887 eventAccummulator.add(event);
888 }
889 }
890
891 // Intent event listener.
892 private class InternalFlowListener implements FlowRuleListener {
893 @Override
894 public void event(FlowRuleEvent event) {
895 eventAccummulator.add(event);
896 }
897 }
898
Simon Huntd2747a02015-04-30 22:41:16 -0700899 // encapsulate
900 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700901 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700902 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700903 }
Simon Huntd2747a02015-04-30 22:41:16 -0700904
905 private final Type type;
906 private final ObjectNode payload;
907
908 TrafficEvent(Type type, ObjectNode payload) {
909 this.type = type;
910 this.payload = payload;
911 }
912 }
913
Thomas Vachuska329af532015-03-10 02:08:33 -0700914 // Periodic update of the traffic information
915 private class TrafficMonitor extends TimerTask {
916 @Override
917 public void run() {
918 try {
919 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700920 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700921 case ALL_FLOW_TRAFFIC:
922 requestAllFlowTraffic();
923 break;
924 case ALL_PORT_TRAFFIC:
925 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700926 break;
927 case DEV_LINK_FLOWS:
928 requestDeviceLinkFlows(trafficEvent.payload);
929 break;
930 case SEL_INTENT:
931 requestSelectedIntentTraffic();
932 break;
933 default:
934 // nothing to do
935 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700936 }
937 }
938 } catch (Exception e) {
939 log.warn("Unable to handle traffic request due to {}", e.getMessage());
940 log.warn("Boom!", e);
941 }
942 }
943 }
944
945 // Periodic update of the summary information
946 private class SummaryMonitor extends TimerTask {
947 @Override
948 public void run() {
949 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700950 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700951 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700952 }
953 } catch (Exception e) {
954 log.warn("Unable to handle summary request due to {}", e.getMessage());
955 log.warn("Boom!", e);
956 }
957 }
958 }
959
960 // Accumulates events to drive methodic update of the summary pane.
961 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
962 protected InternalEventAccummulator() {
963 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
964 }
965
966 @Override
967 public void processItems(List<Event> items) {
968 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700969 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700970 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700971 }
972 } catch (Exception e) {
973 log.warn("Unable to handle summary request due to {}", e.getMessage());
974 log.debug("Boom!", e);
975 }
976 }
977 }
978}