blob: 0caf6a0ec07955fffbf8275475b63c0eafde1c05 [file] [log] [blame]
Thomas Vachuska7d638d32014-11-07 10:24:43 -08001/*
2 * Copyright 2014 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 */
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;
Brian O'Connorabafb502014-12-02 22:26:20 -080022import org.onosproject.cluster.ClusterEvent;
23import org.onosproject.cluster.ClusterEventListener;
24import org.onosproject.cluster.ControllerNode;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.event.AbstractEventAccumulator;
28import org.onosproject.event.Event;
29import org.onosproject.event.EventAccumulator;
30import org.onosproject.mastership.MastershipAdminService;
31import org.onosproject.mastership.MastershipEvent;
32import org.onosproject.mastership.MastershipListener;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.Device;
35import org.onosproject.net.Host;
36import org.onosproject.net.HostId;
37import org.onosproject.net.HostLocation;
38import org.onosproject.net.Link;
39import org.onosproject.net.device.DeviceEvent;
40import org.onosproject.net.device.DeviceListener;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowRuleEvent;
44import org.onosproject.net.flow.FlowRuleListener;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.host.HostEvent;
48import org.onosproject.net.host.HostListener;
49import org.onosproject.net.intent.HostToHostIntent;
50import org.onosproject.net.intent.Intent;
51import org.onosproject.net.intent.IntentEvent;
52import org.onosproject.net.intent.IntentListener;
53import org.onosproject.net.intent.MultiPointToSinglePointIntent;
54import org.onosproject.net.link.LinkEvent;
55import org.onosproject.net.link.LinkListener;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080056import org.onlab.osgi.ServiceDirectory;
57
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 Vachuska5bde31f2014-11-25 15:29:18 -0800123 private final EventAccumulator 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;
171 if (idle || !connection.isOpen()) {
172 return true;
173 }
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 }
179 return false;
180 }
181
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800182 @Override
183 public void onOpen(Connection connection) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800184 log.info("GUI client connected");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800185 this.connection = connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800186 this.control = (FrameConnection) connection;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800187 addListeners();
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800188
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800189 sendAllInstances(null);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800190 sendAllDevices();
191 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800192 sendAllHosts();
193 }
194
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800195 @Override
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800196 public synchronized void onClose(int closeCode, String message) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800197 removeListeners();
Thomas Vachuska22e34922014-11-14 00:40:55 -0800198 timer.cancel();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800199 log.info("GUI client disconnected");
200 }
201
202 @Override
203 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
204 lastActive = System.currentTimeMillis();
205 return true;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800206 }
207
208 @Override
209 public void onMessage(String data) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800210 lastActive = System.currentTimeMillis();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800211 try {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800212 processMessage((ObjectNode) mapper.reader().readTree(data));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800213 } catch (Exception e) {
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800214 log.warn("Unable to parse GUI request {} due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800215 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800216 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800217 }
218
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800219 // Processes the specified event.
220 private void processMessage(ObjectNode event) {
221 String type = string(event, "event", "unknown");
222 if (type.equals("requestDetails")) {
223 requestDetails(event);
224 } else if (type.equals("updateMeta")) {
225 updateMetaUi(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800226
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800227 } else if (type.equals("addHostIntent")) {
228 createHostIntent(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800229 } else if (type.equals("addMultiSourceIntent")) {
230 createMultiSourceIntent(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800231
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800232 } else if (type.equals("requestRelatedIntents")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800233 stopTrafficMonitoring();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800234 requestRelatedIntents(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800235
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800236 } else if (type.equals("requestNextRelatedIntent")) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800237 stopTrafficMonitoring();
238 requestAnotherRelatedIntent(event, +1);
239 } else if (type.equals("requestPrevRelatedIntent")) {
240 stopTrafficMonitoring();
241 requestAnotherRelatedIntent(event, -1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800242 } else if (type.equals("requestSelectedIntentTraffic")) {
243 requestSelectedIntentTraffic(event);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800244 startTrafficMonitoring(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800245
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800246 } else if (type.equals("requestAllTraffic")) {
247 requestAllTraffic(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800248 startTrafficMonitoring(event);
249
Thomas Vachuska29617e52014-11-20 03:17:46 -0800250 } else if (type.equals("requestDeviceLinkFlows")) {
251 requestDeviceLinkFlows(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800252 startTrafficMonitoring(event);
253
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800254 } else if (type.equals("cancelTraffic")) {
255 cancelTraffic(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800256
257 } else if (type.equals("requestSummary")) {
258 requestSummary(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800259 startSummaryMonitoring(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800260 } else if (type.equals("cancelSummary")) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800261 stopSummaryMonitoring();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800262
263 } else if (type.equals("equalizeMasters")) {
264 equalizeMasters(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800265 }
266 }
267
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800268 // Sends the specified data to the client.
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800269 private synchronized void sendMessage(ObjectNode data) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800270 try {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800271 if (connection.isOpen()) {
272 connection.sendMessage(data.toString());
273 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800274 } catch (IOException e) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800275 log.warn("Unable to send message {} to GUI due to {}", data, e);
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800276 log.debug("Boom!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800277 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800278 }
279
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800280 // Sends all controller nodes to the client as node-added messages.
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800281 private void sendAllInstances(String messageType) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800282 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
283 Collections.sort(nodes, NODE_COMPARATOR);
284 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800285 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
286 messageType));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800287 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800288 }
289
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800290 // Sends all devices to the client as device-added messages.
291 private void sendAllDevices() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800292 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800293 for (Device device : deviceService.getDevices()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800294 if (device.type() == Device.Type.ROADM) {
295 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
296 }
297 }
298 for (Device device : deviceService.getDevices()) {
299 if (device.type() != Device.Type.ROADM) {
300 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
301 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800302 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800303 }
304
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800305 // Sends all links to the client as link-added messages.
306 private void sendAllLinks() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800307 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800308 for (Link link : linkService.getLinks()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800309 if (link.type() == Link.Type.OPTICAL) {
310 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
311 }
312 }
313 for (Link link : linkService.getLinks()) {
314 if (link.type() != Link.Type.OPTICAL) {
315 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
316 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800317 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800318 }
319
320 // Sends all hosts to the client as host-added messages.
321 private void sendAllHosts() {
322 for (Host host : hostService.getHosts()) {
323 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
324 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800325 }
326
327 // Sends back device or host details.
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800328 private void requestDetails(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800329 ObjectNode payload = payload(event);
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800330 String type = string(payload, "class", "unknown");
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800331 long sid = number(event, "sid");
332
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800333 if (type.equals("device")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800334 sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800335 } else if (type.equals("host")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800336 sendMessage(hostDetails(hostId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800337 }
338 }
339
Thomas Vachuska9edca302014-11-22 17:06:42 -0800340
Thomas Vachuska4830d392014-11-09 17:09:56 -0800341 // Creates host-to-host intent.
342 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800343 ObjectNode payload = payload(event);
344 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800345 // TODO: add protection against device ids and non-existent hosts.
346 HostId one = hostId(string(payload, "one"));
347 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800348
Thomas Vachuska9edca302014-11-22 17:06:42 -0800349 HostToHostIntent intent =
350 new HostToHostIntent(appId, one, two,
351 DefaultTrafficSelector.builder().build(),
352 DefaultTrafficTreatment.builder().build());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800353
Thomas Vachuska9edca302014-11-22 17:06:42 -0800354 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800355 startMonitoringIntent(event, intent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800356 }
357
Thomas Vachuska9edca302014-11-22 17:06:42 -0800358 // Creates multi-source-to-single-dest intent.
359 private void createMultiSourceIntent(ObjectNode event) {
360 ObjectNode payload = payload(event);
361 long id = number(event, "sid");
362 // TODO: add protection against device ids and non-existent hosts.
363 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
364 HostId dst = hostId(string(payload, "dst"));
365 Host dstHost = hostService.getHost(dst);
366
367 Set<ConnectPoint> ingressPoints = getHostLocations(src);
368
369 // FIXME: clearly, this is not enough
370 TrafficSelector selector = DefaultTrafficSelector.builder()
371 .matchEthDst(dstHost.mac()).build();
372 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
373
374 MultiPointToSinglePointIntent intent =
375 new MultiPointToSinglePointIntent(appId, selector, treatment,
376 ingressPoints, dstHost.location());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800377
Thomas Vachuska9edca302014-11-22 17:06:42 -0800378 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800379 startMonitoringIntent(event, intent);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800380 }
381
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800382
383 private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
384 selectedHosts = new HashSet<>();
385 selectedDevices = new HashSet<>();
386 selectedIntents = new ArrayList<>();
387 selectedIntents.add(intent);
388 currentIntentIndex = -1;
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800389 requestAnotherRelatedIntent(event, +1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800390 requestSelectedIntentTraffic(event);
391 }
392
393
Thomas Vachuska9edca302014-11-22 17:06:42 -0800394 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
395 Set<ConnectPoint> points = new HashSet<>();
396 for (HostId hostId : hostIds) {
397 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800398 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800399 return points;
400 }
401
402 private HostLocation getHostLocation(HostId hostId) {
403 return hostService.getHost(hostId).location();
404 }
405
406 // Produces a list of host ids from the specified JSON array.
407 private Set<HostId> getHostIds(ArrayNode ids) {
408 Set<HostId> hostIds = new HashSet<>();
409 for (JsonNode id : ids) {
410 hostIds.add(hostId(id.asText()));
411 }
412 return hostIds;
413 }
414
415
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800416 private synchronized long startTrafficMonitoring(ObjectNode event) {
417 stopTrafficMonitoring();
Thomas Vachuska9edca302014-11-22 17:06:42 -0800418 trafficEvent = event;
419 trafficTask = new TrafficMonitor();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800420 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800421 return number(event, "sid");
422 }
423
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800424 private synchronized void stopTrafficMonitoring() {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800425 if (trafficTask != null) {
426 trafficTask.cancel();
427 trafficTask = null;
428 trafficEvent = null;
429 }
430 }
431
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800432 // Subscribes for host traffic messages.
433 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800434 long sid = startTrafficMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800435 sendMessage(trafficSummaryMessage(sid));
436 }
437
Thomas Vachuska29617e52014-11-20 03:17:46 -0800438 private void requestDeviceLinkFlows(ObjectNode event) {
439 ObjectNode payload = payload(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800440 long sid = startTrafficMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800441
442 // Get the set of selected hosts and their intents.
443 ArrayNode ids = (ArrayNode) payload.path("ids");
444 Set<Host> hosts = new HashSet<>();
445 Set<Device> devices = getDevices(ids);
446
447 // If there is a hover node, include it in the hosts and find intents.
448 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800449 if (!isNullOrEmpty(hover)) {
450 addHover(hosts, devices, hover);
451 }
452 sendMessage(flowSummaryMessage(sid, devices));
453 }
454
455
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800456 // Requests related intents message.
457 private synchronized void requestRelatedIntents(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800458 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800459 if (!payload.has("ids")) {
460 return;
461 }
462
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800463 long sid = number(event, "sid");
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800464
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800465 // Cancel any other traffic monitoring mode.
466 stopTrafficMonitoring();
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800467
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800468 // Get the set of selected hosts and their intents.
469 ArrayNode ids = (ArrayNode) payload.path("ids");
470 selectedHosts = getHosts(ids);
471 selectedDevices = getDevices(ids);
472 selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
473 intentService.getIntents());
474 currentIntentIndex = -1;
475
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800476 if (haveSelectedIntents()) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800477 // Send a message to highlight all links of all monitored intents.
478 sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800479 }
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800480
481 // FIXME: Re-introduce one the client click vs hover gesture stuff is sorted out.
482// String hover = string(payload, "hover");
483// if (!isNullOrEmpty(hover)) {
484// // If there is a hover node, include it in the selection and find intents.
485// processHoverExtendedSelection(sid, hover);
486// }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800487 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800488
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800489 private boolean haveSelectedIntents() {
490 return selectedIntents != null && !selectedIntents.isEmpty();
491 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800492
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800493 // Processes the selection extended with hovered item to segregate items
494 // into primary (those including the hover) vs secondary highlights.
495 private void processHoverExtendedSelection(long sid, String hover) {
496 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
497 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
498 addHover(hoverSelHosts, hoverSelDevices, hover);
499
500 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
501 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
502 selectedIntents);
503 Set<Intent> secondary = new HashSet<>(selectedIntents);
504 secondary.removeAll(primary);
505
506 // Send a message to highlight all links of all monitored intents.
507 sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
508 new TrafficClass("secondary", secondary)));
509 }
510
511 // Requests next or previous related intent.
512 private void requestAnotherRelatedIntent(ObjectNode event, int offset) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800513 if (haveSelectedIntents()) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800514 currentIntentIndex = currentIntentIndex + offset;
515 if (currentIntentIndex < 0) {
516 currentIntentIndex = selectedIntents.size() - 1;
517 } else if (currentIntentIndex >= selectedIntents.size()) {
518 currentIntentIndex = 0;
519 }
520 sendSelectedIntent(event);
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800521 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800522 }
523
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800524 // Sends traffic information on the related intents with the currently
525 // selected intent highlighted.
526 private void sendSelectedIntent(ObjectNode event) {
527 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
528 log.info("Requested next intent {}", selectedIntent.id());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800529
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800530 Set<Intent> primary = new HashSet<>();
531 primary.add(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800532
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800533 Set<Intent> secondary = new HashSet<>(selectedIntents);
534 secondary.remove(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800535
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800536 // Send a message to highlight all links of the selected intent.
537 sendMessage(trafficMessage(number(event, "sid"),
538 new TrafficClass("primary", primary),
539 new TrafficClass("secondary", secondary)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800540 }
541
542 // Requests monitoring of traffic for the selected intent.
543 private void requestSelectedIntentTraffic(ObjectNode event) {
544 if (haveSelectedIntents()) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800545 if (currentIntentIndex < 0) {
546 currentIntentIndex = 0;
547 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800548 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
549 log.info("Requested traffic for selected {}", selectedIntent.id());
550
551 Set<Intent> primary = new HashSet<>();
552 primary.add(selectedIntent);
553
554 // Send a message to highlight all links of the selected intent.
555 sendMessage(trafficMessage(number(event, "sid"),
556 new TrafficClass("primary", primary, true)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800557 }
558 }
559
560 // Cancels sending traffic messages.
561 private void cancelTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800562 selectedIntents = null;
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800563 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800564 stopTrafficMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800565 }
566
Thomas Vachuska47635c62014-11-22 01:21:36 -0800567
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800568 private synchronized long startSummaryMonitoring(ObjectNode event) {
569 stopSummaryMonitoring();
570 summaryEvent = event;
571 summaryTask = new SummaryMonitor();
572 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
573 return number(event, "sid");
574 }
575
576 private synchronized void stopSummaryMonitoring() {
577 if (summaryEvent != null) {
578 summaryTask.cancel();
579 summaryTask = null;
580 summaryEvent = null;
581 }
582 }
583
Thomas Vachuska47635c62014-11-22 01:21:36 -0800584 // Subscribes for summary messages.
585 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800586 sendMessage(summmaryMessage(number(event, "sid")));
587 }
588
Thomas Vachuska47635c62014-11-22 01:21:36 -0800589
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800590 // Forces mastership role rebalancing.
591 private void equalizeMasters(ObjectNode event) {
592 directory.get(MastershipAdminService.class).balanceRoles();
593 }
594
595
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800596 // Adds all internal listeners.
597 private void addListeners() {
598 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800599 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800600 deviceService.addListener(deviceListener);
601 linkService.addListener(linkListener);
602 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800603 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800604 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800605 }
606
607 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800608 private synchronized void removeListeners() {
609 if (!listenersRemoved) {
610 listenersRemoved = true;
611 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800612 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800613 deviceService.removeListener(deviceListener);
614 linkService.removeListener(linkListener);
615 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800616 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800617 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800618 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800619 }
620
621 // Cluster event listener.
622 private class InternalClusterListener implements ClusterEventListener {
623 @Override
624 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800625 sendMessage(instanceMessage(event, null));
626 }
627 }
628
629 // Mastership change listener
630 private class InternalMastershipListener implements MastershipListener {
631 @Override
632 public void event(MastershipEvent event) {
633 sendAllInstances("updateInstance");
634 Device device = deviceService.getDevice(event.subject());
635 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800636 }
637 }
638
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800639 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800640 private class InternalDeviceListener implements DeviceListener {
641 @Override
642 public void event(DeviceEvent event) {
643 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800644 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800645 }
646 }
647
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800648 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800649 private class InternalLinkListener implements LinkListener {
650 @Override
651 public void event(LinkEvent event) {
652 sendMessage(linkMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800653 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800654 }
655 }
656
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800657 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800658 private class InternalHostListener implements HostListener {
659 @Override
660 public void event(HostEvent event) {
661 sendMessage(hostMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800662 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800663 }
664 }
665
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800666 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800667 private class InternalIntentListener implements IntentListener {
668 @Override
669 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800670 if (trafficEvent != null) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800671 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800672 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800673 eventAccummulator.add(event);
674 }
675 }
676
677 // Intent event listener.
678 private class InternalFlowListener implements FlowRuleListener {
679 @Override
680 public void event(FlowRuleEvent event) {
681 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800682 }
683 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800684
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800685 // Periodic update of the traffic information
Thomas Vachuska47635c62014-11-22 01:21:36 -0800686 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800687 @Override
688 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800689 try {
690 if (trafficEvent != null) {
691 String type = string(trafficEvent, "event", "unknown");
692 if (type.equals("requestAllTraffic")) {
693 requestAllTraffic(trafficEvent);
694 } else if (type.equals("requestDeviceLinkFlows")) {
695 requestDeviceLinkFlows(trafficEvent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800696 } else if (type.equals("requestSelectedIntentTraffic")) {
697 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800698 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800699 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800700 } catch (Exception e) {
701 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800702 log.debug("Boom!", e);
Thomas Vachuska22e34922014-11-14 00:40:55 -0800703 }
704 }
705 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800706
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800707 // Periodic update of the summary information
708 private class SummaryMonitor extends TimerTask {
709 @Override
710 public void run() {
711 try {
712 if (summaryEvent != null) {
713 requestSummary(summaryEvent);
714 }
715 } catch (Exception e) {
716 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800717 log.debug("Boom!", e);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800718 }
719 }
720 }
721
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800722 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800723 private class InternalEventAccummulator extends AbstractEventAccumulator {
724 protected InternalEventAccummulator() {
725 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
726 }
727
Thomas Vachuska47635c62014-11-22 01:21:36 -0800728 @Override
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800729 public void processEvents(List<Event> events) {
730 try {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800731 if (summaryEvent != null) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800732 sendMessage(summmaryMessage(0));
733 }
734 } catch (Exception e) {
735 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800736 log.debug("Boom!", e);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800737 }
738 }
739 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800740}
741