blob: 9e0339ec5cd9206e3f355a9cac9781e7d2834b86 [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;
70
71import static com.google.common.base.Strings.isNullOrEmpty;
72import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
73import static org.onosproject.net.DeviceId.deviceId;
74import static org.onosproject.net.HostId.hostId;
75import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
76import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_UPDATED;
77import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
78import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
79
80/**
81 * Web socket capable of interacting with the GUI topology view.
82 */
83public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
84
Simon Huntd2747a02015-04-30 22:41:16 -070085 private static final String REQ_DETAILS = "requestDetails";
86 private static final String UPDATE_META = "updateMeta";
87 private static final String ADD_HOST_INTENT = "addHostIntent";
88 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
89 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
90 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
91 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
92 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
93 private static final String REQ_ALL_TRAFFIC = "requestAllTraffic";
94 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
95 private static final String CANCEL_TRAFFIC = "cancelTraffic";
96 private static final String REQ_SUMMARY = "requestSummary";
97 private static final String CANCEL_SUMMARY = "cancelSummary";
98 private static final String EQ_MASTERS = "equalizeMasters";
99 private static final String SPRITE_LIST_REQ = "spriteListRequest";
100 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
101 private static final String TOPO_START = "topoStart";
102 private static final String TOPO_STOP = "topoStop";
103
104
Thomas Vachuska329af532015-03-10 02:08:33 -0700105 private static final String APP_ID = "org.onosproject.gui";
106
107 private static final long TRAFFIC_FREQUENCY = 5000;
108 private static final long SUMMARY_FREQUENCY = 30000;
109
110 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
111 new Comparator<ControllerNode>() {
112 @Override
113 public int compare(ControllerNode o1, ControllerNode o2) {
114 return o1.id().toString().compareTo(o2.id().toString());
115 }
116 };
117
118
119 private final Timer timer = new Timer("topology-view");
120
121 private static final int MAX_EVENTS = 1000;
122 private static final int MAX_BATCH_MS = 5000;
123 private static final int MAX_IDLE_MS = 1000;
124
125 private ApplicationId appId;
126
127 private final ClusterEventListener clusterListener = new InternalClusterListener();
128 private final MastershipListener mastershipListener = new InternalMastershipListener();
129 private final DeviceListener deviceListener = new InternalDeviceListener();
130 private final LinkListener linkListener = new InternalLinkListener();
131 private final HostListener hostListener = new InternalHostListener();
132 private final IntentListener intentListener = new InternalIntentListener();
133 private final FlowRuleListener flowListener = new InternalFlowListener();
134
135 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
136
Simon Huntd2747a02015-04-30 22:41:16 -0700137 private TimerTask trafficTask = null;
138 private TrafficEvent trafficEvent = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700139
Simon Huntd2747a02015-04-30 22:41:16 -0700140 private TimerTask summaryTask = null;
141 private boolean summaryRunning = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700142
143 private boolean listenersRemoved = false;
144
145 private TopologyViewIntentFilter intentFilter;
146
147 // Current selection context
148 private Set<Host> selectedHosts;
149 private Set<Device> selectedDevices;
150 private List<Intent> selectedIntents;
151 private int currentIntentIndex = -1;
152
Thomas Vachuska329af532015-03-10 02:08:33 -0700153
154 @Override
155 public void init(UiConnection connection, ServiceDirectory directory) {
156 super.init(connection, directory);
157 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
158 hostService, linkService);
159 appId = directory.get(CoreService.class).registerApplication(APP_ID);
160 }
161
162 @Override
163 public void destroy() {
164 cancelAllRequests();
Thomas Vachuska2bb48632015-04-28 14:40:42 -0700165 removeListeners();
Thomas Vachuska329af532015-03-10 02:08:33 -0700166 super.destroy();
167 }
168
Thomas Vachuska329af532015-03-10 02:08:33 -0700169 @Override
Simon Huntd2747a02015-04-30 22:41:16 -0700170 protected Collection<RequestHandler> getHandlers() {
171 return ImmutableSet.of(
172 new TopoStart(),
173 new TopoStop(),
174 new ReqSummary(),
175 new CancelSummary(),
176 new SpriteListReq(),
177 new SpriteDataReq(),
178 new RequestDetails(),
179 new UpdateMeta(),
180 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700181
Simon Huntd2747a02015-04-30 22:41:16 -0700182 // TODO: migrate traffic related to separate app
183 new AddHostIntent(),
184 new AddMultiSourceIntent(),
185 new ReqRelatedIntents(),
186 new ReqNextIntent(),
187 new ReqPrevIntent(),
188 new ReqSelectedIntentTraffic(),
189 new ReqAllTraffic(),
190 new ReqDevLinkFlows(),
191 new CancelTraffic()
192 );
193 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700194
Simon Huntd2747a02015-04-30 22:41:16 -0700195 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700196
Simon Huntd2747a02015-04-30 22:41:16 -0700197 private final class TopoStart extends RequestHandler {
198 private TopoStart() {
199 super(TOPO_START);
200 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700201
Simon Huntd2747a02015-04-30 22:41:16 -0700202 @Override
203 public void process(long sid, ObjectNode payload) {
204 addListeners();
205 sendAllInstances(null);
206 sendAllDevices();
207 sendAllLinks();
208 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700209 }
210 }
211
Simon Huntd2747a02015-04-30 22:41:16 -0700212 private final class TopoStop extends RequestHandler {
213 private TopoStop() {
214 super(TOPO_STOP);
215 }
216
217 @Override
218 public void process(long sid, ObjectNode payload) {
219 stopSummaryMonitoring();
220 stopTrafficMonitoring();
221 }
222 }
223
224 private final class ReqSummary extends RequestHandler {
225 private ReqSummary() {
226 super(REQ_SUMMARY);
227 }
228
229 @Override
230 public void process(long sid, ObjectNode payload) {
231 requestSummary(sid);
232 startSummaryMonitoring();
233 }
234 }
235
236 private final class CancelSummary extends RequestHandler {
237 private CancelSummary() {
238 super(CANCEL_SUMMARY);
239 }
240
241 @Override
242 public void process(long sid, ObjectNode payload) {
243 stopSummaryMonitoring();
244 }
245 }
246
247 private final class SpriteListReq extends RequestHandler {
248 private SpriteListReq() {
249 super(SPRITE_LIST_REQ);
250 }
251
252 @Override
253 public void process(long sid, ObjectNode payload) {
254 ObjectNode root = mapper.createObjectNode();
255 ArrayNode names = mapper.createArrayNode();
256 get(SpriteService.class).getNames().forEach(names::add);
257 root.set("names", names);
258 sendMessage("spriteListResponse", sid, root);
259 }
260 }
261
262 private final class SpriteDataReq extends RequestHandler {
263 private SpriteDataReq() {
264 super(SPRITE_DATA_REQ);
265 }
266
267 @Override
268 public void process(long sid, ObjectNode payload) {
269 String name = string(payload, "name");
270 ObjectNode root = mapper.createObjectNode();
271 root.set("data", get(SpriteService.class).get(name));
272 sendMessage("spriteDataResponse", sid, root);
273 }
274 }
275
276 private final class RequestDetails extends RequestHandler {
277 private RequestDetails() {
278 super(REQ_DETAILS);
279 }
280
281 @Override
282 public void process(long sid, ObjectNode payload) {
283 String type = string(payload, "class", "unknown");
284 String id = JsonUtils.string(payload, "id");
285
286 if (type.equals("device")) {
287 sendMessage(deviceDetails(deviceId(id), sid));
288 } else if (type.equals("host")) {
289 sendMessage(hostDetails(hostId(id), sid));
290 }
291 }
292 }
293
294 private final class UpdateMeta extends RequestHandler {
295 private UpdateMeta() {
296 super(UPDATE_META);
297 }
298
299 @Override
300 public void process(long sid, ObjectNode payload) {
301 updateMetaUi(payload);
302 }
303 }
304
305 private final class EqMasters extends RequestHandler {
306 private EqMasters() {
307 super(EQ_MASTERS);
308 }
309
310 @Override
311 public void process(long sid, ObjectNode payload) {
312 directory.get(MastershipAdminService.class).balanceRoles();
313 }
314 }
315
316 // === TODO: move traffic related classes to traffic app
317
318 private final class AddHostIntent extends RequestHandler {
319 private AddHostIntent() {
320 super(ADD_HOST_INTENT);
321 }
322
323 @Override
324 public void process(long sid, ObjectNode payload) {
325 // TODO: add protection against device ids and non-existent hosts.
326 HostId one = hostId(string(payload, "one"));
327 HostId two = hostId(string(payload, "two"));
328
329 HostToHostIntent intent = HostToHostIntent.builder()
330 .appId(appId)
331 .one(one)
332 .two(two)
333 .build();
334
335 intentService.submit(intent);
336 startMonitoringIntent(intent);
337 }
338 }
339
340 private final class AddMultiSourceIntent extends RequestHandler {
341 private AddMultiSourceIntent() {
342 super(ADD_MULTI_SRC_INTENT);
343 }
344
345 @Override
346 public void process(long sid, ObjectNode payload) {
347 // TODO: add protection against device ids and non-existent hosts.
348 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
349 HostId dst = hostId(string(payload, "dst"));
350 Host dstHost = hostService.getHost(dst);
351
352 Set<ConnectPoint> ingressPoints = getHostLocations(src);
353
354 // FIXME: clearly, this is not enough
355 TrafficSelector selector = DefaultTrafficSelector.builder()
356 .matchEthDst(dstHost.mac()).build();
357 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
358
359 MultiPointToSinglePointIntent intent =
360 MultiPointToSinglePointIntent.builder()
361 .appId(appId)
362 .selector(selector)
363 .treatment(treatment)
364 .ingressPoints(ingressPoints)
365 .egressPoint(dstHost.location())
366 .build();
367
368 intentService.submit(intent);
369 startMonitoringIntent(intent);
370 }
371 }
372
373 private final class ReqRelatedIntents extends RequestHandler {
374 private ReqRelatedIntents() {
375 super(REQ_RELATED_INTENTS);
376 }
377
378 @Override
379 public void process(long sid, ObjectNode payload) {
380 // Cancel any other traffic monitoring mode.
381 stopTrafficMonitoring();
382
383 if (!payload.has("ids")) {
384 return;
385 }
386
387 // Get the set of selected hosts and their intents.
388 ArrayNode ids = (ArrayNode) payload.path("ids");
389 selectedHosts = getHosts(ids);
390 selectedDevices = getDevices(ids);
391 selectedIntents = intentFilter.findPathIntents(
392 selectedHosts, selectedDevices, intentService.getIntents());
393 currentIntentIndex = -1;
394
395 if (haveSelectedIntents()) {
396 // Send a message to highlight all links of all monitored intents.
397 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
398 }
399
400 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
401// String hover = string(payload, "hover");
402// if (!isNullOrEmpty(hover)) {
403// // If there is a hover node, include it in the selection and find intents.
404// processHoverExtendedSelection(sid, hover);
405// }
406 }
407 }
408
409 private final class ReqNextIntent extends RequestHandler {
410 private ReqNextIntent() {
411 super(REQ_NEXT_INTENT);
412 }
413
414 @Override
415 public void process(long sid, ObjectNode payload) {
416 stopTrafficMonitoring();
417 requestAnotherRelatedIntent(+1);
418 }
419 }
420
421 private final class ReqPrevIntent extends RequestHandler {
422 private ReqPrevIntent() {
423 super(REQ_PREV_INTENT);
424 }
425
426 @Override
427 public void process(long sid, ObjectNode payload) {
428 stopTrafficMonitoring();
429 requestAnotherRelatedIntent(-1);
430 }
431 }
432
433 private final class ReqSelectedIntentTraffic extends RequestHandler {
434 private ReqSelectedIntentTraffic() {
435 super(REQ_SEL_INTENT_TRAFFIC);
436 }
437
438 @Override
439 public void process(long sid, ObjectNode payload) {
440 trafficEvent =
441 new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
442 requestSelectedIntentTraffic();
443 startTrafficMonitoring();
444 }
445 }
446
447 private final class ReqAllTraffic extends RequestHandler {
448 private ReqAllTraffic() {
449 super(REQ_ALL_TRAFFIC);
450 }
451
452 @Override
453 public void process(long sid, ObjectNode payload) {
454 trafficEvent =
455 new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
456 requestAllTraffic();
457 }
458 }
459
460 private final class ReqDevLinkFlows extends RequestHandler {
461 private ReqDevLinkFlows() {
462 super(REQ_DEV_LINK_FLOWS);
463 }
464
465 @Override
466 public void process(long sid, ObjectNode payload) {
467 trafficEvent =
468 new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
469 requestDeviceLinkFlows(payload);
470 }
471 }
472
473 private final class CancelTraffic extends RequestHandler {
474 private CancelTraffic() {
475 super(CANCEL_TRAFFIC);
476 }
477
478 @Override
479 public void process(long sid, ObjectNode payload) {
480 selectedIntents = null;
481 sendMessage(trafficMessage());
482 stopTrafficMonitoring();
483 }
484 }
485
486 //=======================================================================
487
488
Thomas Vachuska329af532015-03-10 02:08:33 -0700489 // Sends the specified data to the client.
490 protected synchronized void sendMessage(ObjectNode data) {
491 UiConnection connection = connection();
492 if (connection != null) {
493 connection.sendMessage(data);
494 }
495 }
496
Simon Huntd2747a02015-04-30 22:41:16 -0700497 // Subscribes for summary messages.
498 private synchronized void requestSummary(long sid) {
499 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700500 }
501
Simon Huntd2747a02015-04-30 22:41:16 -0700502
Thomas Vachuska329af532015-03-10 02:08:33 -0700503 private void cancelAllRequests() {
504 stopSummaryMonitoring();
505 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700506 }
507
508 // Sends all controller nodes to the client as node-added messages.
509 private void sendAllInstances(String messageType) {
510 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
511 Collections.sort(nodes, NODE_COMPARATOR);
512 for (ControllerNode node : nodes) {
513 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
514 messageType));
515 }
516 }
517
518 // Sends all devices to the client as device-added messages.
519 private void sendAllDevices() {
520 // Send optical first, others later for layered rendering
521 for (Device device : deviceService.getDevices()) {
522 if (device.type() == Device.Type.ROADM) {
523 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
524 }
525 }
526 for (Device device : deviceService.getDevices()) {
527 if (device.type() != Device.Type.ROADM) {
528 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
529 }
530 }
531 }
532
533 // Sends all links to the client as link-added messages.
534 private void sendAllLinks() {
535 // Send optical first, others later for layered rendering
536 for (Link link : linkService.getLinks()) {
537 if (link.type() == Link.Type.OPTICAL) {
538 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
539 }
540 }
541 for (Link link : linkService.getLinks()) {
542 if (link.type() != Link.Type.OPTICAL) {
543 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
544 }
545 }
546 }
547
548 // Sends all hosts to the client as host-added messages.
549 private void sendAllHosts() {
550 for (Host host : hostService.getHosts()) {
551 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
552 }
553 }
554
Thomas Vachuska329af532015-03-10 02:08:33 -0700555
Simon Huntd2747a02015-04-30 22:41:16 -0700556 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700557 selectedHosts = new HashSet<>();
558 selectedDevices = new HashSet<>();
559 selectedIntents = new ArrayList<>();
560 selectedIntents.add(intent);
561 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700562 requestAnotherRelatedIntent(+1);
563 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700564 }
565
566
567 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
568 Set<ConnectPoint> points = new HashSet<>();
569 for (HostId hostId : hostIds) {
570 points.add(getHostLocation(hostId));
571 }
572 return points;
573 }
574
575 private HostLocation getHostLocation(HostId hostId) {
576 return hostService.getHost(hostId).location();
577 }
578
579 // Produces a list of host ids from the specified JSON array.
580 private Set<HostId> getHostIds(ArrayNode ids) {
581 Set<HostId> hostIds = new HashSet<>();
582 for (JsonNode id : ids) {
583 hostIds.add(hostId(id.asText()));
584 }
585 return hostIds;
586 }
587
588
Simon Huntd2747a02015-04-30 22:41:16 -0700589 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700590 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700591 trafficTask = new TrafficMonitor();
592 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700593 }
594
595 private synchronized void stopTrafficMonitoring() {
596 if (trafficTask != null) {
597 trafficTask.cancel();
598 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700599 }
600 }
601
602 // Subscribes for host traffic messages.
Simon Huntd2747a02015-04-30 22:41:16 -0700603 private synchronized void requestAllTraffic() {
604 startTrafficMonitoring();
605 sendMessage(trafficSummaryMessage());
Thomas Vachuska329af532015-03-10 02:08:33 -0700606 }
607
Simon Huntd2747a02015-04-30 22:41:16 -0700608 private void requestDeviceLinkFlows(ObjectNode payload) {
609 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700610
611 // Get the set of selected hosts and their intents.
612 ArrayNode ids = (ArrayNode) payload.path("ids");
613 Set<Host> hosts = new HashSet<>();
614 Set<Device> devices = getDevices(ids);
615
616 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700617 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700618 if (!isNullOrEmpty(hover)) {
619 addHover(hosts, devices, hover);
620 }
Simon Huntd2747a02015-04-30 22:41:16 -0700621 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700622 }
623
624
Thomas Vachuska329af532015-03-10 02:08:33 -0700625 private boolean haveSelectedIntents() {
626 return selectedIntents != null && !selectedIntents.isEmpty();
627 }
628
629 // Processes the selection extended with hovered item to segregate items
630 // into primary (those including the hover) vs secondary highlights.
631 private void processHoverExtendedSelection(long sid, String hover) {
632 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
633 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
634 addHover(hoverSelHosts, hoverSelDevices, hover);
635
636 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
637 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
638 selectedIntents);
639 Set<Intent> secondary = new HashSet<>(selectedIntents);
640 secondary.removeAll(primary);
641
642 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700643 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700644 new TrafficClass("secondary", secondary)));
645 }
646
647 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700648 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700649 if (haveSelectedIntents()) {
650 currentIntentIndex = currentIntentIndex + offset;
651 if (currentIntentIndex < 0) {
652 currentIntentIndex = selectedIntents.size() - 1;
653 } else if (currentIntentIndex >= selectedIntents.size()) {
654 currentIntentIndex = 0;
655 }
Simon Huntd2747a02015-04-30 22:41:16 -0700656 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700657 }
658 }
659
660 // Sends traffic information on the related intents with the currently
661 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700662 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700663 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
664 log.info("Requested next intent {}", selectedIntent.id());
665
666 Set<Intent> primary = new HashSet<>();
667 primary.add(selectedIntent);
668
669 Set<Intent> secondary = new HashSet<>(selectedIntents);
670 secondary.remove(selectedIntent);
671
672 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700673 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700674 new TrafficClass("secondary", secondary)));
675 }
676
677 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700678 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700679 if (haveSelectedIntents()) {
680 if (currentIntentIndex < 0) {
681 currentIntentIndex = 0;
682 }
683 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
684 log.info("Requested traffic for selected {}", selectedIntent.id());
685
686 Set<Intent> primary = new HashSet<>();
687 primary.add(selectedIntent);
688
689 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700690 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700691 }
692 }
693
Simon Huntd2747a02015-04-30 22:41:16 -0700694 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700695 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700696 summaryTask = new SummaryMonitor();
697 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700698 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700699 }
700
701 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700702 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700703 summaryTask.cancel();
704 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700705 }
Simon Huntd2747a02015-04-30 22:41:16 -0700706 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700707 }
708
Thomas Vachuska329af532015-03-10 02:08:33 -0700709
710 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700711 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700712 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700713 clusterService.addListener(clusterListener);
714 mastershipService.addListener(mastershipListener);
715 deviceService.addListener(deviceListener);
716 linkService.addListener(linkListener);
717 hostService.addListener(hostListener);
718 intentService.addListener(intentListener);
719 flowService.addListener(flowListener);
720 }
721
722 // Removes all internal listeners.
723 private synchronized void removeListeners() {
724 if (!listenersRemoved) {
725 listenersRemoved = true;
726 clusterService.removeListener(clusterListener);
727 mastershipService.removeListener(mastershipListener);
728 deviceService.removeListener(deviceListener);
729 linkService.removeListener(linkListener);
730 hostService.removeListener(hostListener);
731 intentService.removeListener(intentListener);
732 flowService.removeListener(flowListener);
733 }
734 }
735
736 // Cluster event listener.
737 private class InternalClusterListener implements ClusterEventListener {
738 @Override
739 public void event(ClusterEvent event) {
740 sendMessage(instanceMessage(event, null));
741 }
742 }
743
744 // Mastership change listener
745 private class InternalMastershipListener implements MastershipListener {
746 @Override
747 public void event(MastershipEvent event) {
748 sendAllInstances("updateInstance");
749 Device device = deviceService.getDevice(event.subject());
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700750 if (device != null) {
751 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
752 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700753 }
754 }
755
756 // Device event listener.
757 private class InternalDeviceListener implements DeviceListener {
758 @Override
759 public void event(DeviceEvent event) {
760 sendMessage(deviceMessage(event));
761 eventAccummulator.add(event);
762 }
763 }
764
765 // Link event listener.
766 private class InternalLinkListener implements LinkListener {
767 @Override
768 public void event(LinkEvent event) {
769 sendMessage(linkMessage(event));
770 eventAccummulator.add(event);
771 }
772 }
773
774 // Host event listener.
775 private class InternalHostListener implements HostListener {
776 @Override
777 public void event(HostEvent event) {
778 sendMessage(hostMessage(event));
779 eventAccummulator.add(event);
780 }
781 }
782
783 // Intent event listener.
784 private class InternalIntentListener implements IntentListener {
785 @Override
786 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700787 if (trafficTask != null) {
788 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700789 }
790 eventAccummulator.add(event);
791 }
792 }
793
794 // Intent event listener.
795 private class InternalFlowListener implements FlowRuleListener {
796 @Override
797 public void event(FlowRuleEvent event) {
798 eventAccummulator.add(event);
799 }
800 }
801
Simon Huntd2747a02015-04-30 22:41:16 -0700802 // encapsulate
803 private static class TrafficEvent {
804 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
805
806 private final Type type;
807 private final ObjectNode payload;
808
809 TrafficEvent(Type type, ObjectNode payload) {
810 this.type = type;
811 this.payload = payload;
812 }
813 }
814
Thomas Vachuska329af532015-03-10 02:08:33 -0700815 // Periodic update of the traffic information
816 private class TrafficMonitor extends TimerTask {
817 @Override
818 public void run() {
819 try {
820 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700821 switch (trafficEvent.type) {
822 case ALL_TRAFFIC:
823 requestAllTraffic();
824 break;
825 case DEV_LINK_FLOWS:
826 requestDeviceLinkFlows(trafficEvent.payload);
827 break;
828 case SEL_INTENT:
829 requestSelectedIntentTraffic();
830 break;
831 default:
832 // nothing to do
833 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700834 }
835 }
836 } catch (Exception e) {
837 log.warn("Unable to handle traffic request due to {}", e.getMessage());
838 log.warn("Boom!", e);
839 }
840 }
841 }
842
843 // Periodic update of the summary information
844 private class SummaryMonitor extends TimerTask {
845 @Override
846 public void run() {
847 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700848 if (summaryRunning) {
849 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700850 }
851 } catch (Exception e) {
852 log.warn("Unable to handle summary request due to {}", e.getMessage());
853 log.warn("Boom!", e);
854 }
855 }
856 }
857
858 // Accumulates events to drive methodic update of the summary pane.
859 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
860 protected InternalEventAccummulator() {
861 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
862 }
863
864 @Override
865 public void processItems(List<Event> items) {
866 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700867 if (summaryRunning) {
868 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700869 }
870 } catch (Exception e) {
871 log.warn("Unable to handle summary request due to {}", e.getMessage());
872 log.debug("Boom!", e);
873 }
874 }
875 }
876}