blob: a10e016de747ab2c6baef443e90d96780e493f15 [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 Huntd2747a02015-04-30 22:41:16 -0700106 private static final String TOPO_STOP = "topoStop";
107
108
Thomas Vachuska329af532015-03-10 02:08:33 -0700109 private static final String APP_ID = "org.onosproject.gui";
110
111 private static final long TRAFFIC_FREQUENCY = 5000;
112 private static final long SUMMARY_FREQUENCY = 30000;
113
114 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700115 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700116
117
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700118 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700119
120 private static final int MAX_EVENTS = 1000;
121 private static final int MAX_BATCH_MS = 5000;
122 private static final int MAX_IDLE_MS = 1000;
123
124 private ApplicationId appId;
125
126 private final ClusterEventListener clusterListener = new InternalClusterListener();
127 private final MastershipListener mastershipListener = new InternalMastershipListener();
128 private final DeviceListener deviceListener = new InternalDeviceListener();
129 private final LinkListener linkListener = new InternalLinkListener();
130 private final HostListener hostListener = new InternalHostListener();
131 private final IntentListener intentListener = new InternalIntentListener();
132 private final FlowRuleListener flowListener = new InternalFlowListener();
133
134 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700135 private final ExecutorService msgSender =
136 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700137
Simon Huntd2747a02015-04-30 22:41:16 -0700138 private TimerTask trafficTask = null;
139 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700140
Simon Huntd2747a02015-04-30 22:41:16 -0700141 private TimerTask summaryTask = null;
142 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700143
144 private boolean listenersRemoved = false;
145
146 private TopologyViewIntentFilter intentFilter;
147
148 // Current selection context
149 private Set<Host> selectedHosts;
150 private Set<Device> selectedDevices;
151 private List<Intent> selectedIntents;
152 private int currentIntentIndex = -1;
153
Thomas Vachuska329af532015-03-10 02:08:33 -0700154
155 @Override
156 public void init(UiConnection connection, ServiceDirectory directory) {
157 super.init(connection, directory);
158 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
159 hostService, linkService);
160 appId = directory.get(CoreService.class).registerApplication(APP_ID);
161 }
162
163 @Override
164 public void destroy() {
165 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700166 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700167 super.destroy();
168 }
169
Thomas Vachuska329af532015-03-10 02:08:33 -0700170 @Override
Simon Huntda580882015-05-12 20:58:18 -0700171 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700172 return ImmutableSet.of(
173 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700174 new TopoHeartbeat(),
Simon Huntd2747a02015-04-30 22:41:16 -0700175 new TopoStop(),
176 new ReqSummary(),
177 new CancelSummary(),
178 new SpriteListReq(),
179 new SpriteDataReq(),
180 new RequestDetails(),
181 new UpdateMeta(),
182 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700183
Simon Huntd2747a02015-04-30 22:41:16 -0700184 // TODO: migrate traffic related to separate app
185 new AddHostIntent(),
186 new AddMultiSourceIntent(),
187 new ReqRelatedIntents(),
188 new ReqNextIntent(),
189 new ReqPrevIntent(),
190 new ReqSelectedIntentTraffic(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700191 new ReqAllFlowTraffic(),
192 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700193 new ReqDevLinkFlows(),
194 new CancelTraffic()
195 );
196 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700197
Simon Huntd2747a02015-04-30 22:41:16 -0700198 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700199
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700200 /**
201 * @deprecated in Cardinal Release
202 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700203 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700204 private final class TopoStart extends RequestHandler {
205 private TopoStart() {
206 super(TOPO_START);
207 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700208
Simon Huntd2747a02015-04-30 22:41:16 -0700209 @Override
210 public void process(long sid, ObjectNode payload) {
211 addListeners();
212 sendAllInstances(null);
213 sendAllDevices();
214 sendAllLinks();
215 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700216 }
217 }
218
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700219 /**
220 * @deprecated in Cardinal Release
221 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700222 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700223 private final class TopoHeartbeat extends RequestHandler {
224 private TopoHeartbeat() {
225 super(TOPO_HEARTBEAT);
226 }
227
228 @Override
229 public void process(long sid, ObjectNode payload) {
230 // place holder for now
231 }
232 }
233
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700234 /**
235 * @deprecated in Cardinal Release
236 */
Simon Hunt732bb2e2015-05-13 18:32:16 -0700237 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700238 private final class TopoStop extends RequestHandler {
239 private TopoStop() {
240 super(TOPO_STOP);
241 }
242
243 @Override
244 public void process(long sid, ObjectNode payload) {
245 stopSummaryMonitoring();
246 stopTrafficMonitoring();
247 }
248 }
249
Sho SHIMIZUbe63b232015-06-30 10:57:58 -0700250 /**
251 * @deprecated in Cardinal Release
252 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700253 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700254 private final class ReqSummary extends RequestHandler {
255 private ReqSummary() {
256 super(REQ_SUMMARY);
257 }
258
259 @Override
260 public void process(long sid, ObjectNode payload) {
261 requestSummary(sid);
262 startSummaryMonitoring();
263 }
264 }
265
266 private final class CancelSummary extends RequestHandler {
267 private CancelSummary() {
268 super(CANCEL_SUMMARY);
269 }
270
271 @Override
272 public void process(long sid, ObjectNode payload) {
273 stopSummaryMonitoring();
274 }
275 }
276
277 private final class SpriteListReq extends RequestHandler {
278 private SpriteListReq() {
279 super(SPRITE_LIST_REQ);
280 }
281
282 @Override
283 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700284 ObjectNode root = objectNode();
285 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700286 get(SpriteService.class).getNames().forEach(names::add);
287 root.set("names", names);
288 sendMessage("spriteListResponse", sid, root);
289 }
290 }
291
292 private final class SpriteDataReq extends RequestHandler {
293 private SpriteDataReq() {
294 super(SPRITE_DATA_REQ);
295 }
296
297 @Override
298 public void process(long sid, ObjectNode payload) {
299 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700300 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700301 root.set("data", get(SpriteService.class).get(name));
302 sendMessage("spriteDataResponse", sid, root);
303 }
304 }
305
306 private final class RequestDetails extends RequestHandler {
307 private RequestDetails() {
308 super(REQ_DETAILS);
309 }
310
311 @Override
312 public void process(long sid, ObjectNode payload) {
313 String type = string(payload, "class", "unknown");
314 String id = JsonUtils.string(payload, "id");
315
316 if (type.equals("device")) {
317 sendMessage(deviceDetails(deviceId(id), sid));
318 } else if (type.equals("host")) {
319 sendMessage(hostDetails(hostId(id), sid));
320 }
321 }
322 }
323
324 private final class UpdateMeta extends RequestHandler {
325 private UpdateMeta() {
326 super(UPDATE_META);
327 }
328
329 @Override
330 public void process(long sid, ObjectNode payload) {
331 updateMetaUi(payload);
332 }
333 }
334
335 private final class EqMasters extends RequestHandler {
336 private EqMasters() {
337 super(EQ_MASTERS);
338 }
339
340 @Override
341 public void process(long sid, ObjectNode payload) {
342 directory.get(MastershipAdminService.class).balanceRoles();
343 }
344 }
345
346 // === TODO: move traffic related classes to traffic app
347
348 private final class AddHostIntent extends RequestHandler {
349 private AddHostIntent() {
350 super(ADD_HOST_INTENT);
351 }
352
353 @Override
354 public void process(long sid, ObjectNode payload) {
355 // TODO: add protection against device ids and non-existent hosts.
356 HostId one = hostId(string(payload, "one"));
357 HostId two = hostId(string(payload, "two"));
358
359 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700360 .appId(appId)
361 .one(one)
362 .two(two)
363 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700364
365 intentService.submit(intent);
366 startMonitoringIntent(intent);
367 }
368 }
369
370 private final class AddMultiSourceIntent extends RequestHandler {
371 private AddMultiSourceIntent() {
372 super(ADD_MULTI_SRC_INTENT);
373 }
374
375 @Override
376 public void process(long sid, ObjectNode payload) {
377 // TODO: add protection against device ids and non-existent hosts.
378 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
379 HostId dst = hostId(string(payload, "dst"));
380 Host dstHost = hostService.getHost(dst);
381
382 Set<ConnectPoint> ingressPoints = getHostLocations(src);
383
384 // FIXME: clearly, this is not enough
385 TrafficSelector selector = DefaultTrafficSelector.builder()
386 .matchEthDst(dstHost.mac()).build();
387 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
388
389 MultiPointToSinglePointIntent intent =
390 MultiPointToSinglePointIntent.builder()
391 .appId(appId)
392 .selector(selector)
393 .treatment(treatment)
394 .ingressPoints(ingressPoints)
395 .egressPoint(dstHost.location())
396 .build();
397
398 intentService.submit(intent);
399 startMonitoringIntent(intent);
400 }
401 }
402
403 private final class ReqRelatedIntents extends RequestHandler {
404 private ReqRelatedIntents() {
405 super(REQ_RELATED_INTENTS);
406 }
407
408 @Override
409 public void process(long sid, ObjectNode payload) {
410 // Cancel any other traffic monitoring mode.
411 stopTrafficMonitoring();
412
413 if (!payload.has("ids")) {
414 return;
415 }
416
417 // Get the set of selected hosts and their intents.
418 ArrayNode ids = (ArrayNode) payload.path("ids");
419 selectedHosts = getHosts(ids);
420 selectedDevices = getDevices(ids);
421 selectedIntents = intentFilter.findPathIntents(
422 selectedHosts, selectedDevices, intentService.getIntents());
423 currentIntentIndex = -1;
424
425 if (haveSelectedIntents()) {
426 // Send a message to highlight all links of all monitored intents.
427 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
428 }
429
430 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
431// String hover = string(payload, "hover");
432// if (!isNullOrEmpty(hover)) {
433// // If there is a hover node, include it in the selection and find intents.
434// processHoverExtendedSelection(sid, hover);
435// }
436 }
437 }
438
439 private final class ReqNextIntent extends RequestHandler {
440 private ReqNextIntent() {
441 super(REQ_NEXT_INTENT);
442 }
443
444 @Override
445 public void process(long sid, ObjectNode payload) {
446 stopTrafficMonitoring();
447 requestAnotherRelatedIntent(+1);
448 }
449 }
450
451 private final class ReqPrevIntent extends RequestHandler {
452 private ReqPrevIntent() {
453 super(REQ_PREV_INTENT);
454 }
455
456 @Override
457 public void process(long sid, ObjectNode payload) {
458 stopTrafficMonitoring();
459 requestAnotherRelatedIntent(-1);
460 }
461 }
462
463 private final class ReqSelectedIntentTraffic extends RequestHandler {
464 private ReqSelectedIntentTraffic() {
465 super(REQ_SEL_INTENT_TRAFFIC);
466 }
467
468 @Override
469 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700470 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700471 requestSelectedIntentTraffic();
472 startTrafficMonitoring();
473 }
474 }
475
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700476 private final class ReqAllFlowTraffic extends RequestHandler {
477 private ReqAllFlowTraffic() {
478 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700479 }
480
481 @Override
482 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700483 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
484 requestAllFlowTraffic();
485 }
486 }
487
488 private final class ReqAllPortTraffic extends RequestHandler {
489 private ReqAllPortTraffic() {
490 super(REQ_ALL_PORT_TRAFFIC);
491 }
492
493 @Override
494 public void process(long sid, ObjectNode payload) {
495 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
496 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700497 }
498 }
499
500 private final class ReqDevLinkFlows extends RequestHandler {
501 private ReqDevLinkFlows() {
502 super(REQ_DEV_LINK_FLOWS);
503 }
504
505 @Override
506 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700507 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700508 requestDeviceLinkFlows(payload);
509 }
510 }
511
512 private final class CancelTraffic extends RequestHandler {
513 private CancelTraffic() {
514 super(CANCEL_TRAFFIC);
515 }
516
517 @Override
518 public void process(long sid, ObjectNode payload) {
519 selectedIntents = null;
520 sendMessage(trafficMessage());
521 stopTrafficMonitoring();
522 }
523 }
524
525 //=======================================================================
526
527
Thomas Vachuska329af532015-03-10 02:08:33 -0700528 // Sends the specified data to the client.
529 protected synchronized void sendMessage(ObjectNode data) {
530 UiConnection connection = connection();
531 if (connection != null) {
532 connection.sendMessage(data);
533 }
534 }
535
Simon Huntd2747a02015-04-30 22:41:16 -0700536 // Subscribes for summary messages.
537 private synchronized void requestSummary(long sid) {
538 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700539 }
540
Simon Huntd2747a02015-04-30 22:41:16 -0700541
Thomas Vachuska329af532015-03-10 02:08:33 -0700542 private void cancelAllRequests() {
543 stopSummaryMonitoring();
544 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700545 }
546
547 // Sends all controller nodes to the client as node-added messages.
548 private void sendAllInstances(String messageType) {
549 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
550 Collections.sort(nodes, NODE_COMPARATOR);
551 for (ControllerNode node : nodes) {
552 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
553 messageType));
554 }
555 }
556
557 // Sends all devices to the client as device-added messages.
558 private void sendAllDevices() {
559 // Send optical first, others later for layered rendering
560 for (Device device : deviceService.getDevices()) {
561 if (device.type() == Device.Type.ROADM) {
562 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
563 }
564 }
565 for (Device device : deviceService.getDevices()) {
566 if (device.type() != Device.Type.ROADM) {
567 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
568 }
569 }
570 }
571
572 // Sends all links to the client as link-added messages.
573 private void sendAllLinks() {
574 // Send optical first, others later for layered rendering
575 for (Link link : linkService.getLinks()) {
576 if (link.type() == Link.Type.OPTICAL) {
577 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
578 }
579 }
580 for (Link link : linkService.getLinks()) {
581 if (link.type() != Link.Type.OPTICAL) {
582 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
583 }
584 }
585 }
586
587 // Sends all hosts to the client as host-added messages.
588 private void sendAllHosts() {
589 for (Host host : hostService.getHosts()) {
590 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
591 }
592 }
593
Thomas Vachuska329af532015-03-10 02:08:33 -0700594
Simon Huntd2747a02015-04-30 22:41:16 -0700595 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700596 selectedHosts = new HashSet<>();
597 selectedDevices = new HashSet<>();
598 selectedIntents = new ArrayList<>();
599 selectedIntents.add(intent);
600 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700601 requestAnotherRelatedIntent(+1);
602 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700603 }
604
605
606 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
607 Set<ConnectPoint> points = new HashSet<>();
608 for (HostId hostId : hostIds) {
609 points.add(getHostLocation(hostId));
610 }
611 return points;
612 }
613
614 private HostLocation getHostLocation(HostId hostId) {
615 return hostService.getHost(hostId).location();
616 }
617
618 // Produces a list of host ids from the specified JSON array.
619 private Set<HostId> getHostIds(ArrayNode ids) {
620 Set<HostId> hostIds = new HashSet<>();
621 for (JsonNode id : ids) {
622 hostIds.add(hostId(id.asText()));
623 }
624 return hostIds;
625 }
626
627
Simon Huntd2747a02015-04-30 22:41:16 -0700628 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700629 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700630 trafficTask = new TrafficMonitor();
631 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700632 }
633
634 private synchronized void stopTrafficMonitoring() {
635 if (trafficTask != null) {
636 trafficTask.cancel();
637 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700638 }
639 }
640
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700641 // Subscribes for flow traffic messages.
642 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700643 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700644 sendMessage(trafficSummaryMessage(StatsType.FLOW));
645 }
646
647 // Subscribes for port traffic messages.
648 private synchronized void requestAllPortTraffic() {
649 startTrafficMonitoring();
650 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700651 }
652
Simon Huntd2747a02015-04-30 22:41:16 -0700653 private void requestDeviceLinkFlows(ObjectNode payload) {
654 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700655
656 // Get the set of selected hosts and their intents.
657 ArrayNode ids = (ArrayNode) payload.path("ids");
658 Set<Host> hosts = new HashSet<>();
659 Set<Device> devices = getDevices(ids);
660
661 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700662 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700663 if (!isNullOrEmpty(hover)) {
664 addHover(hosts, devices, hover);
665 }
Simon Huntd2747a02015-04-30 22:41:16 -0700666 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700667 }
668
669
Thomas Vachuska329af532015-03-10 02:08:33 -0700670 private boolean haveSelectedIntents() {
671 return selectedIntents != null && !selectedIntents.isEmpty();
672 }
673
674 // Processes the selection extended with hovered item to segregate items
675 // into primary (those including the hover) vs secondary highlights.
676 private void processHoverExtendedSelection(long sid, String hover) {
677 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
678 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
679 addHover(hoverSelHosts, hoverSelDevices, hover);
680
681 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
682 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
683 selectedIntents);
684 Set<Intent> secondary = new HashSet<>(selectedIntents);
685 secondary.removeAll(primary);
686
687 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700688 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700689 new TrafficClass("secondary", secondary)));
690 }
691
692 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700693 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700694 if (haveSelectedIntents()) {
695 currentIntentIndex = currentIntentIndex + offset;
696 if (currentIntentIndex < 0) {
697 currentIntentIndex = selectedIntents.size() - 1;
698 } else if (currentIntentIndex >= selectedIntents.size()) {
699 currentIntentIndex = 0;
700 }
Simon Huntd2747a02015-04-30 22:41:16 -0700701 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700702 }
703 }
704
705 // Sends traffic information on the related intents with the currently
706 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700707 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
709 log.info("Requested next intent {}", selectedIntent.id());
710
711 Set<Intent> primary = new HashSet<>();
712 primary.add(selectedIntent);
713
714 Set<Intent> secondary = new HashSet<>(selectedIntents);
715 secondary.remove(selectedIntent);
716
717 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700718 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700719 new TrafficClass("secondary", secondary)));
720 }
721
722 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700723 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700724 if (haveSelectedIntents()) {
725 if (currentIntentIndex < 0) {
726 currentIntentIndex = 0;
727 }
728 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
729 log.info("Requested traffic for selected {}", selectedIntent.id());
730
731 Set<Intent> primary = new HashSet<>();
732 primary.add(selectedIntent);
733
734 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700735 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700736 }
737 }
738
Simon Huntd2747a02015-04-30 22:41:16 -0700739 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700740 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700741 summaryTask = new SummaryMonitor();
742 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700743 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700744 }
745
746 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700747 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700748 summaryTask.cancel();
749 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700750 }
Simon Huntd2747a02015-04-30 22:41:16 -0700751 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700752 }
753
Thomas Vachuska329af532015-03-10 02:08:33 -0700754
755 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700756 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700757 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700758 clusterService.addListener(clusterListener);
759 mastershipService.addListener(mastershipListener);
760 deviceService.addListener(deviceListener);
761 linkService.addListener(linkListener);
762 hostService.addListener(hostListener);
763 intentService.addListener(intentListener);
764 flowService.addListener(flowListener);
765 }
766
767 // Removes all internal listeners.
768 private synchronized void removeListeners() {
769 if (!listenersRemoved) {
770 listenersRemoved = true;
771 clusterService.removeListener(clusterListener);
772 mastershipService.removeListener(mastershipListener);
773 deviceService.removeListener(deviceListener);
774 linkService.removeListener(linkListener);
775 hostService.removeListener(hostListener);
776 intentService.removeListener(intentListener);
777 flowService.removeListener(flowListener);
778 }
779 }
780
781 // Cluster event listener.
782 private class InternalClusterListener implements ClusterEventListener {
783 @Override
784 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700785 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700786 }
787 }
788
789 // Mastership change listener
790 private class InternalMastershipListener implements MastershipListener {
791 @Override
792 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700793 msgSender.execute(() -> {
794 sendAllInstances("updateInstance");
795 Device device = deviceService.getDevice(event.subject());
796 if (device != null) {
797 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
798 }
799 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700800 }
801 }
802
803 // Device event listener.
804 private class InternalDeviceListener implements DeviceListener {
805 @Override
806 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700807 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700808 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700809 eventAccummulator.add(event);
810 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700811 }
812 }
813
814 // Link event listener.
815 private class InternalLinkListener implements LinkListener {
816 @Override
817 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700818 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700819 eventAccummulator.add(event);
820 }
821 }
822
823 // Host event listener.
824 private class InternalHostListener implements HostListener {
825 @Override
826 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700827 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700828 eventAccummulator.add(event);
829 }
830 }
831
832 // Intent event listener.
833 private class InternalIntentListener implements IntentListener {
834 @Override
835 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700836 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700837 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700838 }
839 eventAccummulator.add(event);
840 }
841 }
842
843 // Intent event listener.
844 private class InternalFlowListener implements FlowRuleListener {
845 @Override
846 public void event(FlowRuleEvent event) {
847 eventAccummulator.add(event);
848 }
849 }
850
Simon Huntd2747a02015-04-30 22:41:16 -0700851 // encapsulate
852 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700853 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700854 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700855 }
Simon Huntd2747a02015-04-30 22:41:16 -0700856
857 private final Type type;
858 private final ObjectNode payload;
859
860 TrafficEvent(Type type, ObjectNode payload) {
861 this.type = type;
862 this.payload = payload;
863 }
864 }
865
Thomas Vachuska329af532015-03-10 02:08:33 -0700866 // Periodic update of the traffic information
867 private class TrafficMonitor extends TimerTask {
868 @Override
869 public void run() {
870 try {
871 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700872 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700873 case ALL_FLOW_TRAFFIC:
874 requestAllFlowTraffic();
875 break;
876 case ALL_PORT_TRAFFIC:
877 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700878 break;
879 case DEV_LINK_FLOWS:
880 requestDeviceLinkFlows(trafficEvent.payload);
881 break;
882 case SEL_INTENT:
883 requestSelectedIntentTraffic();
884 break;
885 default:
886 // nothing to do
887 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700888 }
889 }
890 } catch (Exception e) {
891 log.warn("Unable to handle traffic request due to {}", e.getMessage());
892 log.warn("Boom!", e);
893 }
894 }
895 }
896
897 // Periodic update of the summary information
898 private class SummaryMonitor extends TimerTask {
899 @Override
900 public void run() {
901 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700902 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700903 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700904 }
905 } catch (Exception e) {
906 log.warn("Unable to handle summary request due to {}", e.getMessage());
907 log.warn("Boom!", e);
908 }
909 }
910 }
911
912 // Accumulates events to drive methodic update of the summary pane.
913 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
914 protected InternalEventAccummulator() {
915 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
916 }
917
918 @Override
919 public void processItems(List<Event> items) {
920 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700921 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700922 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700923 }
924 } catch (Exception e) {
925 log.warn("Unable to handle summary request due to {}", e.getMessage());
926 log.debug("Boom!", e);
927 }
928 }
929 }
930}