blob: 44ad02642db2eeafcdef4b2b83ff331d11bca66e [file] [log] [blame]
Thomas Vachuska7d638d32014-11-07 10:24:43 -08001/*
Simon Hunt8ead3a22015-01-06 11:00:15 -08002 * 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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.gui;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080017
Thomas Vachuska9edca302014-11-22 17:06:42 -080018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080019import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080020import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080021import org.eclipse.jetty.websocket.WebSocket;
Simon Hunt8ead3a22015-01-06 11:00:15 -080022import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskaecb63c52015-02-19 10:03:48 -080023import org.onlab.util.AbstractAccumulator;
24import org.onlab.util.Accumulator;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.cluster.ClusterEvent;
26import org.onosproject.cluster.ClusterEventListener;
27import org.onosproject.cluster.ControllerNode;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import 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;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080057
58import java.io.IOException;
Thomas Vachuska47635c62014-11-22 01:21:36 -080059import java.util.ArrayList;
60import java.util.Collections;
61import java.util.Comparator;
Thomas Vachuska29617e52014-11-20 03:17:46 -080062import java.util.HashSet;
Thomas Vachuska47635c62014-11-22 01:21:36 -080063import java.util.List;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080064import java.util.Set;
Thomas Vachuska22e34922014-11-14 00:40:55 -080065import java.util.Timer;
66import java.util.TimerTask;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080067
Thomas Vachuskae7591e52014-11-13 21:31:15 -080068import static com.google.common.base.Strings.isNullOrEmpty;
Brian O'Connorabafb502014-12-02 22:26:20 -080069import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
70import static org.onosproject.net.DeviceId.deviceId;
71import static org.onosproject.net.HostId.hostId;
72import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
73import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_UPDATED;
74import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
75import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080076
Thomas Vachuska7d638d32014-11-07 10:24:43 -080077/**
78 * Web socket capable of interacting with the GUI topology view.
79 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -080080public class TopologyViewWebSocket
81 extends TopologyViewMessages
Thomas Vachuskaba5621e2014-11-12 01:47:19 -080082 implements WebSocket.OnTextMessage, WebSocket.OnControl {
83
84 private static final long MAX_AGE_MS = 15000;
85
86 private static final byte PING = 0x9;
87 private static final byte PONG = 0xA;
88 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
Thomas Vachuska7d638d32014-11-07 10:24:43 -080089
Brian O'Connorabafb502014-12-02 22:26:20 -080090 private static final String APP_ID = "org.onosproject.gui";
Thomas Vachuska4830d392014-11-09 17:09:56 -080091
alshabib0ad84ec2014-12-03 22:48:01 -080092 private static final long TRAFFIC_FREQUENCY = 5000;
Thomas Vachuska164fa5c2014-12-02 21:59:41 -080093 private static final long SUMMARY_FREQUENCY = 30000;
Thomas Vachuska22e34922014-11-14 00:40:55 -080094
Thomas Vachuska47635c62014-11-22 01:21:36 -080095 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
96 new Comparator<ControllerNode>() {
97 @Override
98 public int compare(ControllerNode o1, ControllerNode o2) {
99 return o1.id().toString().compareTo(o2.id().toString());
100 }
101 };
102
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800103
104 private final Timer timer = new Timer("topology-view");
105
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800106 private static final int MAX_EVENTS = 1000;
107 private static final int MAX_BATCH_MS = 5000;
108 private static final int MAX_IDLE_MS = 1000;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800109
Thomas Vachuska4830d392014-11-09 17:09:56 -0800110 private final ApplicationId appId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800111
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800112 private Connection connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800113 private FrameConnection control;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800114
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800115 private final ClusterEventListener clusterListener = new InternalClusterListener();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800116 private final MastershipListener mastershipListener = new InternalMastershipListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800117 private final DeviceListener deviceListener = new InternalDeviceListener();
118 private final LinkListener linkListener = new InternalLinkListener();
119 private final HostListener hostListener = new InternalHostListener();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800120 private final IntentListener intentListener = new InternalIntentListener();
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800121 private final FlowRuleListener flowListener = new InternalFlowListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800122
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800123 private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska47635c62014-11-22 01:21:36 -0800124
125 private TimerTask trafficTask;
126 private ObjectNode trafficEvent;
127
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800128 private TimerTask summaryTask;
129 private ObjectNode summaryEvent;
130
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800131 private long lastActive = System.currentTimeMillis();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800132 private boolean listenersRemoved = false;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800133
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800134 private TopologyViewIntentFilter intentFilter;
135
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800136 // Current selection context
137 private Set<Host> selectedHosts;
138 private Set<Device> selectedDevices;
139 private List<Intent> selectedIntents;
140 private int currentIntentIndex = -1;
141
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800142 /**
143 * Creates a new web-socket for serving data to GUI topology view.
144 *
145 * @param directory service directory
146 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800147 public TopologyViewWebSocket(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800148 super(directory);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800149 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
Thomas Vachuska9edca302014-11-22 17:06:42 -0800150 hostService, linkService);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800151 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800152 }
153
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800154 /**
155 * Issues a close on the connection.
156 */
157 synchronized void close() {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800158 removeListeners();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800159 if (connection.isOpen()) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800160 connection.close();
161 }
162 }
163
164 /**
165 * Indicates if this connection is idle.
Thomas Vachuska3266abf2014-11-13 09:28:46 -0800166 *
167 * @return true if idle or closed
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800168 */
169 synchronized boolean isIdle() {
170 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
Thomas Vachuskabbc3b4b2014-12-07 15:57:53 -0800171 if (idle || (connection != null && !connection.isOpen())) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800172 return true;
Thomas Vachuskabbc3b4b2014-12-07 15:57:53 -0800173 } else if (connection != null) {
174 try {
175 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
176 } catch (IOException e) {
177 log.warn("Unable to send ping message due to: ", e);
178 }
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800179 }
180 return false;
181 }
182
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800183 @Override
184 public void onOpen(Connection connection) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800185 log.info("GUI client connected");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800186 this.connection = connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800187 this.control = (FrameConnection) connection;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800188 addListeners();
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800189
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800190 sendAllInstances(null);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800191 sendAllDevices();
192 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800193 sendAllHosts();
194 }
195
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800196 @Override
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800197 public synchronized void onClose(int closeCode, String message) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800198 removeListeners();
Thomas Vachuska22e34922014-11-14 00:40:55 -0800199 timer.cancel();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800200 log.info("GUI client disconnected");
201 }
202
203 @Override
204 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
205 lastActive = System.currentTimeMillis();
206 return true;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800207 }
208
209 @Override
210 public void onMessage(String data) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800211 lastActive = System.currentTimeMillis();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800212 try {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800213 processMessage((ObjectNode) mapper.reader().readTree(data));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800214 } catch (Exception e) {
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800215 log.warn("Unable to parse GUI request {} due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800216 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800217 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800218 }
219
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800220 // Processes the specified event.
221 private void processMessage(ObjectNode event) {
222 String type = string(event, "event", "unknown");
223 if (type.equals("requestDetails")) {
224 requestDetails(event);
225 } else if (type.equals("updateMeta")) {
226 updateMetaUi(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800227
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800228 } else if (type.equals("addHostIntent")) {
229 createHostIntent(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800230 } else if (type.equals("addMultiSourceIntent")) {
231 createMultiSourceIntent(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800232
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800233 } else if (type.equals("requestRelatedIntents")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800234 stopTrafficMonitoring();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800235 requestRelatedIntents(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800236
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800237 } else if (type.equals("requestNextRelatedIntent")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800238 stopTrafficMonitoring();
239 requestAnotherRelatedIntent(event, +1);
240 } else if (type.equals("requestPrevRelatedIntent")) {
241 stopTrafficMonitoring();
242 requestAnotherRelatedIntent(event, -1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800243 } else if (type.equals("requestSelectedIntentTraffic")) {
244 requestSelectedIntentTraffic(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800245 startTrafficMonitoring(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800246
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800247 } else if (type.equals("requestAllTraffic")) {
248 requestAllTraffic(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800249 startTrafficMonitoring(event);
250
Thomas Vachuska29617e52014-11-20 03:17:46 -0800251 } else if (type.equals("requestDeviceLinkFlows")) {
252 requestDeviceLinkFlows(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800253 startTrafficMonitoring(event);
254
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800255 } else if (type.equals("cancelTraffic")) {
256 cancelTraffic(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800257
258 } else if (type.equals("requestSummary")) {
259 requestSummary(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800260 startSummaryMonitoring(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800261 } else if (type.equals("cancelSummary")) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800262 stopSummaryMonitoring();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800263
264 } else if (type.equals("equalizeMasters")) {
265 equalizeMasters(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800266 }
267 }
268
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800269 // Sends the specified data to the client.
Bri Prebilic Cole09e464b2015-01-07 17:13:54 -0800270 protected synchronized void sendMessage(ObjectNode data) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800271 try {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800272 if (connection.isOpen()) {
273 connection.sendMessage(data.toString());
274 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800275 } catch (IOException e) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800276 log.warn("Unable to send message {} to GUI due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800277 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800278 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800279 }
280
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800281 // Sends all controller nodes to the client as node-added messages.
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800282 private void sendAllInstances(String messageType) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800283 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
284 Collections.sort(nodes, NODE_COMPARATOR);
285 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800286 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
287 messageType));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800288 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800289 }
290
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800291 // Sends all devices to the client as device-added messages.
292 private void sendAllDevices() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800293 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800294 for (Device device : deviceService.getDevices()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800295 if (device.type() == Device.Type.ROADM) {
296 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
297 }
298 }
299 for (Device device : deviceService.getDevices()) {
300 if (device.type() != Device.Type.ROADM) {
301 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
302 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800303 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800304 }
305
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800306 // Sends all links to the client as link-added messages.
307 private void sendAllLinks() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800308 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800309 for (Link link : linkService.getLinks()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800310 if (link.type() == Link.Type.OPTICAL) {
311 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
312 }
313 }
314 for (Link link : linkService.getLinks()) {
315 if (link.type() != Link.Type.OPTICAL) {
316 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
317 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800318 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800319 }
320
321 // Sends all hosts to the client as host-added messages.
322 private void sendAllHosts() {
323 for (Host host : hostService.getHosts()) {
324 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
325 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800326 }
327
328 // Sends back device or host details.
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800329 private void requestDetails(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800330 ObjectNode payload = payload(event);
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800331 String type = string(payload, "class", "unknown");
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800332 long sid = number(event, "sid");
333
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800334 if (type.equals("device")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800335 sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800336 } else if (type.equals("host")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800337 sendMessage(hostDetails(hostId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800338 }
339 }
340
Thomas Vachuska9edca302014-11-22 17:06:42 -0800341
Thomas Vachuska4830d392014-11-09 17:09:56 -0800342 // Creates host-to-host intent.
343 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800344 ObjectNode payload = payload(event);
345 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800346 // TODO: add protection against device ids and non-existent hosts.
347 HostId one = hostId(string(payload, "one"));
348 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800349
Thomas Vachuska9edca302014-11-22 17:06:42 -0800350 HostToHostIntent intent =
351 new HostToHostIntent(appId, one, two,
352 DefaultTrafficSelector.builder().build(),
353 DefaultTrafficTreatment.builder().build());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800354
Thomas Vachuska9edca302014-11-22 17:06:42 -0800355 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800356 startMonitoringIntent(event, intent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800357 }
358
Thomas Vachuska9edca302014-11-22 17:06:42 -0800359 // Creates multi-source-to-single-dest intent.
360 private void createMultiSourceIntent(ObjectNode event) {
361 ObjectNode payload = payload(event);
362 long id = number(event, "sid");
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.builder().build();
374
375 MultiPointToSinglePointIntent intent =
376 new MultiPointToSinglePointIntent(appId, selector, treatment,
377 ingressPoints, dstHost.location());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800378
Thomas Vachuska9edca302014-11-22 17:06:42 -0800379 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800380 startMonitoringIntent(event, intent);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800381 }
382
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800383
384 private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
385 selectedHosts = new HashSet<>();
386 selectedDevices = new HashSet<>();
387 selectedIntents = new ArrayList<>();
388 selectedIntents.add(intent);
389 currentIntentIndex = -1;
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800390 requestAnotherRelatedIntent(event, +1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800391 requestSelectedIntentTraffic(event);
392 }
393
394
Thomas Vachuska9edca302014-11-22 17:06:42 -0800395 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
396 Set<ConnectPoint> points = new HashSet<>();
397 for (HostId hostId : hostIds) {
398 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800399 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800400 return points;
401 }
402
403 private HostLocation getHostLocation(HostId hostId) {
404 return hostService.getHost(hostId).location();
405 }
406
407 // Produces a list of host ids from the specified JSON array.
408 private Set<HostId> getHostIds(ArrayNode ids) {
409 Set<HostId> hostIds = new HashSet<>();
410 for (JsonNode id : ids) {
411 hostIds.add(hostId(id.asText()));
412 }
413 return hostIds;
414 }
415
416
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800417 private synchronized long startTrafficMonitoring(ObjectNode event) {
418 stopTrafficMonitoring();
Thomas Vachuska9edca302014-11-22 17:06:42 -0800419 trafficEvent = event;
420 trafficTask = new TrafficMonitor();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800421 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800422 return number(event, "sid");
423 }
424
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800425 private synchronized void stopTrafficMonitoring() {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800426 if (trafficTask != null) {
427 trafficTask.cancel();
428 trafficTask = null;
429 trafficEvent = null;
430 }
431 }
432
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800433 // Subscribes for host traffic messages.
434 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800435 long sid = startTrafficMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800436 sendMessage(trafficSummaryMessage(sid));
437 }
438
Thomas Vachuska29617e52014-11-20 03:17:46 -0800439 private void requestDeviceLinkFlows(ObjectNode event) {
440 ObjectNode payload = payload(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800441 long sid = startTrafficMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800442
443 // Get the set of selected hosts and their intents.
444 ArrayNode ids = (ArrayNode) payload.path("ids");
445 Set<Host> hosts = new HashSet<>();
446 Set<Device> devices = getDevices(ids);
447
448 // If there is a hover node, include it in the hosts and find intents.
449 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800450 if (!isNullOrEmpty(hover)) {
451 addHover(hosts, devices, hover);
452 }
453 sendMessage(flowSummaryMessage(sid, devices));
454 }
455
456
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800457 // Requests related intents message.
458 private synchronized void requestRelatedIntents(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800459 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800460 if (!payload.has("ids")) {
461 return;
462 }
463
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800464 long sid = number(event, "sid");
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800465
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800466 // Cancel any other traffic monitoring mode.
467 stopTrafficMonitoring();
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800468
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800469 // Get the set of selected hosts and their intents.
470 ArrayNode ids = (ArrayNode) payload.path("ids");
471 selectedHosts = getHosts(ids);
472 selectedDevices = getDevices(ids);
473 selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
474 intentService.getIntents());
475 currentIntentIndex = -1;
476
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800477 if (haveSelectedIntents()) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800478 // Send a message to highlight all links of all monitored intents.
479 sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800480 }
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800481
482 // FIXME: Re-introduce one the client click vs hover gesture stuff is sorted out.
483// String hover = string(payload, "hover");
484// if (!isNullOrEmpty(hover)) {
485// // If there is a hover node, include it in the selection and find intents.
486// processHoverExtendedSelection(sid, hover);
487// }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800488 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800489
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800490 private boolean haveSelectedIntents() {
491 return selectedIntents != null && !selectedIntents.isEmpty();
492 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800493
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800494 // Processes the selection extended with hovered item to segregate items
495 // into primary (those including the hover) vs secondary highlights.
496 private void processHoverExtendedSelection(long sid, String hover) {
497 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
498 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
499 addHover(hoverSelHosts, hoverSelDevices, hover);
500
501 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
502 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
503 selectedIntents);
504 Set<Intent> secondary = new HashSet<>(selectedIntents);
505 secondary.removeAll(primary);
506
507 // Send a message to highlight all links of all monitored intents.
508 sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
509 new TrafficClass("secondary", secondary)));
510 }
511
512 // Requests next or previous related intent.
513 private void requestAnotherRelatedIntent(ObjectNode event, int offset) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800514 if (haveSelectedIntents()) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800515 currentIntentIndex = currentIntentIndex + offset;
516 if (currentIntentIndex < 0) {
517 currentIntentIndex = selectedIntents.size() - 1;
518 } else if (currentIntentIndex >= selectedIntents.size()) {
519 currentIntentIndex = 0;
520 }
521 sendSelectedIntent(event);
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800522 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800523 }
524
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800525 // Sends traffic information on the related intents with the currently
526 // selected intent highlighted.
527 private void sendSelectedIntent(ObjectNode event) {
528 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
529 log.info("Requested next intent {}", selectedIntent.id());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800530
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800531 Set<Intent> primary = new HashSet<>();
532 primary.add(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800533
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800534 Set<Intent> secondary = new HashSet<>(selectedIntents);
535 secondary.remove(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800536
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800537 // Send a message to highlight all links of the selected intent.
538 sendMessage(trafficMessage(number(event, "sid"),
539 new TrafficClass("primary", primary),
540 new TrafficClass("secondary", secondary)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800541 }
542
543 // Requests monitoring of traffic for the selected intent.
544 private void requestSelectedIntentTraffic(ObjectNode event) {
545 if (haveSelectedIntents()) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800546 if (currentIntentIndex < 0) {
547 currentIntentIndex = 0;
548 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800549 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
550 log.info("Requested traffic for selected {}", selectedIntent.id());
551
552 Set<Intent> primary = new HashSet<>();
553 primary.add(selectedIntent);
554
555 // Send a message to highlight all links of the selected intent.
556 sendMessage(trafficMessage(number(event, "sid"),
557 new TrafficClass("primary", primary, true)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800558 }
559 }
560
561 // Cancels sending traffic messages.
562 private void cancelTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800563 selectedIntents = null;
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800564 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800565 stopTrafficMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800566 }
567
Thomas Vachuska47635c62014-11-22 01:21:36 -0800568
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800569 private synchronized long startSummaryMonitoring(ObjectNode event) {
570 stopSummaryMonitoring();
571 summaryEvent = event;
572 summaryTask = new SummaryMonitor();
573 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
574 return number(event, "sid");
575 }
576
577 private synchronized void stopSummaryMonitoring() {
578 if (summaryEvent != null) {
579 summaryTask.cancel();
580 summaryTask = null;
581 summaryEvent = null;
582 }
583 }
584
Thomas Vachuska47635c62014-11-22 01:21:36 -0800585 // Subscribes for summary messages.
586 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800587 sendMessage(summmaryMessage(number(event, "sid")));
588 }
589
Thomas Vachuska47635c62014-11-22 01:21:36 -0800590
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800591 // Forces mastership role rebalancing.
592 private void equalizeMasters(ObjectNode event) {
593 directory.get(MastershipAdminService.class).balanceRoles();
594 }
595
596
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800597 // Adds all internal listeners.
598 private void addListeners() {
599 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800600 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800601 deviceService.addListener(deviceListener);
602 linkService.addListener(linkListener);
603 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800604 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800605 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800606 }
607
608 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800609 private synchronized void removeListeners() {
610 if (!listenersRemoved) {
611 listenersRemoved = true;
612 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800613 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800614 deviceService.removeListener(deviceListener);
615 linkService.removeListener(linkListener);
616 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800617 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800618 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800619 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800620 }
621
622 // Cluster event listener.
623 private class InternalClusterListener implements ClusterEventListener {
624 @Override
625 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800626 sendMessage(instanceMessage(event, null));
627 }
628 }
629
630 // Mastership change listener
631 private class InternalMastershipListener implements MastershipListener {
632 @Override
633 public void event(MastershipEvent event) {
634 sendAllInstances("updateInstance");
635 Device device = deviceService.getDevice(event.subject());
636 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800637 }
638 }
639
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800640 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800641 private class InternalDeviceListener implements DeviceListener {
642 @Override
643 public void event(DeviceEvent event) {
644 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800645 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800646 }
647 }
648
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800649 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800650 private class InternalLinkListener implements LinkListener {
651 @Override
652 public void event(LinkEvent event) {
653 sendMessage(linkMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800654 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800655 }
656 }
657
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800658 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800659 private class InternalHostListener implements HostListener {
660 @Override
661 public void event(HostEvent event) {
662 sendMessage(hostMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800663 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800664 }
665 }
666
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800667 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800668 private class InternalIntentListener implements IntentListener {
669 @Override
670 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800671 if (trafficEvent != null) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800672 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800673 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800674 eventAccummulator.add(event);
675 }
676 }
677
678 // Intent event listener.
679 private class InternalFlowListener implements FlowRuleListener {
680 @Override
681 public void event(FlowRuleEvent event) {
682 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800683 }
684 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800685
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800686 // Periodic update of the traffic information
Thomas Vachuska47635c62014-11-22 01:21:36 -0800687 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800688 @Override
689 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800690 try {
691 if (trafficEvent != null) {
692 String type = string(trafficEvent, "event", "unknown");
693 if (type.equals("requestAllTraffic")) {
694 requestAllTraffic(trafficEvent);
695 } else if (type.equals("requestDeviceLinkFlows")) {
696 requestDeviceLinkFlows(trafficEvent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800697 } else if (type.equals("requestSelectedIntentTraffic")) {
698 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800699 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800700 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800701 } catch (Exception e) {
702 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800703 log.debug("Boom!", e);
Thomas Vachuska22e34922014-11-14 00:40:55 -0800704 }
705 }
706 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800707
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800708 // Periodic update of the summary information
709 private class SummaryMonitor extends TimerTask {
710 @Override
711 public void run() {
712 try {
713 if (summaryEvent != null) {
714 requestSummary(summaryEvent);
715 }
716 } catch (Exception e) {
717 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800718 log.debug("Boom!", e);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800719 }
720 }
721 }
722
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800723 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800724 private class InternalEventAccummulator extends AbstractAccumulator<Event> {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800725 protected InternalEventAccummulator() {
726 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
727 }
728
Thomas Vachuska47635c62014-11-22 01:21:36 -0800729 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800730 public void processItems(List<Event> items) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800731 try {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800732 if (summaryEvent != null) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800733 sendMessage(summmaryMessage(0));
734 }
735 } catch (Exception e) {
736 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800737 log.debug("Boom!", e);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800738 }
739 }
740 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800741}
742