blob: 08a9e13606bbc9022f5ee5ea1113fe2a9d71f0c8 [file] [log] [blame]
Thomas Vachuska329af532015-03-10 02:08:33 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ui.impl;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import com.google.common.collect.ImmutableSet;
22import org.onlab.osgi.ServiceDirectory;
23import org.onlab.util.AbstractAccumulator;
24import org.onlab.util.Accumulator;
25import org.onosproject.cluster.ClusterEvent;
26import org.onosproject.cluster.ClusterEventListener;
27import org.onosproject.cluster.ControllerNode;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.event.Event;
31import org.onosproject.mastership.MastershipAdminService;
32import org.onosproject.mastership.MastershipEvent;
33import org.onosproject.mastership.MastershipListener;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.Device;
36import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
38import org.onosproject.net.HostLocation;
39import org.onosproject.net.Link;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowRuleEvent;
45import org.onosproject.net.flow.FlowRuleListener;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.host.HostEvent;
49import org.onosproject.net.host.HostListener;
50import org.onosproject.net.intent.HostToHostIntent;
51import org.onosproject.net.intent.Intent;
52import org.onosproject.net.intent.IntentEvent;
53import org.onosproject.net.intent.IntentListener;
54import org.onosproject.net.intent.MultiPointToSinglePointIntent;
55import org.onosproject.net.link.LinkEvent;
56import org.onosproject.net.link.LinkListener;
Simon Huntd2747a02015-04-30 22:41:16 -070057import org.onosproject.ui.JsonUtils;
58import org.onosproject.ui.RequestHandler;
Thomas Vachuska329af532015-03-10 02:08:33 -070059import org.onosproject.ui.UiConnection;
60
61import java.util.ArrayList;
Simon Huntd2747a02015-04-30 22:41:16 -070062import java.util.Collection;
Thomas Vachuska329af532015-03-10 02:08:33 -070063import java.util.Collections;
64import java.util.Comparator;
65import java.util.HashSet;
66import java.util.List;
67import java.util.Set;
68import java.util.Timer;
69import java.util.TimerTask;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070070import java.util.concurrent.ExecutorService;
Thomas Vachuska329af532015-03-10 02:08:33 -070071
72import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska52c98bd2015-05-27 20:54:02 -070073import static java.util.concurrent.Executors.newSingleThreadExecutor;
74import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska329af532015-03-10 02:08:33 -070075import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
76import static org.onosproject.net.DeviceId.deviceId;
77import static org.onosproject.net.HostId.hostId;
Thomas Vachuskacb5016f2015-05-18 14:11:43 -070078import static org.onosproject.net.device.DeviceEvent.Type.*;
Thomas Vachuska329af532015-03-10 02:08:33 -070079import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
80import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
81
82/**
83 * Web socket capable of interacting with the GUI topology view.
84 */
85public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
86
Simon Huntd2747a02015-04-30 22:41:16 -070087 private static final String REQ_DETAILS = "requestDetails";
88 private static final String UPDATE_META = "updateMeta";
89 private static final String ADD_HOST_INTENT = "addHostIntent";
90 private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
91 private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
92 private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
93 private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
94 private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
95 private static final String REQ_ALL_TRAFFIC = "requestAllTraffic";
96 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
97 private static final String CANCEL_TRAFFIC = "cancelTraffic";
98 private static final String REQ_SUMMARY = "requestSummary";
99 private static final String CANCEL_SUMMARY = "cancelSummary";
100 private static final String EQ_MASTERS = "equalizeMasters";
101 private static final String SPRITE_LIST_REQ = "spriteListRequest";
102 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
103 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700104 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd2747a02015-04-30 22:41:16 -0700105 private static final String TOPO_STOP = "topoStop";
106
107
Thomas Vachuska329af532015-03-10 02:08:33 -0700108 private static final String APP_ID = "org.onosproject.gui";
109
110 private static final long TRAFFIC_FREQUENCY = 5000;
111 private static final long SUMMARY_FREQUENCY = 30000;
112
113 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700114 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700115
116
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700117 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700118
119 private static final int MAX_EVENTS = 1000;
120 private static final int MAX_BATCH_MS = 5000;
121 private static final int MAX_IDLE_MS = 1000;
122
123 private ApplicationId appId;
124
125 private final ClusterEventListener clusterListener = new InternalClusterListener();
126 private final MastershipListener mastershipListener = new InternalMastershipListener();
127 private final DeviceListener deviceListener = new InternalDeviceListener();
128 private final LinkListener linkListener = new InternalLinkListener();
129 private final HostListener hostListener = new InternalHostListener();
130 private final IntentListener intentListener = new InternalIntentListener();
131 private final FlowRuleListener flowListener = new InternalFlowListener();
132
133 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700134 private final ExecutorService msgSender =
135 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700136
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) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700515
Simon Huntd2747a02015-04-30 22:41:16 -0700516 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) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700757 msgSender.execute(() -> {
758 sendMessage(instanceMessage(event, null));
759 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700760 }
761 }
762
763 // Mastership change listener
764 private class InternalMastershipListener implements MastershipListener {
765 @Override
766 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700767 msgSender.execute(() -> {
768 sendAllInstances("updateInstance");
769 Device device = deviceService.getDevice(event.subject());
770 if (device != null) {
771 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
772 }
773 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700774 }
775 }
776
777 // Device event listener.
778 private class InternalDeviceListener implements DeviceListener {
779 @Override
780 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700781 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700782 msgSender.execute(() -> {
783 sendMessage(deviceMessage(event));
784 });
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700785 eventAccummulator.add(event);
786 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700787 }
788 }
789
790 // Link event listener.
791 private class InternalLinkListener implements LinkListener {
792 @Override
793 public void event(LinkEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700794 msgSender.execute(() -> {
795 sendMessage(linkMessage(event));
796 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700797 eventAccummulator.add(event);
798 }
799 }
800
801 // Host event listener.
802 private class InternalHostListener implements HostListener {
803 @Override
804 public void event(HostEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700805 msgSender.execute(() -> {
806 sendMessage(hostMessage(event));
807 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700808 eventAccummulator.add(event);
809 }
810 }
811
812 // Intent event listener.
813 private class InternalIntentListener implements IntentListener {
814 @Override
815 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700816 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700817 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700818 }
819 eventAccummulator.add(event);
820 }
821 }
822
823 // Intent event listener.
824 private class InternalFlowListener implements FlowRuleListener {
825 @Override
826 public void event(FlowRuleEvent event) {
827 eventAccummulator.add(event);
828 }
829 }
830
Simon Huntd2747a02015-04-30 22:41:16 -0700831 // encapsulate
832 private static class TrafficEvent {
833 enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
834
835 private final Type type;
836 private final ObjectNode payload;
837
838 TrafficEvent(Type type, ObjectNode payload) {
839 this.type = type;
840 this.payload = payload;
841 }
842 }
843
Thomas Vachuska329af532015-03-10 02:08:33 -0700844 // Periodic update of the traffic information
845 private class TrafficMonitor extends TimerTask {
846 @Override
847 public void run() {
848 try {
849 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700850 switch (trafficEvent.type) {
851 case ALL_TRAFFIC:
852 requestAllTraffic();
853 break;
854 case DEV_LINK_FLOWS:
855 requestDeviceLinkFlows(trafficEvent.payload);
856 break;
857 case SEL_INTENT:
858 requestSelectedIntentTraffic();
859 break;
860 default:
861 // nothing to do
862 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700863 }
864 }
865 } catch (Exception e) {
866 log.warn("Unable to handle traffic request due to {}", e.getMessage());
867 log.warn("Boom!", e);
868 }
869 }
870 }
871
872 // Periodic update of the summary information
873 private class SummaryMonitor extends TimerTask {
874 @Override
875 public void run() {
876 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700877 if (summaryRunning) {
878 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700879 }
880 } catch (Exception e) {
881 log.warn("Unable to handle summary request due to {}", e.getMessage());
882 log.warn("Boom!", e);
883 }
884 }
885 }
886
887 // Accumulates events to drive methodic update of the summary pane.
888 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
889 protected InternalEventAccummulator() {
890 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
891 }
892
893 @Override
894 public void processItems(List<Event> items) {
895 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700896 if (summaryRunning) {
897 requestSummary(0);
Thomas Vachuska329af532015-03-10 02:08:33 -0700898 }
899 } catch (Exception e) {
900 log.warn("Unable to handle summary request due to {}", e.getMessage());
901 log.debug("Boom!", e);
902 }
903 }
904 }
905}