blob: 16b0eb6755dc71d263fe8753894ad1435ba2ca46 [file] [log] [blame]
Thomas Vachuska7d638d32014-11-07 10:24:43 -08001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska7d638d32014-11-07 10:24:43 -08003 *
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 */
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080016package org.onosproject.ui.impl;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080017
Ray Milkeyebc5d222015-03-18 15:45:36 -070018import java.io.IOException;
19import java.util.ArrayList;
20import java.util.Collections;
21import java.util.Comparator;
22import java.util.HashSet;
23import java.util.List;
24import java.util.Set;
25import java.util.Timer;
26import java.util.TimerTask;
27
Thomas Vachuska7d638d32014-11-07 10:24:43 -080028import org.eclipse.jetty.websocket.WebSocket;
Simon Hunt8ead3a22015-01-06 11:00:15 -080029import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskaecb63c52015-02-19 10:03:48 -080030import org.onlab.util.AbstractAccumulator;
31import org.onlab.util.Accumulator;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.cluster.ClusterEvent;
33import org.onosproject.cluster.ClusterEventListener;
34import org.onosproject.cluster.ControllerNode;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.mastership.MastershipAdminService;
39import org.onosproject.mastership.MastershipEvent;
40import org.onosproject.mastership.MastershipListener;
41import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.Device;
43import org.onosproject.net.Host;
44import org.onosproject.net.HostId;
45import org.onosproject.net.HostLocation;
46import org.onosproject.net.Link;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.flow.DefaultTrafficSelector;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
51import org.onosproject.net.flow.FlowRuleEvent;
52import org.onosproject.net.flow.FlowRuleListener;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
55import org.onosproject.net.host.HostEvent;
56import org.onosproject.net.host.HostListener;
57import org.onosproject.net.intent.HostToHostIntent;
58import org.onosproject.net.intent.Intent;
59import org.onosproject.net.intent.IntentEvent;
60import org.onosproject.net.intent.IntentListener;
61import org.onosproject.net.intent.MultiPointToSinglePointIntent;
62import org.onosproject.net.link.LinkEvent;
63import org.onosproject.net.link.LinkListener;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080064
Ray Milkeyebc5d222015-03-18 15:45:36 -070065import com.fasterxml.jackson.databind.JsonNode;
66import com.fasterxml.jackson.databind.node.ArrayNode;
67import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080068
Thomas Vachuskae7591e52014-11-13 21:31:15 -080069import static com.google.common.base.Strings.isNullOrEmpty;
Brian O'Connorabafb502014-12-02 22:26:20 -080070import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
71import static org.onosproject.net.DeviceId.deviceId;
72import static org.onosproject.net.HostId.hostId;
73import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
74import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_UPDATED;
75import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
76import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080077
Thomas Vachuska7d638d32014-11-07 10:24:43 -080078/**
79 * Web socket capable of interacting with the GUI topology view.
80 */
Thomas Vachuska329af532015-03-10 02:08:33 -070081@Deprecated
Thomas Vachuska7c27ad72014-11-14 16:20:10 -080082public class TopologyViewWebSocket
83 extends TopologyViewMessages
Thomas Vachuskaba5621e2014-11-12 01:47:19 -080084 implements WebSocket.OnTextMessage, WebSocket.OnControl {
85
86 private static final long MAX_AGE_MS = 15000;
87
88 private static final byte PING = 0x9;
89 private static final byte PONG = 0xA;
90 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
Thomas Vachuska7d638d32014-11-07 10:24:43 -080091
Brian O'Connorabafb502014-12-02 22:26:20 -080092 private static final String APP_ID = "org.onosproject.gui";
Thomas Vachuska4830d392014-11-09 17:09:56 -080093
alshabib0ad84ec2014-12-03 22:48:01 -080094 private static final long TRAFFIC_FREQUENCY = 5000;
Thomas Vachuska164fa5c2014-12-02 21:59:41 -080095 private static final long SUMMARY_FREQUENCY = 30000;
Thomas Vachuska22e34922014-11-14 00:40:55 -080096
Thomas Vachuska47635c62014-11-22 01:21:36 -080097 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
98 new Comparator<ControllerNode>() {
99 @Override
100 public int compare(ControllerNode o1, ControllerNode o2) {
101 return o1.id().toString().compareTo(o2.id().toString());
102 }
103 };
104
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800105
106 private final Timer timer = new Timer("topology-view");
107
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800108 private static final int MAX_EVENTS = 1000;
109 private static final int MAX_BATCH_MS = 5000;
110 private static final int MAX_IDLE_MS = 1000;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800111
Thomas Vachuska4830d392014-11-09 17:09:56 -0800112 private final ApplicationId appId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800113
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800114 private Connection connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800115 private FrameConnection control;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800116
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800117 private final ClusterEventListener clusterListener = new InternalClusterListener();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800118 private final MastershipListener mastershipListener = new InternalMastershipListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800119 private final DeviceListener deviceListener = new InternalDeviceListener();
120 private final LinkListener linkListener = new InternalLinkListener();
121 private final HostListener hostListener = new InternalHostListener();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800122 private final IntentListener intentListener = new InternalIntentListener();
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800123 private final FlowRuleListener flowListener = new InternalFlowListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800124
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800125 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska47635c62014-11-22 01:21:36 -0800126
127 private TimerTask trafficTask;
128 private ObjectNode trafficEvent;
129
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800130 private TimerTask summaryTask;
131 private ObjectNode summaryEvent;
132
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800133 private long lastActive = System.currentTimeMillis();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800134 private boolean listenersRemoved = false;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800135
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800136 private TopologyViewIntentFilter intentFilter;
137
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800138 // Current selection context
139 private Set<Host> selectedHosts;
140 private Set<Device> selectedDevices;
141 private List<Intent> selectedIntents;
142 private int currentIntentIndex = -1;
143
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800144 /**
145 * Creates a new web-socket for serving data to GUI topology view.
146 *
147 * @param directory service directory
148 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800149 public TopologyViewWebSocket(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800150 super(directory);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800151 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
Thomas Vachuska9edca302014-11-22 17:06:42 -0800152 hostService, linkService);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800153 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800154 }
155
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800156 /**
157 * Issues a close on the connection.
158 */
159 synchronized void close() {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800160 removeListeners();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800161 if (connection.isOpen()) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800162 connection.close();
163 }
164 }
165
166 /**
167 * Indicates if this connection is idle.
Thomas Vachuska3266abf2014-11-13 09:28:46 -0800168 *
169 * @return true if idle or closed
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800170 */
171 synchronized boolean isIdle() {
172 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
Thomas Vachuskabbc3b4b2014-12-07 15:57:53 -0800173 if (idle || (connection != null && !connection.isOpen())) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800174 return true;
Thomas Vachuskabbc3b4b2014-12-07 15:57:53 -0800175 } else if (connection != null) {
176 try {
177 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
178 } catch (IOException e) {
179 log.warn("Unable to send ping message due to: ", e);
180 }
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800181 }
182 return false;
183 }
184
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800185 @Override
186 public void onOpen(Connection connection) {
Thomas Vachuskab52a0142015-04-21 17:48:15 -0700187 log.info("Legacy GUI client connected");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800188 this.connection = connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800189 this.control = (FrameConnection) connection;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800190 addListeners();
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800191
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800192 sendAllInstances(null);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800193 sendAllDevices();
194 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800195 sendAllHosts();
196 }
197
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800198 @Override
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800199 public synchronized void onClose(int closeCode, String message) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800200 removeListeners();
Thomas Vachuska22e34922014-11-14 00:40:55 -0800201 timer.cancel();
Thomas Vachuskab52a0142015-04-21 17:48:15 -0700202 log.info("Legacy GUI client disconnected");
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800203 }
204
205 @Override
206 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
207 lastActive = System.currentTimeMillis();
208 return true;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800209 }
210
211 @Override
212 public void onMessage(String data) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800213 lastActive = System.currentTimeMillis();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800214 try {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800215 processMessage((ObjectNode) mapper.reader().readTree(data));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800216 } catch (Exception e) {
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800217 log.warn("Unable to parse GUI request {} due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800218 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800219 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800220 }
221
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800222 // Processes the specified event.
223 private void processMessage(ObjectNode event) {
224 String type = string(event, "event", "unknown");
225 if (type.equals("requestDetails")) {
226 requestDetails(event);
227 } else if (type.equals("updateMeta")) {
228 updateMetaUi(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800229
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800230 } else if (type.equals("addHostIntent")) {
231 createHostIntent(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800232 } else if (type.equals("addMultiSourceIntent")) {
233 createMultiSourceIntent(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800234
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800235 } else if (type.equals("requestRelatedIntents")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800236 stopTrafficMonitoring();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800237 requestRelatedIntents(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800238
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800239 } else if (type.equals("requestNextRelatedIntent")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800240 stopTrafficMonitoring();
241 requestAnotherRelatedIntent(event, +1);
242 } else if (type.equals("requestPrevRelatedIntent")) {
243 stopTrafficMonitoring();
244 requestAnotherRelatedIntent(event, -1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800245 } else if (type.equals("requestSelectedIntentTraffic")) {
246 requestSelectedIntentTraffic(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800247 startTrafficMonitoring(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800248
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800249 } else if (type.equals("requestAllTraffic")) {
250 requestAllTraffic(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800251 startTrafficMonitoring(event);
252
Thomas Vachuska29617e52014-11-20 03:17:46 -0800253 } else if (type.equals("requestDeviceLinkFlows")) {
254 requestDeviceLinkFlows(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800255 startTrafficMonitoring(event);
256
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800257 } else if (type.equals("cancelTraffic")) {
258 cancelTraffic(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800259
260 } else if (type.equals("requestSummary")) {
261 requestSummary(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800262 startSummaryMonitoring(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800263 } else if (type.equals("cancelSummary")) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800264 stopSummaryMonitoring();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800265
266 } else if (type.equals("equalizeMasters")) {
267 equalizeMasters(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800268 }
269 }
270
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800271 // Sends the specified data to the client.
Bri Prebilic Cole09e464b2015-01-07 17:13:54 -0800272 protected synchronized void sendMessage(ObjectNode data) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800273 try {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800274 if (connection.isOpen()) {
275 connection.sendMessage(data.toString());
276 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800277 } catch (IOException e) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800278 log.warn("Unable to send message {} to GUI due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800279 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800280 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800281 }
282
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800283 // Sends all controller nodes to the client as node-added messages.
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800284 private void sendAllInstances(String messageType) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800285 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
286 Collections.sort(nodes, NODE_COMPARATOR);
287 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800288 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
289 messageType));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800290 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800291 }
292
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800293 // Sends all devices to the client as device-added messages.
294 private void sendAllDevices() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800295 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800296 for (Device device : deviceService.getDevices()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800297 if (device.type() == Device.Type.ROADM) {
298 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
299 }
300 }
301 for (Device device : deviceService.getDevices()) {
302 if (device.type() != Device.Type.ROADM) {
303 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
304 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800305 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800306 }
307
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800308 // Sends all links to the client as link-added messages.
309 private void sendAllLinks() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800310 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800311 for (Link link : linkService.getLinks()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800312 if (link.type() == Link.Type.OPTICAL) {
313 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
314 }
315 }
316 for (Link link : linkService.getLinks()) {
317 if (link.type() != Link.Type.OPTICAL) {
318 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
319 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800320 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800321 }
322
323 // Sends all hosts to the client as host-added messages.
324 private void sendAllHosts() {
325 for (Host host : hostService.getHosts()) {
326 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
327 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800328 }
329
330 // Sends back device or host details.
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800331 private void requestDetails(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800332 ObjectNode payload = payload(event);
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800333 String type = string(payload, "class", "unknown");
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800334 long sid = number(event, "sid");
335
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800336 if (type.equals("device")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800337 sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800338 } else if (type.equals("host")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800339 sendMessage(hostDetails(hostId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800340 }
341 }
342
Thomas Vachuska9edca302014-11-22 17:06:42 -0800343
Thomas Vachuska4830d392014-11-09 17:09:56 -0800344 // Creates host-to-host intent.
345 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800346 ObjectNode payload = payload(event);
347 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800348 // TODO: add protection against device ids and non-existent hosts.
349 HostId one = hostId(string(payload, "one"));
350 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800351
Thomas Vachuska9edca302014-11-22 17:06:42 -0800352 HostToHostIntent intent =
Ray Milkeyebc5d222015-03-18 15:45:36 -0700353 HostToHostIntent.builder()
354 .appId(appId)
355 .one(one)
356 .two(two)
357 .build();
358
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800359
Thomas Vachuska9edca302014-11-22 17:06:42 -0800360 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800361 startMonitoringIntent(event, intent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800362 }
363
Thomas Vachuska9edca302014-11-22 17:06:42 -0800364 // Creates multi-source-to-single-dest intent.
365 private void createMultiSourceIntent(ObjectNode event) {
366 ObjectNode payload = payload(event);
367 long id = number(event, "sid");
368 // TODO: add protection against device ids and non-existent hosts.
369 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
370 HostId dst = hostId(string(payload, "dst"));
371 Host dstHost = hostService.getHost(dst);
372
373 Set<ConnectPoint> ingressPoints = getHostLocations(src);
374
375 // FIXME: clearly, this is not enough
376 TrafficSelector selector = DefaultTrafficSelector.builder()
377 .matchEthDst(dstHost.mac()).build();
Brian O'Connor6b528132015-03-10 16:39:52 -0700378 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
Thomas Vachuska9edca302014-11-22 17:06:42 -0800379
380 MultiPointToSinglePointIntent intent =
Ray Milkeyebc5d222015-03-18 15:45:36 -0700381 MultiPointToSinglePointIntent.builder()
382 .appId(appId)
383 .selector(selector)
384 .treatment(treatment)
385 .ingressPoints(ingressPoints)
386 .egressPoint(dstHost.location())
387 .build();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800388
Thomas Vachuska9edca302014-11-22 17:06:42 -0800389 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800390 startMonitoringIntent(event, intent);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800391 }
392
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800393
394 private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
395 selectedHosts = new HashSet<>();
396 selectedDevices = new HashSet<>();
397 selectedIntents = new ArrayList<>();
398 selectedIntents.add(intent);
399 currentIntentIndex = -1;
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800400 requestAnotherRelatedIntent(event, +1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800401 requestSelectedIntentTraffic(event);
402 }
403
404
Thomas Vachuska9edca302014-11-22 17:06:42 -0800405 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
406 Set<ConnectPoint> points = new HashSet<>();
407 for (HostId hostId : hostIds) {
408 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800409 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800410 return points;
411 }
412
413 private HostLocation getHostLocation(HostId hostId) {
414 return hostService.getHost(hostId).location();
415 }
416
417 // Produces a list of host ids from the specified JSON array.
418 private Set<HostId> getHostIds(ArrayNode ids) {
419 Set<HostId> hostIds = new HashSet<>();
420 for (JsonNode id : ids) {
421 hostIds.add(hostId(id.asText()));
422 }
423 return hostIds;
424 }
425
426
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800427 private synchronized long startTrafficMonitoring(ObjectNode event) {
428 stopTrafficMonitoring();
Thomas Vachuska9edca302014-11-22 17:06:42 -0800429 trafficEvent = event;
430 trafficTask = new TrafficMonitor();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800431 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800432 return number(event, "sid");
433 }
434
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800435 private synchronized void stopTrafficMonitoring() {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800436 if (trafficTask != null) {
437 trafficTask.cancel();
438 trafficTask = null;
439 trafficEvent = null;
440 }
441 }
442
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800443 // Subscribes for host traffic messages.
444 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800445 long sid = startTrafficMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800446 sendMessage(trafficSummaryMessage(sid));
447 }
448
Thomas Vachuska29617e52014-11-20 03:17:46 -0800449 private void requestDeviceLinkFlows(ObjectNode event) {
450 ObjectNode payload = payload(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800451 long sid = startTrafficMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800452
453 // Get the set of selected hosts and their intents.
454 ArrayNode ids = (ArrayNode) payload.path("ids");
455 Set<Host> hosts = new HashSet<>();
456 Set<Device> devices = getDevices(ids);
457
458 // If there is a hover node, include it in the hosts and find intents.
459 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800460 if (!isNullOrEmpty(hover)) {
461 addHover(hosts, devices, hover);
462 }
463 sendMessage(flowSummaryMessage(sid, devices));
464 }
465
466
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800467 // Requests related intents message.
468 private synchronized void requestRelatedIntents(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800469 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800470 if (!payload.has("ids")) {
471 return;
472 }
473
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800474 long sid = number(event, "sid");
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800475
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800476 // Cancel any other traffic monitoring mode.
477 stopTrafficMonitoring();
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800478
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800479 // Get the set of selected hosts and their intents.
480 ArrayNode ids = (ArrayNode) payload.path("ids");
481 selectedHosts = getHosts(ids);
482 selectedDevices = getDevices(ids);
483 selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
484 intentService.getIntents());
485 currentIntentIndex = -1;
486
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800487 if (haveSelectedIntents()) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800488 // Send a message to highlight all links of all monitored intents.
489 sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800490 }
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800491
492 // FIXME: Re-introduce one the client click vs hover gesture stuff is sorted out.
493// String hover = string(payload, "hover");
494// if (!isNullOrEmpty(hover)) {
495// // If there is a hover node, include it in the selection and find intents.
496// processHoverExtendedSelection(sid, hover);
497// }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800498 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800499
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800500 private boolean haveSelectedIntents() {
501 return selectedIntents != null && !selectedIntents.isEmpty();
502 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800503
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800504 // Processes the selection extended with hovered item to segregate items
505 // into primary (those including the hover) vs secondary highlights.
506 private void processHoverExtendedSelection(long sid, String hover) {
507 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
508 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
509 addHover(hoverSelHosts, hoverSelDevices, hover);
510
511 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
512 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
513 selectedIntents);
514 Set<Intent> secondary = new HashSet<>(selectedIntents);
515 secondary.removeAll(primary);
516
517 // Send a message to highlight all links of all monitored intents.
518 sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
519 new TrafficClass("secondary", secondary)));
520 }
521
522 // Requests next or previous related intent.
523 private void requestAnotherRelatedIntent(ObjectNode event, int offset) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800524 if (haveSelectedIntents()) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800525 currentIntentIndex = currentIntentIndex + offset;
526 if (currentIntentIndex < 0) {
527 currentIntentIndex = selectedIntents.size() - 1;
528 } else if (currentIntentIndex >= selectedIntents.size()) {
529 currentIntentIndex = 0;
530 }
531 sendSelectedIntent(event);
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800532 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800533 }
534
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800535 // Sends traffic information on the related intents with the currently
536 // selected intent highlighted.
537 private void sendSelectedIntent(ObjectNode event) {
538 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
539 log.info("Requested next intent {}", selectedIntent.id());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800540
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800541 Set<Intent> primary = new HashSet<>();
542 primary.add(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800543
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800544 Set<Intent> secondary = new HashSet<>(selectedIntents);
545 secondary.remove(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800546
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800547 // Send a message to highlight all links of the selected intent.
548 sendMessage(trafficMessage(number(event, "sid"),
549 new TrafficClass("primary", primary),
550 new TrafficClass("secondary", secondary)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800551 }
552
553 // Requests monitoring of traffic for the selected intent.
554 private void requestSelectedIntentTraffic(ObjectNode event) {
555 if (haveSelectedIntents()) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800556 if (currentIntentIndex < 0) {
557 currentIntentIndex = 0;
558 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800559 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
560 log.info("Requested traffic for selected {}", selectedIntent.id());
561
562 Set<Intent> primary = new HashSet<>();
563 primary.add(selectedIntent);
564
565 // Send a message to highlight all links of the selected intent.
566 sendMessage(trafficMessage(number(event, "sid"),
567 new TrafficClass("primary", primary, true)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800568 }
569 }
570
571 // Cancels sending traffic messages.
572 private void cancelTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800573 selectedIntents = null;
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800574 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800575 stopTrafficMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800576 }
577
Thomas Vachuska47635c62014-11-22 01:21:36 -0800578
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800579 private synchronized long startSummaryMonitoring(ObjectNode event) {
580 stopSummaryMonitoring();
581 summaryEvent = event;
582 summaryTask = new SummaryMonitor();
583 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
584 return number(event, "sid");
585 }
586
587 private synchronized void stopSummaryMonitoring() {
588 if (summaryEvent != null) {
589 summaryTask.cancel();
590 summaryTask = null;
591 summaryEvent = null;
592 }
593 }
594
Thomas Vachuska47635c62014-11-22 01:21:36 -0800595 // Subscribes for summary messages.
596 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800597 sendMessage(summmaryMessage(number(event, "sid")));
598 }
599
Thomas Vachuska47635c62014-11-22 01:21:36 -0800600
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800601 // Forces mastership role rebalancing.
602 private void equalizeMasters(ObjectNode event) {
603 directory.get(MastershipAdminService.class).balanceRoles();
604 }
605
606
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800607 // Adds all internal listeners.
608 private void addListeners() {
609 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800610 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800611 deviceService.addListener(deviceListener);
612 linkService.addListener(linkListener);
613 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800614 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800615 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800616 }
617
618 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800619 private synchronized void removeListeners() {
620 if (!listenersRemoved) {
621 listenersRemoved = true;
622 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800623 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800624 deviceService.removeListener(deviceListener);
625 linkService.removeListener(linkListener);
626 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800627 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800628 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800629 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800630 }
631
632 // Cluster event listener.
633 private class InternalClusterListener implements ClusterEventListener {
634 @Override
635 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800636 sendMessage(instanceMessage(event, null));
637 }
638 }
639
640 // Mastership change listener
641 private class InternalMastershipListener implements MastershipListener {
642 @Override
643 public void event(MastershipEvent event) {
644 sendAllInstances("updateInstance");
645 Device device = deviceService.getDevice(event.subject());
646 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800647 }
648 }
649
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800650 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800651 private class InternalDeviceListener implements DeviceListener {
652 @Override
653 public void event(DeviceEvent event) {
654 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800655 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800656 }
657 }
658
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800659 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800660 private class InternalLinkListener implements LinkListener {
661 @Override
662 public void event(LinkEvent event) {
663 sendMessage(linkMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800664 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800665 }
666 }
667
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800668 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800669 private class InternalHostListener implements HostListener {
670 @Override
671 public void event(HostEvent event) {
672 sendMessage(hostMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800673 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800674 }
675 }
676
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800677 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800678 private class InternalIntentListener implements IntentListener {
679 @Override
680 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800681 if (trafficEvent != null) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800682 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800683 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800684 eventAccummulator.add(event);
685 }
686 }
687
688 // Intent event listener.
689 private class InternalFlowListener implements FlowRuleListener {
690 @Override
691 public void event(FlowRuleEvent event) {
692 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800693 }
694 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800695
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800696 // Periodic update of the traffic information
Thomas Vachuska47635c62014-11-22 01:21:36 -0800697 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800698 @Override
699 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800700 try {
701 if (trafficEvent != null) {
702 String type = string(trafficEvent, "event", "unknown");
703 if (type.equals("requestAllTraffic")) {
704 requestAllTraffic(trafficEvent);
705 } else if (type.equals("requestDeviceLinkFlows")) {
706 requestDeviceLinkFlows(trafficEvent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800707 } else if (type.equals("requestSelectedIntentTraffic")) {
708 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800709 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800710 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800711 } catch (Exception e) {
712 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800713 log.debug("Boom!", e);
Thomas Vachuska22e34922014-11-14 00:40:55 -0800714 }
715 }
716 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800717
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800718 // Periodic update of the summary information
719 private class SummaryMonitor extends TimerTask {
720 @Override
721 public void run() {
722 try {
723 if (summaryEvent != null) {
724 requestSummary(summaryEvent);
725 }
726 } catch (Exception e) {
727 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800728 log.debug("Boom!", e);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800729 }
730 }
731 }
732
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800733 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800734 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800735 protected InternalEventAccummulator() {
736 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
737 }
738
Thomas Vachuska47635c62014-11-22 01:21:36 -0800739 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800740 public void processItems(List<Event> items) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800741 try {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800742 if (summaryEvent != null) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800743 sendMessage(summmaryMessage(0));
744 }
745 } catch (Exception e) {
746 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800747 log.debug("Boom!", e);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800748 }
749 }
750 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800751}
752