blob: 478bfe7333f7798521540ed1b676c17e8ba18c3d [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 Huntda580882015-05-12 20:58:18 -0700170 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd2747a02015-04-30 22:41:16 -0700171 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 Huntc54cd1b2015-05-11 13:43:44 -0700197 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700198 private final class TopoStart extends RequestHandler {
199 private TopoStart() {
200 super(TOPO_START);
201 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700202
Simon Huntd2747a02015-04-30 22:41:16 -0700203 @Override
204 public void process(long sid, ObjectNode payload) {
205 addListeners();
206 sendAllInstances(null);
207 sendAllDevices();
208 sendAllLinks();
209 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700210 }
211 }
212
Simon Huntc54cd1b2015-05-11 13:43:44 -0700213 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700214 private final class TopoStop extends RequestHandler {
215 private TopoStop() {
216 super(TOPO_STOP);
217 }
218
219 @Override
220 public void process(long sid, ObjectNode payload) {
221 stopSummaryMonitoring();
222 stopTrafficMonitoring();
223 }
224 }
225
Simon Huntc54cd1b2015-05-11 13:43:44 -0700226 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700227 private final class ReqSummary extends RequestHandler {
228 private ReqSummary() {
229 super(REQ_SUMMARY);
230 }
231
232 @Override
233 public void process(long sid, ObjectNode payload) {
234 requestSummary(sid);
235 startSummaryMonitoring();
236 }
237 }
238
239 private final class CancelSummary extends RequestHandler {
240 private CancelSummary() {
241 super(CANCEL_SUMMARY);
242 }
243
244 @Override
245 public void process(long sid, ObjectNode payload) {
246 stopSummaryMonitoring();
247 }
248 }
249
250 private final class SpriteListReq extends RequestHandler {
251 private SpriteListReq() {
252 super(SPRITE_LIST_REQ);
253 }
254
255 @Override
256 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700257 ObjectNode root = objectNode();
258 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700259 get(SpriteService.class).getNames().forEach(names::add);
260 root.set("names", names);
261 sendMessage("spriteListResponse", sid, root);
262 }
263 }
264
265 private final class SpriteDataReq extends RequestHandler {
266 private SpriteDataReq() {
267 super(SPRITE_DATA_REQ);
268 }
269
270 @Override
271 public void process(long sid, ObjectNode payload) {
272 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700273 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700274 root.set("data", get(SpriteService.class).get(name));
275 sendMessage("spriteDataResponse", sid, root);
276 }
277 }
278
279 private final class RequestDetails extends RequestHandler {
280 private RequestDetails() {
281 super(REQ_DETAILS);
282 }
283
284 @Override
285 public void process(long sid, ObjectNode payload) {
286 String type = string(payload, "class", "unknown");
287 String id = JsonUtils.string(payload, "id");
288
289 if (type.equals("device")) {
290 sendMessage(deviceDetails(deviceId(id), sid));
291 } else if (type.equals("host")) {
292 sendMessage(hostDetails(hostId(id), sid));
293 }
294 }
295 }
296
297 private final class UpdateMeta extends RequestHandler {
298 private UpdateMeta() {
299 super(UPDATE_META);
300 }
301
302 @Override
303 public void process(long sid, ObjectNode payload) {
304 updateMetaUi(payload);
305 }
306 }
307
308 private final class EqMasters extends RequestHandler {
309 private EqMasters() {
310 super(EQ_MASTERS);
311 }
312
313 @Override
314 public void process(long sid, ObjectNode payload) {
315 directory.get(MastershipAdminService.class).balanceRoles();
316 }
317 }
318
319 // === TODO: move traffic related classes to traffic app
320
321 private final class AddHostIntent extends RequestHandler {
322 private AddHostIntent() {
323 super(ADD_HOST_INTENT);
324 }
325
326 @Override
327 public void process(long sid, ObjectNode payload) {
328 // TODO: add protection against device ids and non-existent hosts.
329 HostId one = hostId(string(payload, "one"));
330 HostId two = hostId(string(payload, "two"));
331
332 HostToHostIntent intent = HostToHostIntent.builder()
333 .appId(appId)
334 .one(one)
335 .two(two)
336 .build();
337
338 intentService.submit(intent);
339 startMonitoringIntent(intent);
340 }
341 }
342
343 private final class AddMultiSourceIntent extends RequestHandler {
344 private AddMultiSourceIntent() {
345 super(ADD_MULTI_SRC_INTENT);
346 }
347
348 @Override
349 public void process(long sid, ObjectNode payload) {
350 // TODO: add protection against device ids and non-existent hosts.
351 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
352 HostId dst = hostId(string(payload, "dst"));
353 Host dstHost = hostService.getHost(dst);
354
355 Set<ConnectPoint> ingressPoints = getHostLocations(src);
356
357 // FIXME: clearly, this is not enough
358 TrafficSelector selector = DefaultTrafficSelector.builder()
359 .matchEthDst(dstHost.mac()).build();
360 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
361
362 MultiPointToSinglePointIntent intent =
363 MultiPointToSinglePointIntent.builder()
364 .appId(appId)
365 .selector(selector)
366 .treatment(treatment)
367 .ingressPoints(ingressPoints)
368 .egressPoint(dstHost.location())
369 .build();
370
371 intentService.submit(intent);
372 startMonitoringIntent(intent);
373 }
374 }
375
376 private final class ReqRelatedIntents extends RequestHandler {
377 private ReqRelatedIntents() {
378 super(REQ_RELATED_INTENTS);
379 }
380
381 @Override
382 public void process(long sid, ObjectNode payload) {
383 // Cancel any other traffic monitoring mode.
384 stopTrafficMonitoring();
385
386 if (!payload.has("ids")) {
387 return;
388 }
389
390 // Get the set of selected hosts and their intents.
391 ArrayNode ids = (ArrayNode) payload.path("ids");
392 selectedHosts = getHosts(ids);
393 selectedDevices = getDevices(ids);
394 selectedIntents = intentFilter.findPathIntents(
395 selectedHosts, selectedDevices, intentService.getIntents());
396 currentIntentIndex = -1;
397
398 if (haveSelectedIntents()) {
399 // Send a message to highlight all links of all monitored intents.
400 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
401 }
402
403 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
404// String hover = string(payload, "hover");
405// if (!isNullOrEmpty(hover)) {
406// // If there is a hover node, include it in the selection and find intents.
407// processHoverExtendedSelection(sid, hover);
408// }
409 }
410 }
411
412 private final class ReqNextIntent extends RequestHandler {
413 private ReqNextIntent() {
414 super(REQ_NEXT_INTENT);
415 }
416
417 @Override
418 public void process(long sid, ObjectNode payload) {
419 stopTrafficMonitoring();
420 requestAnotherRelatedIntent(+1);
421 }
422 }
423
424 private final class ReqPrevIntent extends RequestHandler {
425 private ReqPrevIntent() {
426 super(REQ_PREV_INTENT);
427 }
428
429 @Override
430 public void process(long sid, ObjectNode payload) {
431 stopTrafficMonitoring();
432 requestAnotherRelatedIntent(-1);
433 }
434 }
435
436 private final class ReqSelectedIntentTraffic extends RequestHandler {
437 private ReqSelectedIntentTraffic() {
438 super(REQ_SEL_INTENT_TRAFFIC);
439 }
440
441 @Override
442 public void process(long sid, ObjectNode payload) {
443 trafficEvent =
444 new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
445 requestSelectedIntentTraffic();
446 startTrafficMonitoring();
447 }
448 }
449
450 private final class ReqAllTraffic extends RequestHandler {
451 private ReqAllTraffic() {
452 super(REQ_ALL_TRAFFIC);
453 }
454
455 @Override
456 public void process(long sid, ObjectNode payload) {
457 trafficEvent =
458 new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
459 requestAllTraffic();
460 }
461 }
462
463 private final class ReqDevLinkFlows extends RequestHandler {
464 private ReqDevLinkFlows() {
465 super(REQ_DEV_LINK_FLOWS);
466 }
467
468 @Override
469 public void process(long sid, ObjectNode payload) {
470 trafficEvent =
471 new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
472 requestDeviceLinkFlows(payload);
473 }
474 }
475
476 private final class CancelTraffic extends RequestHandler {
477 private CancelTraffic() {
478 super(CANCEL_TRAFFIC);
479 }
480
481 @Override
482 public void process(long sid, ObjectNode payload) {
483 selectedIntents = null;
484 sendMessage(trafficMessage());
485 stopTrafficMonitoring();
486 }
487 }
488
489 //=======================================================================
490
491
Thomas Vachuska329af532015-03-10 02:08:33 -0700492 // Sends the specified data to the client.
493 protected synchronized void sendMessage(ObjectNode data) {
494 UiConnection connection = connection();
495 if (connection != null) {
496 connection.sendMessage(data);
497 }
498 }
499
Simon Huntd2747a02015-04-30 22:41:16 -0700500 // Subscribes for summary messages.
501 private synchronized void requestSummary(long sid) {
502 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700503 }
504
Simon Huntd2747a02015-04-30 22:41:16 -0700505
Thomas Vachuska329af532015-03-10 02:08:33 -0700506 private void cancelAllRequests() {
507 stopSummaryMonitoring();
508 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700509 }
510
511 // Sends all controller nodes to the client as node-added messages.
512 private void sendAllInstances(String messageType) {
513 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
514 Collections.sort(nodes, NODE_COMPARATOR);
515 for (ControllerNode node : nodes) {
516 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
517 messageType));
518 }
519 }
520
521 // Sends all devices to the client as device-added messages.
522 private void sendAllDevices() {
523 // Send optical first, others later for layered rendering
524 for (Device device : deviceService.getDevices()) {
525 if (device.type() == Device.Type.ROADM) {
526 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
527 }
528 }
529 for (Device device : deviceService.getDevices()) {
530 if (device.type() != Device.Type.ROADM) {
531 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
532 }
533 }
534 }
535
536 // Sends all links to the client as link-added messages.
537 private void sendAllLinks() {
538 // Send optical first, others later for layered rendering
539 for (Link link : linkService.getLinks()) {
540 if (link.type() == Link.Type.OPTICAL) {
541 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
542 }
543 }
544 for (Link link : linkService.getLinks()) {
545 if (link.type() != Link.Type.OPTICAL) {
546 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
547 }
548 }
549 }
550
551 // Sends all hosts to the client as host-added messages.
552 private void sendAllHosts() {
553 for (Host host : hostService.getHosts()) {
554 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
555 }
556 }
557
Thomas Vachuska329af532015-03-10 02:08:33 -0700558
Simon Huntd2747a02015-04-30 22:41:16 -0700559 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700560 selectedHosts = new HashSet<>();
561 selectedDevices = new HashSet<>();
562 selectedIntents = new ArrayList<>();
563 selectedIntents.add(intent);
564 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700565 requestAnotherRelatedIntent(+1);
566 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700567 }
568
569
570 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
571 Set<ConnectPoint> points = new HashSet<>();
572 for (HostId hostId : hostIds) {
573 points.add(getHostLocation(hostId));
574 }
575 return points;
576 }
577
578 private HostLocation getHostLocation(HostId hostId) {
579 return hostService.getHost(hostId).location();
580 }
581
582 // Produces a list of host ids from the specified JSON array.
583 private Set<HostId> getHostIds(ArrayNode ids) {
584 Set<HostId> hostIds = new HashSet<>();
585 for (JsonNode id : ids) {
586 hostIds.add(hostId(id.asText()));
587 }
588 return hostIds;
589 }
590
591
Simon Huntd2747a02015-04-30 22:41:16 -0700592 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700593 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700594 trafficTask = new TrafficMonitor();
595 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700596 }
597
598 private synchronized void stopTrafficMonitoring() {
599 if (trafficTask != null) {
600 trafficTask.cancel();
601 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700602 }
603 }
604
605 // Subscribes for host traffic messages.
Simon Huntd2747a02015-04-30 22:41:16 -0700606 private synchronized void requestAllTraffic() {
607 startTrafficMonitoring();
608 sendMessage(trafficSummaryMessage());
Thomas Vachuska329af532015-03-10 02:08:33 -0700609 }
610
Simon Huntd2747a02015-04-30 22:41:16 -0700611 private void requestDeviceLinkFlows(ObjectNode payload) {
612 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700613
614 // Get the set of selected hosts and their intents.
615 ArrayNode ids = (ArrayNode) payload.path("ids");
616 Set<Host> hosts = new HashSet<>();
617 Set<Device> devices = getDevices(ids);
618
619 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700620 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700621 if (!isNullOrEmpty(hover)) {
622 addHover(hosts, devices, hover);
623 }
Simon Huntd2747a02015-04-30 22:41:16 -0700624 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700625 }
626
627
Thomas Vachuska329af532015-03-10 02:08:33 -0700628 private boolean haveSelectedIntents() {
629 return selectedIntents != null && !selectedIntents.isEmpty();
630 }
631
632 // Processes the selection extended with hovered item to segregate items
633 // into primary (those including the hover) vs secondary highlights.
634 private void processHoverExtendedSelection(long sid, String hover) {
635 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
636 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
637 addHover(hoverSelHosts, hoverSelDevices, hover);
638
639 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
640 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
641 selectedIntents);
642 Set<Intent> secondary = new HashSet<>(selectedIntents);
643 secondary.removeAll(primary);
644
645 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700646 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700647 new TrafficClass("secondary", secondary)));
648 }
649
650 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700651 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700652 if (haveSelectedIntents()) {
653 currentIntentIndex = currentIntentIndex + offset;
654 if (currentIntentIndex < 0) {
655 currentIntentIndex = selectedIntents.size() - 1;
656 } else if (currentIntentIndex >= selectedIntents.size()) {
657 currentIntentIndex = 0;
658 }
Simon Huntd2747a02015-04-30 22:41:16 -0700659 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700660 }
661 }
662
663 // Sends traffic information on the related intents with the currently
664 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700665 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700666 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
667 log.info("Requested next intent {}", selectedIntent.id());
668
669 Set<Intent> primary = new HashSet<>();
670 primary.add(selectedIntent);
671
672 Set<Intent> secondary = new HashSet<>(selectedIntents);
673 secondary.remove(selectedIntent);
674
675 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700676 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700677 new TrafficClass("secondary", secondary)));
678 }
679
680 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700681 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700682 if (haveSelectedIntents()) {
683 if (currentIntentIndex < 0) {
684 currentIntentIndex = 0;
685 }
686 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
687 log.info("Requested traffic for selected {}", selectedIntent.id());
688
689 Set<Intent> primary = new HashSet<>();
690 primary.add(selectedIntent);
691
692 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700693 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700694 }
695 }
696
Simon Huntd2747a02015-04-30 22:41:16 -0700697 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700698 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700699 summaryTask = new SummaryMonitor();
700 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700701 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700702 }
703
704 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700705 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700706 summaryTask.cancel();
707 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 }
Simon Huntd2747a02015-04-30 22:41:16 -0700709 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700710 }
711
Thomas Vachuska329af532015-03-10 02:08:33 -0700712
713 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700714 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700715 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 clusterService.addListener(clusterListener);
717 mastershipService.addListener(mastershipListener);
718 deviceService.addListener(deviceListener);
719 linkService.addListener(linkListener);
720 hostService.addListener(hostListener);
721 intentService.addListener(intentListener);
722 flowService.addListener(flowListener);
723 }
724
725 // Removes all internal listeners.
726 private synchronized void removeListeners() {
727 if (!listenersRemoved) {
728 listenersRemoved = true;
729 clusterService.removeListener(clusterListener);
730 mastershipService.removeListener(mastershipListener);
731 deviceService.removeListener(deviceListener);
732 linkService.removeListener(linkListener);
733 hostService.removeListener(hostListener);
734 intentService.removeListener(intentListener);
735 flowService.removeListener(flowListener);
736 }
737 }
738
739 // Cluster event listener.
740 private class InternalClusterListener implements ClusterEventListener {
741 @Override
742 public void event(ClusterEvent event) {
743 sendMessage(instanceMessage(event, null));
744 }
745 }
746
747 // Mastership change listener
748 private class InternalMastershipListener implements MastershipListener {
749 @Override
750 public void event(MastershipEvent event) {
751 sendAllInstances("updateInstance");
752 Device device = deviceService.getDevice(event.subject());
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700753 if (device != null) {
754 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
755 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700756 }
757 }
758
759 // Device event listener.
760 private class InternalDeviceListener implements DeviceListener {
761 @Override
762 public void event(DeviceEvent event) {
763 sendMessage(deviceMessage(event));
764 eventAccummulator.add(event);
765 }
766 }
767
768 // Link event listener.
769 private class InternalLinkListener implements LinkListener {
770 @Override
771 public void event(LinkEvent event) {
772 sendMessage(linkMessage(event));
773 eventAccummulator.add(event);
774 }
775 }
776
777 // Host event listener.
778 private class InternalHostListener implements HostListener {
779 @Override
780 public void event(HostEvent event) {
781 sendMessage(hostMessage(event));
782 eventAccummulator.add(event);
783 }
784 }
785
786 // Intent event listener.
787 private class InternalIntentListener implements IntentListener {
788 @Override
789 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700790 if (trafficTask != null) {
791 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700792 }
793 eventAccummulator.add(event);
794 }
795 }
796
797 // Intent event listener.
798 private class InternalFlowListener implements FlowRuleListener {
799 @Override
800 public void event(FlowRuleEvent event) {
801 eventAccummulator.add(event);
802 }
803 }
804
Simon Huntd2747a02015-04-30 22:41:16 -0700805 // encapsulate
806 private static class TrafficEvent {
807 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
808
809 private final Type type;
810 private final ObjectNode payload;
811
812 TrafficEvent(Type type, ObjectNode payload) {
813 this.type = type;
814 this.payload = payload;
815 }
816 }
817
Thomas Vachuska329af532015-03-10 02:08:33 -0700818 // Periodic update of the traffic information
819 private class TrafficMonitor extends TimerTask {
820 @Override
821 public void run() {
822 try {
823 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700824 switch (trafficEvent.type) {
825 case ALL_TRAFFIC:
826 requestAllTraffic();
827 break;
828 case DEV_LINK_FLOWS:
829 requestDeviceLinkFlows(trafficEvent.payload);
830 break;
831 case SEL_INTENT:
832 requestSelectedIntentTraffic();
833 break;
834 default:
835 // nothing to do
836 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700837 }
838 }
839 } catch (Exception e) {
840 log.warn("Unable to handle traffic request due to {}", e.getMessage());
841 log.warn("Boom!", e);
842 }
843 }
844 }
845
846 // Periodic update of the summary information
847 private class SummaryMonitor extends TimerTask {
848 @Override
849 public void run() {
850 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700851 if (summaryRunning) {
852 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700853 }
854 } catch (Exception e) {
855 log.warn("Unable to handle summary request due to {}", e.getMessage());
856 log.warn("Boom!", e);
857 }
858 }
859 }
860
861 // Accumulates events to drive methodic update of the summary pane.
862 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
863 protected InternalEventAccummulator() {
864 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
865 }
866
867 @Override
868 public void processItems(List<Event> items) {
869 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700870 if (summaryRunning) {
871 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700872 }
873 } catch (Exception e) {
874 log.warn("Unable to handle summary request due to {}", e.getMessage());
875 log.debug("Boom!", e);
876 }
877 }
878 }
879}