blob: e3fa67431e9de2b655a432cd26d1280f69faf5b1 [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;
Thomas Vachuskacb5016f2015-05-18 14:11:43 -070075import static org.onosproject.net.device.DeviceEvent.Type.*;
Thomas Vachuska329af532015-03-10 02:08:33 -070076import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
77import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
78
79/**
80 * Web socket capable of interacting with the GUI topology view.
81 */
82public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
83
Simon Huntd2747a02015-04-30 22:41:16 -070084 private static final String REQ_DETAILS = "requestDetails";
85 private static final String UPDATE_META = "updateMeta";
86 private static final String ADD_HOST_INTENT = "addHostIntent";
87 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
88 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
89 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
90 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
91 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
92 private static final String REQ_ALL_TRAFFIC = "requestAllTraffic";
93 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
94 private static final String CANCEL_TRAFFIC = "cancelTraffic";
95 private static final String REQ_SUMMARY = "requestSummary";
96 private static final String CANCEL_SUMMARY = "cancelSummary";
97 private static final String EQ_MASTERS = "equalizeMasters";
98 private static final String SPRITE_LIST_REQ = "spriteListRequest";
99 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
100 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700101 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd2747a02015-04-30 22:41:16 -0700102 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(),
Simon Hunt732bb2e2015-05-13 18:32:16 -0700173 new TopoHeartbeat(),
Simon Huntd2747a02015-04-30 22:41:16 -0700174 new TopoStop(),
175 new ReqSummary(),
176 new CancelSummary(),
177 new SpriteListReq(),
178 new SpriteDataReq(),
179 new RequestDetails(),
180 new UpdateMeta(),
181 new EqMasters(),
Thomas Vachuska329af532015-03-10 02:08:33 -0700182
Simon Huntd2747a02015-04-30 22:41:16 -0700183 // TODO: migrate traffic related to separate app
184 new AddHostIntent(),
185 new AddMultiSourceIntent(),
186 new ReqRelatedIntents(),
187 new ReqNextIntent(),
188 new ReqPrevIntent(),
189 new ReqSelectedIntentTraffic(),
190 new ReqAllTraffic(),
191 new ReqDevLinkFlows(),
192 new CancelTraffic()
193 );
194 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700195
Simon Huntd2747a02015-04-30 22:41:16 -0700196 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700197
Simon Huntc54cd1b2015-05-11 13:43:44 -0700198 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700199 private final class TopoStart extends RequestHandler {
200 private TopoStart() {
201 super(TOPO_START);
202 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700203
Simon Huntd2747a02015-04-30 22:41:16 -0700204 @Override
205 public void process(long sid, ObjectNode payload) {
206 addListeners();
207 sendAllInstances(null);
208 sendAllDevices();
209 sendAllLinks();
210 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700211 }
212 }
213
Simon Huntc54cd1b2015-05-11 13:43:44 -0700214 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700215 private final class TopoHeartbeat extends RequestHandler {
216 private TopoHeartbeat() {
217 super(TOPO_HEARTBEAT);
218 }
219
220 @Override
221 public void process(long sid, ObjectNode payload) {
222 // place holder for now
223 }
224 }
225
226 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700227 private final class TopoStop extends RequestHandler {
228 private TopoStop() {
229 super(TOPO_STOP);
230 }
231
232 @Override
233 public void process(long sid, ObjectNode payload) {
234 stopSummaryMonitoring();
235 stopTrafficMonitoring();
236 }
237 }
238
Simon Huntc54cd1b2015-05-11 13:43:44 -0700239 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700240 private final class ReqSummary extends RequestHandler {
241 private ReqSummary() {
242 super(REQ_SUMMARY);
243 }
244
245 @Override
246 public void process(long sid, ObjectNode payload) {
247 requestSummary(sid);
248 startSummaryMonitoring();
249 }
250 }
251
252 private final class CancelSummary extends RequestHandler {
253 private CancelSummary() {
254 super(CANCEL_SUMMARY);
255 }
256
257 @Override
258 public void process(long sid, ObjectNode payload) {
259 stopSummaryMonitoring();
260 }
261 }
262
263 private final class SpriteListReq extends RequestHandler {
264 private SpriteListReq() {
265 super(SPRITE_LIST_REQ);
266 }
267
268 @Override
269 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700270 ObjectNode root = objectNode();
271 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700272 get(SpriteService.class).getNames().forEach(names::add);
273 root.set("names", names);
274 sendMessage("spriteListResponse", sid, root);
275 }
276 }
277
278 private final class SpriteDataReq extends RequestHandler {
279 private SpriteDataReq() {
280 super(SPRITE_DATA_REQ);
281 }
282
283 @Override
284 public void process(long sid, ObjectNode payload) {
285 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700286 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700287 root.set("data", get(SpriteService.class).get(name));
288 sendMessage("spriteDataResponse", sid, root);
289 }
290 }
291
292 private final class RequestDetails extends RequestHandler {
293 private RequestDetails() {
294 super(REQ_DETAILS);
295 }
296
297 @Override
298 public void process(long sid, ObjectNode payload) {
299 String type = string(payload, "class", "unknown");
300 String id = JsonUtils.string(payload, "id");
301
302 if (type.equals("device")) {
303 sendMessage(deviceDetails(deviceId(id), sid));
304 } else if (type.equals("host")) {
305 sendMessage(hostDetails(hostId(id), sid));
306 }
307 }
308 }
309
310 private final class UpdateMeta extends RequestHandler {
311 private UpdateMeta() {
312 super(UPDATE_META);
313 }
314
315 @Override
316 public void process(long sid, ObjectNode payload) {
317 updateMetaUi(payload);
318 }
319 }
320
321 private final class EqMasters extends RequestHandler {
322 private EqMasters() {
323 super(EQ_MASTERS);
324 }
325
326 @Override
327 public void process(long sid, ObjectNode payload) {
328 directory.get(MastershipAdminService.class).balanceRoles();
329 }
330 }
331
332 // === TODO: move traffic related classes to traffic app
333
334 private final class AddHostIntent extends RequestHandler {
335 private AddHostIntent() {
336 super(ADD_HOST_INTENT);
337 }
338
339 @Override
340 public void process(long sid, ObjectNode payload) {
341 // TODO: add protection against device ids and non-existent hosts.
342 HostId one = hostId(string(payload, "one"));
343 HostId two = hostId(string(payload, "two"));
344
345 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700346 .appId(appId)
347 .one(one)
348 .two(two)
349 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700350
351 intentService.submit(intent);
352 startMonitoringIntent(intent);
353 }
354 }
355
356 private final class AddMultiSourceIntent extends RequestHandler {
357 private AddMultiSourceIntent() {
358 super(ADD_MULTI_SRC_INTENT);
359 }
360
361 @Override
362 public void process(long sid, ObjectNode payload) {
363 // TODO: add protection against device ids and non-existent hosts.
364 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
365 HostId dst = hostId(string(payload, "dst"));
366 Host dstHost = hostService.getHost(dst);
367
368 Set<ConnectPoint> ingressPoints = getHostLocations(src);
369
370 // FIXME: clearly, this is not enough
371 TrafficSelector selector = DefaultTrafficSelector.builder()
372 .matchEthDst(dstHost.mac()).build();
373 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
374
375 MultiPointToSinglePointIntent intent =
376 MultiPointToSinglePointIntent.builder()
377 .appId(appId)
378 .selector(selector)
379 .treatment(treatment)
380 .ingressPoints(ingressPoints)
381 .egressPoint(dstHost.location())
382 .build();
383
384 intentService.submit(intent);
385 startMonitoringIntent(intent);
386 }
387 }
388
389 private final class ReqRelatedIntents extends RequestHandler {
390 private ReqRelatedIntents() {
391 super(REQ_RELATED_INTENTS);
392 }
393
394 @Override
395 public void process(long sid, ObjectNode payload) {
396 // Cancel any other traffic monitoring mode.
397 stopTrafficMonitoring();
398
399 if (!payload.has("ids")) {
400 return;
401 }
402
403 // Get the set of selected hosts and their intents.
404 ArrayNode ids = (ArrayNode) payload.path("ids");
405 selectedHosts = getHosts(ids);
406 selectedDevices = getDevices(ids);
407 selectedIntents = intentFilter.findPathIntents(
408 selectedHosts, selectedDevices, intentService.getIntents());
409 currentIntentIndex = -1;
410
411 if (haveSelectedIntents()) {
412 // Send a message to highlight all links of all monitored intents.
413 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
414 }
415
416 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
417// String hover = string(payload, "hover");
418// if (!isNullOrEmpty(hover)) {
419// // If there is a hover node, include it in the selection and find intents.
420// processHoverExtendedSelection(sid, hover);
421// }
422 }
423 }
424
425 private final class ReqNextIntent extends RequestHandler {
426 private ReqNextIntent() {
427 super(REQ_NEXT_INTENT);
428 }
429
430 @Override
431 public void process(long sid, ObjectNode payload) {
432 stopTrafficMonitoring();
433 requestAnotherRelatedIntent(+1);
434 }
435 }
436
437 private final class ReqPrevIntent extends RequestHandler {
438 private ReqPrevIntent() {
439 super(REQ_PREV_INTENT);
440 }
441
442 @Override
443 public void process(long sid, ObjectNode payload) {
444 stopTrafficMonitoring();
445 requestAnotherRelatedIntent(-1);
446 }
447 }
448
449 private final class ReqSelectedIntentTraffic extends RequestHandler {
450 private ReqSelectedIntentTraffic() {
451 super(REQ_SEL_INTENT_TRAFFIC);
452 }
453
454 @Override
455 public void process(long sid, ObjectNode payload) {
456 trafficEvent =
457 new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
458 requestSelectedIntentTraffic();
459 startTrafficMonitoring();
460 }
461 }
462
463 private final class ReqAllTraffic extends RequestHandler {
464 private ReqAllTraffic() {
465 super(REQ_ALL_TRAFFIC);
466 }
467
468 @Override
469 public void process(long sid, ObjectNode payload) {
470 trafficEvent =
471 new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
472 requestAllTraffic();
473 }
474 }
475
476 private final class ReqDevLinkFlows extends RequestHandler {
477 private ReqDevLinkFlows() {
478 super(REQ_DEV_LINK_FLOWS);
479 }
480
481 @Override
482 public void process(long sid, ObjectNode payload) {
483 trafficEvent =
484 new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
485 requestDeviceLinkFlows(payload);
486 }
487 }
488
489 private final class CancelTraffic extends RequestHandler {
490 private CancelTraffic() {
491 super(CANCEL_TRAFFIC);
492 }
493
494 @Override
495 public void process(long sid, ObjectNode payload) {
496 selectedIntents = null;
497 sendMessage(trafficMessage());
498 stopTrafficMonitoring();
499 }
500 }
501
502 //=======================================================================
503
504
Thomas Vachuska329af532015-03-10 02:08:33 -0700505 // Sends the specified data to the client.
506 protected synchronized void sendMessage(ObjectNode data) {
507 UiConnection connection = connection();
508 if (connection != null) {
509 connection.sendMessage(data);
510 }
511 }
512
Simon Huntd2747a02015-04-30 22:41:16 -0700513 // Subscribes for summary messages.
514 private synchronized void requestSummary(long sid) {
515 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700516 }
517
Simon Huntd2747a02015-04-30 22:41:16 -0700518
Thomas Vachuska329af532015-03-10 02:08:33 -0700519 private void cancelAllRequests() {
520 stopSummaryMonitoring();
521 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700522 }
523
524 // Sends all controller nodes to the client as node-added messages.
525 private void sendAllInstances(String messageType) {
526 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
527 Collections.sort(nodes, NODE_COMPARATOR);
528 for (ControllerNode node : nodes) {
529 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
530 messageType));
531 }
532 }
533
534 // Sends all devices to the client as device-added messages.
535 private void sendAllDevices() {
536 // Send optical first, others later for layered rendering
537 for (Device device : deviceService.getDevices()) {
538 if (device.type() == Device.Type.ROADM) {
539 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
540 }
541 }
542 for (Device device : deviceService.getDevices()) {
543 if (device.type() != Device.Type.ROADM) {
544 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
545 }
546 }
547 }
548
549 // Sends all links to the client as link-added messages.
550 private void sendAllLinks() {
551 // Send optical first, others later for layered rendering
552 for (Link link : linkService.getLinks()) {
553 if (link.type() == Link.Type.OPTICAL) {
554 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
555 }
556 }
557 for (Link link : linkService.getLinks()) {
558 if (link.type() != Link.Type.OPTICAL) {
559 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
560 }
561 }
562 }
563
564 // Sends all hosts to the client as host-added messages.
565 private void sendAllHosts() {
566 for (Host host : hostService.getHosts()) {
567 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
568 }
569 }
570
Thomas Vachuska329af532015-03-10 02:08:33 -0700571
Simon Huntd2747a02015-04-30 22:41:16 -0700572 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700573 selectedHosts = new HashSet<>();
574 selectedDevices = new HashSet<>();
575 selectedIntents = new ArrayList<>();
576 selectedIntents.add(intent);
577 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700578 requestAnotherRelatedIntent(+1);
579 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700580 }
581
582
583 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
584 Set<ConnectPoint> points = new HashSet<>();
585 for (HostId hostId : hostIds) {
586 points.add(getHostLocation(hostId));
587 }
588 return points;
589 }
590
591 private HostLocation getHostLocation(HostId hostId) {
592 return hostService.getHost(hostId).location();
593 }
594
595 // Produces a list of host ids from the specified JSON array.
596 private Set<HostId> getHostIds(ArrayNode ids) {
597 Set<HostId> hostIds = new HashSet<>();
598 for (JsonNode id : ids) {
599 hostIds.add(hostId(id.asText()));
600 }
601 return hostIds;
602 }
603
604
Simon Huntd2747a02015-04-30 22:41:16 -0700605 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700606 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700607 trafficTask = new TrafficMonitor();
608 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700609 }
610
611 private synchronized void stopTrafficMonitoring() {
612 if (trafficTask != null) {
613 trafficTask.cancel();
614 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700615 }
616 }
617
618 // Subscribes for host traffic messages.
Simon Huntd2747a02015-04-30 22:41:16 -0700619 private synchronized void requestAllTraffic() {
620 startTrafficMonitoring();
621 sendMessage(trafficSummaryMessage());
Thomas Vachuska329af532015-03-10 02:08:33 -0700622 }
623
Simon Huntd2747a02015-04-30 22:41:16 -0700624 private void requestDeviceLinkFlows(ObjectNode payload) {
625 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700626
627 // Get the set of selected hosts and their intents.
628 ArrayNode ids = (ArrayNode) payload.path("ids");
629 Set<Host> hosts = new HashSet<>();
630 Set<Device> devices = getDevices(ids);
631
632 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700633 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700634 if (!isNullOrEmpty(hover)) {
635 addHover(hosts, devices, hover);
636 }
Simon Huntd2747a02015-04-30 22:41:16 -0700637 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700638 }
639
640
Thomas Vachuska329af532015-03-10 02:08:33 -0700641 private boolean haveSelectedIntents() {
642 return selectedIntents != null && !selectedIntents.isEmpty();
643 }
644
645 // Processes the selection extended with hovered item to segregate items
646 // into primary (those including the hover) vs secondary highlights.
647 private void processHoverExtendedSelection(long sid, String hover) {
648 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
649 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
650 addHover(hoverSelHosts, hoverSelDevices, hover);
651
652 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
653 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
654 selectedIntents);
655 Set<Intent> secondary = new HashSet<>(selectedIntents);
656 secondary.removeAll(primary);
657
658 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700659 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700660 new TrafficClass("secondary", secondary)));
661 }
662
663 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700664 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700665 if (haveSelectedIntents()) {
666 currentIntentIndex = currentIntentIndex + offset;
667 if (currentIntentIndex < 0) {
668 currentIntentIndex = selectedIntents.size() - 1;
669 } else if (currentIntentIndex >= selectedIntents.size()) {
670 currentIntentIndex = 0;
671 }
Simon Huntd2747a02015-04-30 22:41:16 -0700672 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700673 }
674 }
675
676 // Sends traffic information on the related intents with the currently
677 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700678 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700679 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
680 log.info("Requested next intent {}", selectedIntent.id());
681
682 Set<Intent> primary = new HashSet<>();
683 primary.add(selectedIntent);
684
685 Set<Intent> secondary = new HashSet<>(selectedIntents);
686 secondary.remove(selectedIntent);
687
688 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700689 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700690 new TrafficClass("secondary", secondary)));
691 }
692
693 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700694 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700695 if (haveSelectedIntents()) {
696 if (currentIntentIndex < 0) {
697 currentIntentIndex = 0;
698 }
699 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
700 log.info("Requested traffic for selected {}", selectedIntent.id());
701
702 Set<Intent> primary = new HashSet<>();
703 primary.add(selectedIntent);
704
705 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700706 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700707 }
708 }
709
Simon Huntd2747a02015-04-30 22:41:16 -0700710 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700711 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700712 summaryTask = new SummaryMonitor();
713 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700714 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700715 }
716
717 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700718 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700719 summaryTask.cancel();
720 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700721 }
Simon Huntd2747a02015-04-30 22:41:16 -0700722 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700723 }
724
Thomas Vachuska329af532015-03-10 02:08:33 -0700725
726 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700727 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700728 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700729 clusterService.addListener(clusterListener);
730 mastershipService.addListener(mastershipListener);
731 deviceService.addListener(deviceListener);
732 linkService.addListener(linkListener);
733 hostService.addListener(hostListener);
734 intentService.addListener(intentListener);
735 flowService.addListener(flowListener);
736 }
737
738 // Removes all internal listeners.
739 private synchronized void removeListeners() {
740 if (!listenersRemoved) {
741 listenersRemoved = true;
742 clusterService.removeListener(clusterListener);
743 mastershipService.removeListener(mastershipListener);
744 deviceService.removeListener(deviceListener);
745 linkService.removeListener(linkListener);
746 hostService.removeListener(hostListener);
747 intentService.removeListener(intentListener);
748 flowService.removeListener(flowListener);
749 }
750 }
751
752 // Cluster event listener.
753 private class InternalClusterListener implements ClusterEventListener {
754 @Override
755 public void event(ClusterEvent event) {
756 sendMessage(instanceMessage(event, null));
757 }
758 }
759
760 // Mastership change listener
761 private class InternalMastershipListener implements MastershipListener {
762 @Override
763 public void event(MastershipEvent event) {
764 sendAllInstances("updateInstance");
765 Device device = deviceService.getDevice(event.subject());
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700766 if (device != null) {
767 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
768 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700769 }
770 }
771
772 // Device event listener.
773 private class InternalDeviceListener implements DeviceListener {
774 @Override
775 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700776 if (event.type() != PORT_STATS_UPDATED) {
777 sendMessage(deviceMessage(event));
778 eventAccummulator.add(event);
779 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700780 }
781 }
782
783 // Link event listener.
784 private class InternalLinkListener implements LinkListener {
785 @Override
786 public void event(LinkEvent event) {
787 sendMessage(linkMessage(event));
788 eventAccummulator.add(event);
789 }
790 }
791
792 // Host event listener.
793 private class InternalHostListener implements HostListener {
794 @Override
795 public void event(HostEvent event) {
796 sendMessage(hostMessage(event));
797 eventAccummulator.add(event);
798 }
799 }
800
801 // Intent event listener.
802 private class InternalIntentListener implements IntentListener {
803 @Override
804 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700805 if (trafficTask != null) {
806 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700807 }
808 eventAccummulator.add(event);
809 }
810 }
811
812 // Intent event listener.
813 private class InternalFlowListener implements FlowRuleListener {
814 @Override
815 public void event(FlowRuleEvent event) {
816 eventAccummulator.add(event);
817 }
818 }
819
Simon Huntd2747a02015-04-30 22:41:16 -0700820 // encapsulate
821 private static class TrafficEvent {
822 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
823
824 private final Type type;
825 private final ObjectNode payload;
826
827 TrafficEvent(Type type, ObjectNode payload) {
828 this.type = type;
829 this.payload = payload;
830 }
831 }
832
Thomas Vachuska329af532015-03-10 02:08:33 -0700833 // Periodic update of the traffic information
834 private class TrafficMonitor extends TimerTask {
835 @Override
836 public void run() {
837 try {
838 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700839 switch (trafficEvent.type) {
840 case ALL_TRAFFIC:
841 requestAllTraffic();
842 break;
843 case DEV_LINK_FLOWS:
844 requestDeviceLinkFlows(trafficEvent.payload);
845 break;
846 case SEL_INTENT:
847 requestSelectedIntentTraffic();
848 break;
849 default:
850 // nothing to do
851 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700852 }
853 }
854 } catch (Exception e) {
855 log.warn("Unable to handle traffic request due to {}", e.getMessage());
856 log.warn("Boom!", e);
857 }
858 }
859 }
860
861 // Periodic update of the summary information
862 private class SummaryMonitor extends TimerTask {
863 @Override
864 public void run() {
865 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700866 if (summaryRunning) {
867 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700868 }
869 } catch (Exception e) {
870 log.warn("Unable to handle summary request due to {}", e.getMessage());
871 log.warn("Boom!", e);
872 }
873 }
874 }
875
876 // Accumulates events to drive methodic update of the summary pane.
877 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
878 protected InternalEventAccummulator() {
879 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
880 }
881
882 @Override
883 public void processItems(List<Event> items) {
884 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700885 if (summaryRunning) {
886 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700887 }
888 } catch (Exception e) {
889 log.warn("Unable to handle summary request due to {}", e.getMessage());
890 log.debug("Boom!", e);
891 }
892 }
893 }
894}