blob: ec70531e30ce60a1b6f108f19b54c158555145e6 [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";
Thomas Vachuskaf0397b52015-05-29 13:50:17 -070095 private static final String REQ_ALL_FLOW_TRAFFIC = "requestAllFlowTraffic";
96 private static final String REQ_ALL_PORT_TRAFFIC = "requestAllPortTraffic";
Simon Huntd2747a02015-04-30 22:41:16 -070097 private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
98 private static final String CANCEL_TRAFFIC = "cancelTraffic";
99 private static final String REQ_SUMMARY = "requestSummary";
100 private static final String CANCEL_SUMMARY = "cancelSummary";
101 private static final String EQ_MASTERS = "equalizeMasters";
102 private static final String SPRITE_LIST_REQ = "spriteListRequest";
103 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
104 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -0700105 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd2747a02015-04-30 22:41:16 -0700106 private static final String TOPO_STOP = "topoStop";
107
108
Thomas Vachuska329af532015-03-10 02:08:33 -0700109 private static final String APP_ID = "org.onosproject.gui";
110
111 private static final long TRAFFIC_FREQUENCY = 5000;
112 private static final long SUMMARY_FREQUENCY = 30000;
113
114 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
Thomas Vachuskac0fe09a2015-05-21 12:56:22 -0700115 (o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
Thomas Vachuska329af532015-03-10 02:08:33 -0700116
117
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700118 private final Timer timer = new Timer("onos-topology-view");
Thomas Vachuska329af532015-03-10 02:08:33 -0700119
120 private static final int MAX_EVENTS = 1000;
121 private static final int MAX_BATCH_MS = 5000;
122 private static final int MAX_IDLE_MS = 1000;
123
124 private ApplicationId appId;
125
126 private final ClusterEventListener clusterListener = new InternalClusterListener();
127 private final MastershipListener mastershipListener = new InternalMastershipListener();
128 private final DeviceListener deviceListener = new InternalDeviceListener();
129 private final LinkListener linkListener = new InternalLinkListener();
130 private final HostListener hostListener = new InternalHostListener();
131 private final IntentListener intentListener = new InternalIntentListener();
132 private final FlowRuleListener flowListener = new InternalFlowListener();
133
134 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700135 private final ExecutorService msgSender =
136 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
Thomas Vachuska329af532015-03-10 02:08:33 -0700137
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(),
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700191 new ReqAllFlowTraffic(),
192 new ReqAllPortTraffic(),
Simon Huntd2747a02015-04-30 22:41:16 -0700193 new ReqDevLinkFlows(),
194 new CancelTraffic()
195 );
196 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700197
Simon Huntd2747a02015-04-30 22:41:16 -0700198 // ==================================================================
Thomas Vachuska329af532015-03-10 02:08:33 -0700199
Simon Huntc54cd1b2015-05-11 13:43:44 -0700200 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700201 private final class TopoStart extends RequestHandler {
202 private TopoStart() {
203 super(TOPO_START);
204 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700205
Simon Huntd2747a02015-04-30 22:41:16 -0700206 @Override
207 public void process(long sid, ObjectNode payload) {
208 addListeners();
209 sendAllInstances(null);
210 sendAllDevices();
211 sendAllLinks();
212 sendAllHosts();
Thomas Vachuska329af532015-03-10 02:08:33 -0700213 }
214 }
215
Simon Huntc54cd1b2015-05-11 13:43:44 -0700216 @Deprecated
Simon Hunt732bb2e2015-05-13 18:32:16 -0700217 private final class TopoHeartbeat extends RequestHandler {
218 private TopoHeartbeat() {
219 super(TOPO_HEARTBEAT);
220 }
221
222 @Override
223 public void process(long sid, ObjectNode payload) {
224 // place holder for now
225 }
226 }
227
228 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700229 private final class TopoStop extends RequestHandler {
230 private TopoStop() {
231 super(TOPO_STOP);
232 }
233
234 @Override
235 public void process(long sid, ObjectNode payload) {
236 stopSummaryMonitoring();
237 stopTrafficMonitoring();
238 }
239 }
240
Simon Huntc54cd1b2015-05-11 13:43:44 -0700241 @Deprecated
Simon Huntd2747a02015-04-30 22:41:16 -0700242 private final class ReqSummary extends RequestHandler {
243 private ReqSummary() {
244 super(REQ_SUMMARY);
245 }
246
247 @Override
248 public void process(long sid, ObjectNode payload) {
249 requestSummary(sid);
250 startSummaryMonitoring();
251 }
252 }
253
254 private final class CancelSummary extends RequestHandler {
255 private CancelSummary() {
256 super(CANCEL_SUMMARY);
257 }
258
259 @Override
260 public void process(long sid, ObjectNode payload) {
261 stopSummaryMonitoring();
262 }
263 }
264
265 private final class SpriteListReq extends RequestHandler {
266 private SpriteListReq() {
267 super(SPRITE_LIST_REQ);
268 }
269
270 @Override
271 public void process(long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700272 ObjectNode root = objectNode();
273 ArrayNode names = arrayNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700274 get(SpriteService.class).getNames().forEach(names::add);
275 root.set("names", names);
276 sendMessage("spriteListResponse", sid, root);
277 }
278 }
279
280 private final class SpriteDataReq extends RequestHandler {
281 private SpriteDataReq() {
282 super(SPRITE_DATA_REQ);
283 }
284
285 @Override
286 public void process(long sid, ObjectNode payload) {
287 String name = string(payload, "name");
Simon Huntda580882015-05-12 20:58:18 -0700288 ObjectNode root = objectNode();
Simon Huntd2747a02015-04-30 22:41:16 -0700289 root.set("data", get(SpriteService.class).get(name));
290 sendMessage("spriteDataResponse", sid, root);
291 }
292 }
293
294 private final class RequestDetails extends RequestHandler {
295 private RequestDetails() {
296 super(REQ_DETAILS);
297 }
298
299 @Override
300 public void process(long sid, ObjectNode payload) {
301 String type = string(payload, "class", "unknown");
302 String id = JsonUtils.string(payload, "id");
303
304 if (type.equals("device")) {
305 sendMessage(deviceDetails(deviceId(id), sid));
306 } else if (type.equals("host")) {
307 sendMessage(hostDetails(hostId(id), sid));
308 }
309 }
310 }
311
312 private final class UpdateMeta extends RequestHandler {
313 private UpdateMeta() {
314 super(UPDATE_META);
315 }
316
317 @Override
318 public void process(long sid, ObjectNode payload) {
319 updateMetaUi(payload);
320 }
321 }
322
323 private final class EqMasters extends RequestHandler {
324 private EqMasters() {
325 super(EQ_MASTERS);
326 }
327
328 @Override
329 public void process(long sid, ObjectNode payload) {
330 directory.get(MastershipAdminService.class).balanceRoles();
331 }
332 }
333
334 // === TODO: move traffic related classes to traffic app
335
336 private final class AddHostIntent extends RequestHandler {
337 private AddHostIntent() {
338 super(ADD_HOST_INTENT);
339 }
340
341 @Override
342 public void process(long sid, ObjectNode payload) {
343 // TODO: add protection against device ids and non-existent hosts.
344 HostId one = hostId(string(payload, "one"));
345 HostId two = hostId(string(payload, "two"));
346
347 HostToHostIntent intent = HostToHostIntent.builder()
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700348 .appId(appId)
349 .one(one)
350 .two(two)
351 .build();
Simon Huntd2747a02015-04-30 22:41:16 -0700352
353 intentService.submit(intent);
354 startMonitoringIntent(intent);
355 }
356 }
357
358 private final class AddMultiSourceIntent extends RequestHandler {
359 private AddMultiSourceIntent() {
360 super(ADD_MULTI_SRC_INTENT);
361 }
362
363 @Override
364 public void process(long sid, ObjectNode payload) {
365 // TODO: add protection against device ids and non-existent hosts.
366 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
367 HostId dst = hostId(string(payload, "dst"));
368 Host dstHost = hostService.getHost(dst);
369
370 Set<ConnectPoint> ingressPoints = getHostLocations(src);
371
372 // FIXME: clearly, this is not enough
373 TrafficSelector selector = DefaultTrafficSelector.builder()
374 .matchEthDst(dstHost.mac()).build();
375 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
376
377 MultiPointToSinglePointIntent intent =
378 MultiPointToSinglePointIntent.builder()
379 .appId(appId)
380 .selector(selector)
381 .treatment(treatment)
382 .ingressPoints(ingressPoints)
383 .egressPoint(dstHost.location())
384 .build();
385
386 intentService.submit(intent);
387 startMonitoringIntent(intent);
388 }
389 }
390
391 private final class ReqRelatedIntents extends RequestHandler {
392 private ReqRelatedIntents() {
393 super(REQ_RELATED_INTENTS);
394 }
395
396 @Override
397 public void process(long sid, ObjectNode payload) {
398 // Cancel any other traffic monitoring mode.
399 stopTrafficMonitoring();
400
401 if (!payload.has("ids")) {
402 return;
403 }
404
405 // Get the set of selected hosts and their intents.
406 ArrayNode ids = (ArrayNode) payload.path("ids");
407 selectedHosts = getHosts(ids);
408 selectedDevices = getDevices(ids);
409 selectedIntents = intentFilter.findPathIntents(
410 selectedHosts, selectedDevices, intentService.getIntents());
411 currentIntentIndex = -1;
412
413 if (haveSelectedIntents()) {
414 // Send a message to highlight all links of all monitored intents.
415 sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
416 }
417
418 // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
419// String hover = string(payload, "hover");
420// if (!isNullOrEmpty(hover)) {
421// // If there is a hover node, include it in the selection and find intents.
422// processHoverExtendedSelection(sid, hover);
423// }
424 }
425 }
426
427 private final class ReqNextIntent extends RequestHandler {
428 private ReqNextIntent() {
429 super(REQ_NEXT_INTENT);
430 }
431
432 @Override
433 public void process(long sid, ObjectNode payload) {
434 stopTrafficMonitoring();
435 requestAnotherRelatedIntent(+1);
436 }
437 }
438
439 private final class ReqPrevIntent extends RequestHandler {
440 private ReqPrevIntent() {
441 super(REQ_PREV_INTENT);
442 }
443
444 @Override
445 public void process(long sid, ObjectNode payload) {
446 stopTrafficMonitoring();
447 requestAnotherRelatedIntent(-1);
448 }
449 }
450
451 private final class ReqSelectedIntentTraffic extends RequestHandler {
452 private ReqSelectedIntentTraffic() {
453 super(REQ_SEL_INTENT_TRAFFIC);
454 }
455
456 @Override
457 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700458 trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700459 requestSelectedIntentTraffic();
460 startTrafficMonitoring();
461 }
462 }
463
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700464 private final class ReqAllFlowTraffic extends RequestHandler {
465 private ReqAllFlowTraffic() {
466 super(REQ_ALL_FLOW_TRAFFIC);
Simon Huntd2747a02015-04-30 22:41:16 -0700467 }
468
469 @Override
470 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700471 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload);
472 requestAllFlowTraffic();
473 }
474 }
475
476 private final class ReqAllPortTraffic extends RequestHandler {
477 private ReqAllPortTraffic() {
478 super(REQ_ALL_PORT_TRAFFIC);
479 }
480
481 @Override
482 public void process(long sid, ObjectNode payload) {
483 trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload);
484 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700485 }
486 }
487
488 private final class ReqDevLinkFlows extends RequestHandler {
489 private ReqDevLinkFlows() {
490 super(REQ_DEV_LINK_FLOWS);
491 }
492
493 @Override
494 public void process(long sid, ObjectNode payload) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700495 trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
Simon Huntd2747a02015-04-30 22:41:16 -0700496 requestDeviceLinkFlows(payload);
497 }
498 }
499
500 private final class CancelTraffic extends RequestHandler {
501 private CancelTraffic() {
502 super(CANCEL_TRAFFIC);
503 }
504
505 @Override
506 public void process(long sid, ObjectNode payload) {
507 selectedIntents = null;
508 sendMessage(trafficMessage());
509 stopTrafficMonitoring();
510 }
511 }
512
513 //=======================================================================
514
515
Thomas Vachuska329af532015-03-10 02:08:33 -0700516 // Sends the specified data to the client.
517 protected synchronized void sendMessage(ObjectNode data) {
518 UiConnection connection = connection();
519 if (connection != null) {
520 connection.sendMessage(data);
521 }
522 }
523
Simon Huntd2747a02015-04-30 22:41:16 -0700524 // Subscribes for summary messages.
525 private synchronized void requestSummary(long sid) {
526 sendMessage(summmaryMessage(sid));
Thomas Vachuska329af532015-03-10 02:08:33 -0700527 }
528
Simon Huntd2747a02015-04-30 22:41:16 -0700529
Thomas Vachuska329af532015-03-10 02:08:33 -0700530 private void cancelAllRequests() {
531 stopSummaryMonitoring();
532 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700533 }
534
535 // Sends all controller nodes to the client as node-added messages.
536 private void sendAllInstances(String messageType) {
537 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
538 Collections.sort(nodes, NODE_COMPARATOR);
539 for (ControllerNode node : nodes) {
540 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
541 messageType));
542 }
543 }
544
545 // Sends all devices to the client as device-added messages.
546 private void sendAllDevices() {
547 // Send optical first, others later for layered rendering
548 for (Device device : deviceService.getDevices()) {
549 if (device.type() == Device.Type.ROADM) {
550 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
551 }
552 }
553 for (Device device : deviceService.getDevices()) {
554 if (device.type() != Device.Type.ROADM) {
555 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
556 }
557 }
558 }
559
560 // Sends all links to the client as link-added messages.
561 private void sendAllLinks() {
562 // Send optical first, others later for layered rendering
563 for (Link link : linkService.getLinks()) {
564 if (link.type() == Link.Type.OPTICAL) {
565 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
566 }
567 }
568 for (Link link : linkService.getLinks()) {
569 if (link.type() != Link.Type.OPTICAL) {
570 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
571 }
572 }
573 }
574
575 // Sends all hosts to the client as host-added messages.
576 private void sendAllHosts() {
577 for (Host host : hostService.getHosts()) {
578 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
579 }
580 }
581
Thomas Vachuska329af532015-03-10 02:08:33 -0700582
Simon Huntd2747a02015-04-30 22:41:16 -0700583 private synchronized void startMonitoringIntent(Intent intent) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700584 selectedHosts = new HashSet<>();
585 selectedDevices = new HashSet<>();
586 selectedIntents = new ArrayList<>();
587 selectedIntents.add(intent);
588 currentIntentIndex = -1;
Simon Huntd2747a02015-04-30 22:41:16 -0700589 requestAnotherRelatedIntent(+1);
590 requestSelectedIntentTraffic();
Thomas Vachuska329af532015-03-10 02:08:33 -0700591 }
592
593
594 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
595 Set<ConnectPoint> points = new HashSet<>();
596 for (HostId hostId : hostIds) {
597 points.add(getHostLocation(hostId));
598 }
599 return points;
600 }
601
602 private HostLocation getHostLocation(HostId hostId) {
603 return hostService.getHost(hostId).location();
604 }
605
606 // Produces a list of host ids from the specified JSON array.
607 private Set<HostId> getHostIds(ArrayNode ids) {
608 Set<HostId> hostIds = new HashSet<>();
609 for (JsonNode id : ids) {
610 hostIds.add(hostId(id.asText()));
611 }
612 return hostIds;
613 }
614
615
Simon Huntd2747a02015-04-30 22:41:16 -0700616 private synchronized void startTrafficMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700617 stopTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700618 trafficTask = new TrafficMonitor();
619 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska329af532015-03-10 02:08:33 -0700620 }
621
622 private synchronized void stopTrafficMonitoring() {
623 if (trafficTask != null) {
624 trafficTask.cancel();
625 trafficTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700626 }
627 }
628
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700629 // Subscribes for flow traffic messages.
630 private synchronized void requestAllFlowTraffic() {
Simon Huntd2747a02015-04-30 22:41:16 -0700631 startTrafficMonitoring();
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700632 sendMessage(trafficSummaryMessage(StatsType.FLOW));
633 }
634
635 // Subscribes for port traffic messages.
636 private synchronized void requestAllPortTraffic() {
637 startTrafficMonitoring();
638 sendMessage(trafficSummaryMessage(StatsType.PORT));
Thomas Vachuska329af532015-03-10 02:08:33 -0700639 }
640
Simon Huntd2747a02015-04-30 22:41:16 -0700641 private void requestDeviceLinkFlows(ObjectNode payload) {
642 startTrafficMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700643
644 // Get the set of selected hosts and their intents.
645 ArrayNode ids = (ArrayNode) payload.path("ids");
646 Set<Host> hosts = new HashSet<>();
647 Set<Device> devices = getDevices(ids);
648
649 // If there is a hover node, include it in the hosts and find intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700650 String hover = JsonUtils.string(payload, "hover");
Thomas Vachuska329af532015-03-10 02:08:33 -0700651 if (!isNullOrEmpty(hover)) {
652 addHover(hosts, devices, hover);
653 }
Simon Huntd2747a02015-04-30 22:41:16 -0700654 sendMessage(flowSummaryMessage(devices));
Thomas Vachuska329af532015-03-10 02:08:33 -0700655 }
656
657
Thomas Vachuska329af532015-03-10 02:08:33 -0700658 private boolean haveSelectedIntents() {
659 return selectedIntents != null && !selectedIntents.isEmpty();
660 }
661
662 // Processes the selection extended with hovered item to segregate items
663 // into primary (those including the hover) vs secondary highlights.
664 private void processHoverExtendedSelection(long sid, String hover) {
665 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
666 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
667 addHover(hoverSelHosts, hoverSelDevices, hover);
668
669 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
670 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
671 selectedIntents);
672 Set<Intent> secondary = new HashSet<>(selectedIntents);
673 secondary.removeAll(primary);
674
675 // Send a message to highlight all links of all monitored intents.
Simon Huntd2747a02015-04-30 22:41:16 -0700676 sendMessage(trafficMessage(new TrafficClass("primary", primary),
Thomas Vachuska329af532015-03-10 02:08:33 -0700677 new TrafficClass("secondary", secondary)));
678 }
679
680 // Requests next or previous related intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700681 private void requestAnotherRelatedIntent(int offset) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700682 if (haveSelectedIntents()) {
683 currentIntentIndex = currentIntentIndex + offset;
684 if (currentIntentIndex < 0) {
685 currentIntentIndex = selectedIntents.size() - 1;
686 } else if (currentIntentIndex >= selectedIntents.size()) {
687 currentIntentIndex = 0;
688 }
Simon Huntd2747a02015-04-30 22:41:16 -0700689 sendSelectedIntent();
Thomas Vachuska329af532015-03-10 02:08:33 -0700690 }
691 }
692
693 // Sends traffic information on the related intents with the currently
694 // selected intent highlighted.
Simon Huntd2747a02015-04-30 22:41:16 -0700695 private void sendSelectedIntent() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700696 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
697 log.info("Requested next intent {}", selectedIntent.id());
698
699 Set<Intent> primary = new HashSet<>();
700 primary.add(selectedIntent);
701
702 Set<Intent> secondary = new HashSet<>(selectedIntents);
703 secondary.remove(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),
Thomas Vachuska329af532015-03-10 02:08:33 -0700707 new TrafficClass("secondary", secondary)));
708 }
709
710 // Requests monitoring of traffic for the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700711 private void requestSelectedIntentTraffic() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700712 if (haveSelectedIntents()) {
713 if (currentIntentIndex < 0) {
714 currentIntentIndex = 0;
715 }
716 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
717 log.info("Requested traffic for selected {}", selectedIntent.id());
718
719 Set<Intent> primary = new HashSet<>();
720 primary.add(selectedIntent);
721
722 // Send a message to highlight all links of the selected intent.
Simon Huntd2747a02015-04-30 22:41:16 -0700723 sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700724 }
725 }
726
Simon Huntd2747a02015-04-30 22:41:16 -0700727 private synchronized void startSummaryMonitoring() {
Thomas Vachuska329af532015-03-10 02:08:33 -0700728 stopSummaryMonitoring();
Thomas Vachuska329af532015-03-10 02:08:33 -0700729 summaryTask = new SummaryMonitor();
730 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
Simon Huntd2747a02015-04-30 22:41:16 -0700731 summaryRunning = true;
Thomas Vachuska329af532015-03-10 02:08:33 -0700732 }
733
734 private synchronized void stopSummaryMonitoring() {
Simon Huntd2747a02015-04-30 22:41:16 -0700735 if (summaryTask != null) {
Thomas Vachuska329af532015-03-10 02:08:33 -0700736 summaryTask.cancel();
737 summaryTask = null;
Thomas Vachuska329af532015-03-10 02:08:33 -0700738 }
Simon Huntd2747a02015-04-30 22:41:16 -0700739 summaryRunning = false;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700740 }
741
Thomas Vachuska329af532015-03-10 02:08:33 -0700742
743 // Adds all internal listeners.
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700744 private synchronized void addListeners() {
Thomas Vachuskae586b792015-03-26 13:59:38 -0700745 listenersRemoved = false;
Thomas Vachuska329af532015-03-10 02:08:33 -0700746 clusterService.addListener(clusterListener);
747 mastershipService.addListener(mastershipListener);
748 deviceService.addListener(deviceListener);
749 linkService.addListener(linkListener);
750 hostService.addListener(hostListener);
751 intentService.addListener(intentListener);
752 flowService.addListener(flowListener);
753 }
754
755 // Removes all internal listeners.
756 private synchronized void removeListeners() {
757 if (!listenersRemoved) {
758 listenersRemoved = true;
759 clusterService.removeListener(clusterListener);
760 mastershipService.removeListener(mastershipListener);
761 deviceService.removeListener(deviceListener);
762 linkService.removeListener(linkListener);
763 hostService.removeListener(hostListener);
764 intentService.removeListener(intentListener);
765 flowService.removeListener(flowListener);
766 }
767 }
768
769 // Cluster event listener.
770 private class InternalClusterListener implements ClusterEventListener {
771 @Override
772 public void event(ClusterEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700773 msgSender.execute(() -> sendMessage(instanceMessage(event, null)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700774 }
775 }
776
777 // Mastership change listener
778 private class InternalMastershipListener implements MastershipListener {
779 @Override
780 public void event(MastershipEvent event) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700781 msgSender.execute(() -> {
782 sendAllInstances("updateInstance");
783 Device device = deviceService.getDevice(event.subject());
784 if (device != null) {
785 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
786 }
787 });
Thomas Vachuska329af532015-03-10 02:08:33 -0700788 }
789 }
790
791 // Device event listener.
792 private class InternalDeviceListener implements DeviceListener {
793 @Override
794 public void event(DeviceEvent event) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700795 if (event.type() != PORT_STATS_UPDATED) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700796 msgSender.execute(() -> sendMessage(deviceMessage(event)));
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700797 eventAccummulator.add(event);
798 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700799 }
800 }
801
802 // Link event listener.
803 private class InternalLinkListener implements LinkListener {
804 @Override
805 public void event(LinkEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700806 msgSender.execute(() -> sendMessage(linkMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700807 eventAccummulator.add(event);
808 }
809 }
810
811 // Host event listener.
812 private class InternalHostListener implements HostListener {
813 @Override
814 public void event(HostEvent event) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700815 msgSender.execute(() -> sendMessage(hostMessage(event)));
Thomas Vachuska329af532015-03-10 02:08:33 -0700816 eventAccummulator.add(event);
817 }
818 }
819
820 // Intent event listener.
821 private class InternalIntentListener implements IntentListener {
822 @Override
823 public void event(IntentEvent event) {
Simon Huntd2747a02015-04-30 22:41:16 -0700824 if (trafficTask != null) {
Thomas Vachuska52c98bd2015-05-27 20:54:02 -0700825 msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic);
Thomas Vachuska329af532015-03-10 02:08:33 -0700826 }
827 eventAccummulator.add(event);
828 }
829 }
830
831 // Intent event listener.
832 private class InternalFlowListener implements FlowRuleListener {
833 @Override
834 public void event(FlowRuleEvent event) {
835 eventAccummulator.add(event);
836 }
837 }
838
Simon Huntd2747a02015-04-30 22:41:16 -0700839 // encapsulate
840 private static class TrafficEvent {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700841 enum Type {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700842 ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700843 }
Simon Huntd2747a02015-04-30 22:41:16 -0700844
845 private final Type type;
846 private final ObjectNode payload;
847
848 TrafficEvent(Type type, ObjectNode payload) {
849 this.type = type;
850 this.payload = payload;
851 }
852 }
853
Thomas Vachuska329af532015-03-10 02:08:33 -0700854 // Periodic update of the traffic information
855 private class TrafficMonitor extends TimerTask {
856 @Override
857 public void run() {
858 try {
859 if (trafficEvent != null) {
Simon Huntd2747a02015-04-30 22:41:16 -0700860 switch (trafficEvent.type) {
Thomas Vachuskaf0397b52015-05-29 13:50:17 -0700861 case ALL_FLOW_TRAFFIC:
862 requestAllFlowTraffic();
863 break;
864 case ALL_PORT_TRAFFIC:
865 requestAllPortTraffic();
Simon Huntd2747a02015-04-30 22:41:16 -0700866 break;
867 case DEV_LINK_FLOWS:
868 requestDeviceLinkFlows(trafficEvent.payload);
869 break;
870 case SEL_INTENT:
871 requestSelectedIntentTraffic();
872 break;
873 default:
874 // nothing to do
875 break;
Thomas Vachuska329af532015-03-10 02:08:33 -0700876 }
877 }
878 } catch (Exception e) {
879 log.warn("Unable to handle traffic request due to {}", e.getMessage());
880 log.warn("Boom!", e);
881 }
882 }
883 }
884
885 // Periodic update of the summary information
886 private class SummaryMonitor extends TimerTask {
887 @Override
888 public void run() {
889 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700890 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700891 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700892 }
893 } catch (Exception e) {
894 log.warn("Unable to handle summary request due to {}", e.getMessage());
895 log.warn("Boom!", e);
896 }
897 }
898 }
899
900 // Accumulates events to drive methodic update of the summary pane.
901 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
902 protected InternalEventAccummulator() {
903 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
904 }
905
906 @Override
907 public void processItems(List<Event> items) {
908 try {
Simon Huntd2747a02015-04-30 22:41:16 -0700909 if (summaryRunning) {
Thomas Vachuskac7f79962015-05-28 09:37:34 -0700910 msgSender.execute(() -> requestSummary(0));
Thomas Vachuska329af532015-03-10 02:08:33 -0700911 }
912 } catch (Exception e) {
913 log.warn("Unable to handle summary request due to {}", e.getMessage());
914 log.debug("Boom!", e);
915 }
916 }
917 }
918}