blob: 211058a6af2142b71e2fc53c5b68a5cc84dd01f4 [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";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700102 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd2747a02015-04-30 22:41:16 -0700103 private static final String TOPO_STOP = "topoStop";
104
105
Thomas Vachuska329af532015-03-10 02:08:33 -0700106 private static final String APP_ID = "org.onosproject.gui";
107
108 private static final long TRAFFIC_FREQUENCY = 5000;
109 private static final long SUMMARY_FREQUENCY = 30000;
110
111 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
112 new Comparator<ControllerNode>() {
113 @Override
114 public int compare(ControllerNode o1, ControllerNode o2) {
115 return o1.id().toString().compareTo(o2.id().toString());
116 }
117 };
118
119
120 private final Timer timer = new Timer("topology-view");
121
122 private static final int MAX_EVENTS = 1000;
123 private static final int MAX_BATCH_MS = 5000;
124 private static final int MAX_IDLE_MS = 1000;
125
126 private ApplicationId appId;
127
128 private final ClusterEventListener clusterListener = new InternalClusterListener();
129 private final MastershipListener mastershipListener = new InternalMastershipListener();
130 private final DeviceListener deviceListener = new InternalDeviceListener();
131 private final LinkListener linkListener = new InternalLinkListener();
132 private final HostListener hostListener = new InternalHostListener();
133 private final IntentListener intentListener = new InternalIntentListener();
134 private final FlowRuleListener flowListener = new InternalFlowListener();
135
136 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
137
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(),
191 new ReqAllTraffic(),
192 new ReqDevLinkFlows(),
193 new CancelTraffic()
194 );
195 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700196
Simon Huntd2747a02015-04-30 22:41:16 -0700197 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700198
Simon Huntc54cd1b2015-05-11 13:43:44 -0700199 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700200 private final class TopoStart extends RequestHandler {
201 private TopoStart() {
202 super(TOPO_START);
203 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700204
Simon Huntd2747a02015-04-30 22:41:16 -0700205 @Override
206 public void process(long sid, ObjectNode payload) {
207 addListeners();
208 sendAllInstances(null);
209 sendAllDevices();
210 sendAllLinks();
211 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700212 }
213 }
214
Simon Huntc54cd1b2015-05-11 13:43:44 -0700215 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700216 private final class TopoHeartbeat extends RequestHandler {
217 private TopoHeartbeat() {
218 super(TOPO_HEARTBEAT);
219 }
220
221 @Override
222 public void process(long sid, ObjectNode payload) {
223 // place holder for now
224 }
225 }
226
227 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700228 private final class TopoStop extends RequestHandler {
229 private TopoStop() {
230 super(TOPO_STOP);
231 }
232
233 @Override
234 public void process(long sid, ObjectNode payload) {
235 stopSummaryMonitoring();
236 stopTrafficMonitoring();
237 }
238 }
239
Simon Huntc54cd1b2015-05-11 13:43:44 -0700240 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700241 private final class ReqSummary extends RequestHandler {
242 private ReqSummary() {
243 super(REQ_SUMMARY);
244 }
245
246 @Override
247 public void process(long sid, ObjectNode payload) {
248 requestSummary(sid);
249 startSummaryMonitoring();
250 }
251 }
252
253 private final class CancelSummary extends RequestHandler {
254 private CancelSummary() {
255 super(CANCEL_SUMMARY);
256 }
257
258 @Override
259 public void process(long sid, ObjectNode payload) {
260 stopSummaryMonitoring();
261 }
262 }
263
264 private final class SpriteListReq extends RequestHandler {
265 private SpriteListReq() {
266 super(SPRITE_LIST_REQ);
267 }
268
269 @Override
270 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700271 ObjectNode root = objectNode();
272 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700273 get(SpriteService.class).getNames().forEach(names::add);
274 root.set("names", names);
275 sendMessage("spriteListResponse", sid, root);
276 }
277 }
278
279 private final class SpriteDataReq extends RequestHandler {
280 private SpriteDataReq() {
281 super(SPRITE_DATA_REQ);
282 }
283
284 @Override
285 public void process(long sid, ObjectNode payload) {
286 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700287 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700288 root.set("data", get(SpriteService.class).get(name));
289 sendMessage("spriteDataResponse", sid, root);
290 }
291 }
292
293 private final class RequestDetails extends RequestHandler {
294 private RequestDetails() {
295 super(REQ_DETAILS);
296 }
297
298 @Override
299 public void process(long sid, ObjectNode payload) {
300 String type = string(payload, "class", "unknown");
301 String id = JsonUtils.string(payload, "id");
302
303 if (type.equals("device")) {
304 sendMessage(deviceDetails(deviceId(id), sid));
305 } else if (type.equals("host")) {
306 sendMessage(hostDetails(hostId(id), sid));
307 }
308 }
309 }
310
311 private final class UpdateMeta extends RequestHandler {
312 private UpdateMeta() {
313 super(UPDATE_META);
314 }
315
316 @Override
317 public void process(long sid, ObjectNode payload) {
318 updateMetaUi(payload);
319 }
320 }
321
322 private final class EqMasters extends RequestHandler {
323 private EqMasters() {
324 super(EQ_MASTERS);
325 }
326
327 @Override
328 public void process(long sid, ObjectNode payload) {
329 directory.get(MastershipAdminService.class).balanceRoles();
330 }
331 }
332
333 // === TODO: move traffic related classes to traffic app
334
335 private final class AddHostIntent extends RequestHandler {
336 private AddHostIntent() {
337 super(ADD_HOST_INTENT);
338 }
339
340 @Override
341 public void process(long sid, ObjectNode payload) {
342 // TODO: add protection against device ids and non-existent hosts.
343 HostId one = hostId(string(payload, "one"));
344 HostId two = hostId(string(payload, "two"));
345
346 HostToHostIntent intent = HostToHostIntent.builder()
347 .appId(appId)
348 .one(one)
349 .two(two)
350 .build();
351
352 intentService.submit(intent);
353 startMonitoringIntent(intent);
354 }
355 }
356
357 private final class AddMultiSourceIntent extends RequestHandler {
358 private AddMultiSourceIntent() {
359 super(ADD_MULTI_SRC_INTENT);
360 }
361
362 @Override
363 public void process(long sid, ObjectNode payload) {
364 // TODO: add protection against device ids and non-existent hosts.
365 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
366 HostId dst = hostId(string(payload, "dst"));
367 Host dstHost = hostService.getHost(dst);
368
369 Set<ConnectPoint> ingressPoints = getHostLocations(src);
370
371 // FIXME: clearly, this is not enough
372 TrafficSelector selector = DefaultTrafficSelector.builder()
373 .matchEthDst(dstHost.mac()).build();
374 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
375
376 MultiPointToSinglePointIntent intent =
377 MultiPointToSinglePointIntent.builder()
378 .appId(appId)
379 .selector(selector)
380 .treatment(treatment)
381 .ingressPoints(ingressPoints)
382 .egressPoint(dstHost.location())
383 .build();
384
385 intentService.submit(intent);
386 startMonitoringIntent(intent);
387 }
388 }
389
390 private final class ReqRelatedIntents extends RequestHandler {
391 private ReqRelatedIntents() {
392 super(REQ_RELATED_INTENTS);
393 }
394
395 @Override
396 public void process(long sid, ObjectNode payload) {
397 // Cancel any other traffic monitoring mode.
398 stopTrafficMonitoring();
399
400 if (!payload.has("ids")) {
401 return;
402 }
403
404 // Get the set of selected hosts and their intents.
405 ArrayNode ids = (ArrayNode) payload.path("ids");
406 selectedHosts = getHosts(ids);
407 selectedDevices = getDevices(ids);
408 selectedIntents = intentFilter.findPathIntents(
409 selectedHosts, selectedDevices, intentService.getIntents());
410 currentIntentIndex = -1;
411
412 if (haveSelectedIntents()) {
413 // Send a message to highlight all links of all monitored intents.
414 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
415 }
416
417 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
418// String hover = string(payload, "hover");
419// if (!isNullOrEmpty(hover)) {
420// // If there is a hover node, include it in the selection and find intents.
421// processHoverExtendedSelection(sid, hover);
422// }
423 }
424 }
425
426 private final class ReqNextIntent extends RequestHandler {
427 private ReqNextIntent() {
428 super(REQ_NEXT_INTENT);
429 }
430
431 @Override
432 public void process(long sid, ObjectNode payload) {
433 stopTrafficMonitoring();
434 requestAnotherRelatedIntent(+1);
435 }
436 }
437
438 private final class ReqPrevIntent extends RequestHandler {
439 private ReqPrevIntent() {
440 super(REQ_PREV_INTENT);
441 }
442
443 @Override
444 public void process(long sid, ObjectNode payload) {
445 stopTrafficMonitoring();
446 requestAnotherRelatedIntent(-1);
447 }
448 }
449
450 private final class ReqSelectedIntentTraffic extends RequestHandler {
451 private ReqSelectedIntentTraffic() {
452 super(REQ_SEL_INTENT_TRAFFIC);
453 }
454
455 @Override
456 public void process(long sid, ObjectNode payload) {
457 trafficEvent =
458 new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
459 requestSelectedIntentTraffic();
460 startTrafficMonitoring();
461 }
462 }
463
464 private final class ReqAllTraffic extends RequestHandler {
465 private ReqAllTraffic() {
466 super(REQ_ALL_TRAFFIC);
467 }
468
469 @Override
470 public void process(long sid, ObjectNode payload) {
471 trafficEvent =
472 new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
473 requestAllTraffic();
474 }
475 }
476
477 private final class ReqDevLinkFlows extends RequestHandler {
478 private ReqDevLinkFlows() {
479 super(REQ_DEV_LINK_FLOWS);
480 }
481
482 @Override
483 public void process(long sid, ObjectNode payload) {
484 trafficEvent =
485 new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
486 requestDeviceLinkFlows(payload);
487 }
488 }
489
490 private final class CancelTraffic extends RequestHandler {
491 private CancelTraffic() {
492 super(CANCEL_TRAFFIC);
493 }
494
495 @Override
496 public void process(long sid, ObjectNode payload) {
497 selectedIntents = null;
498 sendMessage(trafficMessage());
499 stopTrafficMonitoring();
500 }
501 }
502
503 //=======================================================================
504
505
Thomas Vachuska329af532015-03-10 02:08:33 -0700506 // Sends the specified data to the client.
507 protected synchronized void sendMessage(ObjectNode data) {
508 UiConnection connection = connection();
509 if (connection != null) {
510 connection.sendMessage(data);
511 }
512 }
513
Simon Huntd2747a02015-04-30 22:41:16 -0700514 // Subscribes for summary messages.
515 private synchronized void requestSummary(long sid) {
516 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700517 }
518
Simon Huntd2747a02015-04-30 22:41:16 -0700519
Thomas Vachuska329af532015-03-10 02:08:33 -0700520 private void cancelAllRequests() {
521 stopSummaryMonitoring();
522 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700523 }
524
525 // Sends all controller nodes to the client as node-added messages.
526 private void sendAllInstances(String messageType) {
527 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
528 Collections.sort(nodes, NODE_COMPARATOR);
529 for (ControllerNode node : nodes) {
530 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
531 messageType));
532 }
533 }
534
535 // Sends all devices to the client as device-added messages.
536 private void sendAllDevices() {
537 // Send optical first, others later for layered rendering
538 for (Device device : deviceService.getDevices()) {
539 if (device.type() == Device.Type.ROADM) {
540 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
541 }
542 }
543 for (Device device : deviceService.getDevices()) {
544 if (device.type() != Device.Type.ROADM) {
545 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
546 }
547 }
548 }
549
550 // Sends all links to the client as link-added messages.
551 private void sendAllLinks() {
552 // Send optical first, others later for layered rendering
553 for (Link link : linkService.getLinks()) {
554 if (link.type() == Link.Type.OPTICAL) {
555 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
556 }
557 }
558 for (Link link : linkService.getLinks()) {
559 if (link.type() != Link.Type.OPTICAL) {
560 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
561 }
562 }
563 }
564
565 // Sends all hosts to the client as host-added messages.
566 private void sendAllHosts() {
567 for (Host host : hostService.getHosts()) {
568 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
569 }
570 }
571
Thomas Vachuska329af532015-03-10 02:08:33 -0700572
Simon Huntd2747a02015-04-30 22:41:16 -0700573 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700574 selectedHosts = new HashSet<>();
575 selectedDevices = new HashSet<>();
576 selectedIntents = new ArrayList<>();
577 selectedIntents.add(intent);
578 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700579 requestAnotherRelatedIntent(+1);
580 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700581 }
582
583
584 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
585 Set<ConnectPoint> points = new HashSet<>();
586 for (HostId hostId : hostIds) {
587 points.add(getHostLocation(hostId));
588 }
589 return points;
590 }
591
592 private HostLocation getHostLocation(HostId hostId) {
593 return hostService.getHost(hostId).location();
594 }
595
596 // Produces a list of host ids from the specified JSON array.
597 private Set<HostId> getHostIds(ArrayNode ids) {
598 Set<HostId> hostIds = new HashSet<>();
599 for (JsonNode id : ids) {
600 hostIds.add(hostId(id.asText()));
601 }
602 return hostIds;
603 }
604
605
Simon Huntd2747a02015-04-30 22:41:16 -0700606 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700607 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700608 trafficTask = new TrafficMonitor();
609 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700610 }
611
612 private synchronized void stopTrafficMonitoring() {
613 if (trafficTask != null) {
614 trafficTask.cancel();
615 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700616 }
617 }
618
619 // Subscribes for host traffic messages.
Simon Huntd2747a02015-04-30 22:41:16 -0700620 private synchronized void requestAllTraffic() {
621 startTrafficMonitoring();
622 sendMessage(trafficSummaryMessage());
Thomas Vachuska329af532015-03-10 02:08:33 -0700623 }
624
Simon Huntd2747a02015-04-30 22:41:16 -0700625 private void requestDeviceLinkFlows(ObjectNode payload) {
626 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700627
628 // Get the set of selected hosts and their intents.
629 ArrayNode ids = (ArrayNode) payload.path("ids");
630 Set<Host> hosts = new HashSet<>();
631 Set<Device> devices = getDevices(ids);
632
633 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700634 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700635 if (!isNullOrEmpty(hover)) {
636 addHover(hosts, devices, hover);
637 }
Simon Huntd2747a02015-04-30 22:41:16 -0700638 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700639 }
640
641
Thomas Vachuska329af532015-03-10 02:08:33 -0700642 private boolean haveSelectedIntents() {
643 return selectedIntents != null && !selectedIntents.isEmpty();
644 }
645
646 // Processes the selection extended with hovered item to segregate items
647 // into primary (those including the hover) vs secondary highlights.
648 private void processHoverExtendedSelection(long sid, String hover) {
649 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
650 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
651 addHover(hoverSelHosts, hoverSelDevices, hover);
652
653 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
654 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
655 selectedIntents);
656 Set<Intent> secondary = new HashSet<>(selectedIntents);
657 secondary.removeAll(primary);
658
659 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700660 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700661 new TrafficClass("secondary", secondary)));
662 }
663
664 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700665 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700666 if (haveSelectedIntents()) {
667 currentIntentIndex = currentIntentIndex + offset;
668 if (currentIntentIndex < 0) {
669 currentIntentIndex = selectedIntents.size() - 1;
670 } else if (currentIntentIndex >= selectedIntents.size()) {
671 currentIntentIndex = 0;
672 }
Simon Huntd2747a02015-04-30 22:41:16 -0700673 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700674 }
675 }
676
677 // Sends traffic information on the related intents with the currently
678 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700679 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700680 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
681 log.info("Requested next intent {}", selectedIntent.id());
682
683 Set<Intent> primary = new HashSet<>();
684 primary.add(selectedIntent);
685
686 Set<Intent> secondary = new HashSet<>(selectedIntents);
687 secondary.remove(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),
Thomas Vachuska329af532015-03-10 02:08:33 -0700691 new TrafficClass("secondary", secondary)));
692 }
693
694 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700695 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700696 if (haveSelectedIntents()) {
697 if (currentIntentIndex < 0) {
698 currentIntentIndex = 0;
699 }
700 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
701 log.info("Requested traffic for selected {}", selectedIntent.id());
702
703 Set<Intent> primary = new HashSet<>();
704 primary.add(selectedIntent);
705
706 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700707 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700708 }
709 }
710
Simon Huntd2747a02015-04-30 22:41:16 -0700711 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700712 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700713 summaryTask = new SummaryMonitor();
714 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700715 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700716 }
717
718 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700719 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700720 summaryTask.cancel();
721 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700722 }
Simon Huntd2747a02015-04-30 22:41:16 -0700723 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700724 }
725
Thomas Vachuska329af532015-03-10 02:08:33 -0700726
727 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700728 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700729 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700730 clusterService.addListener(clusterListener);
731 mastershipService.addListener(mastershipListener);
732 deviceService.addListener(deviceListener);
733 linkService.addListener(linkListener);
734 hostService.addListener(hostListener);
735 intentService.addListener(intentListener);
736 flowService.addListener(flowListener);
737 }
738
739 // Removes all internal listeners.
740 private synchronized void removeListeners() {
741 if (!listenersRemoved) {
742 listenersRemoved = true;
743 clusterService.removeListener(clusterListener);
744 mastershipService.removeListener(mastershipListener);
745 deviceService.removeListener(deviceListener);
746 linkService.removeListener(linkListener);
747 hostService.removeListener(hostListener);
748 intentService.removeListener(intentListener);
749 flowService.removeListener(flowListener);
750 }
751 }
752
753 // Cluster event listener.
754 private class InternalClusterListener implements ClusterEventListener {
755 @Override
756 public void event(ClusterEvent event) {
757 sendMessage(instanceMessage(event, null));
758 }
759 }
760
761 // Mastership change listener
762 private class InternalMastershipListener implements MastershipListener {
763 @Override
764 public void event(MastershipEvent event) {
765 sendAllInstances("updateInstance");
766 Device device = deviceService.getDevice(event.subject());
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700767 if (device != null) {
768 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
769 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700770 }
771 }
772
773 // Device event listener.
774 private class InternalDeviceListener implements DeviceListener {
775 @Override
776 public void event(DeviceEvent event) {
777 sendMessage(deviceMessage(event));
778 eventAccummulator.add(event);
779 }
780 }
781
782 // Link event listener.
783 private class InternalLinkListener implements LinkListener {
784 @Override
785 public void event(LinkEvent event) {
786 sendMessage(linkMessage(event));
787 eventAccummulator.add(event);
788 }
789 }
790
791 // Host event listener.
792 private class InternalHostListener implements HostListener {
793 @Override
794 public void event(HostEvent event) {
795 sendMessage(hostMessage(event));
796 eventAccummulator.add(event);
797 }
798 }
799
800 // Intent event listener.
801 private class InternalIntentListener implements IntentListener {
802 @Override
803 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700804 if (trafficTask != null) {
805 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700806 }
807 eventAccummulator.add(event);
808 }
809 }
810
811 // Intent event listener.
812 private class InternalFlowListener implements FlowRuleListener {
813 @Override
814 public void event(FlowRuleEvent event) {
815 eventAccummulator.add(event);
816 }
817 }
818
Simon Huntd2747a02015-04-30 22:41:16 -0700819 // encapsulate
820 private static class TrafficEvent {
821 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
822
823 private final Type type;
824 private final ObjectNode payload;
825
826 TrafficEvent(Type type, ObjectNode payload) {
827 this.type = type;
828 this.payload = payload;
829 }
830 }
831
Thomas Vachuska329af532015-03-10 02:08:33 -0700832 // Periodic update of the traffic information
833 private class TrafficMonitor extends TimerTask {
834 @Override
835 public void run() {
836 try {
837 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700838 switch (trafficEvent.type) {
839 case ALL_TRAFFIC:
840 requestAllTraffic();
841 break;
842 case DEV_LINK_FLOWS:
843 requestDeviceLinkFlows(trafficEvent.payload);
844 break;
845 case SEL_INTENT:
846 requestSelectedIntentTraffic();
847 break;
848 default:
849 // nothing to do
850 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700851 }
852 }
853 } catch (Exception e) {
854 log.warn("Unable to handle traffic request due to {}", e.getMessage());
855 log.warn("Boom!", e);
856 }
857 }
858 }
859
860 // Periodic update of the summary information
861 private class SummaryMonitor extends TimerTask {
862 @Override
863 public void run() {
864 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700865 if (summaryRunning) {
866 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700867 }
868 } catch (Exception e) {
869 log.warn("Unable to handle summary request due to {}", e.getMessage());
870 log.warn("Boom!", e);
871 }
872 }
873 }
874
875 // Accumulates events to drive methodic update of the summary pane.
876 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
877 protected InternalEventAccummulator() {
878 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
879 }
880
881 @Override
882 public void processItems(List<Event> items) {
883 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700884 if (summaryRunning) {
885 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700886 }
887 } catch (Exception e) {
888 log.warn("Unable to handle summary request due to {}", e.getMessage());
889 log.debug("Boom!", e);
890 }
891 }
892 }
893}