blob: bf2a2ef8b08a443c3b5345bb09a89560f8c0713e [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;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080057import org.onlab.packet.Ethernet;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080058
59import java.io.IOException;
Thomas Vachuska47635c62014-11-22 01:21:36 -080060import java.util.ArrayList;
61import java.util.Collections;
62import java.util.Comparator;
Thomas Vachuska29617e52014-11-20 03:17:46 -080063import java.util.HashSet;
Thomas Vachuska47635c62014-11-22 01:21:36 -080064import java.util.List;
Thomas Vachuskadea45ff2014-11-12 18:35:46 -080065import java.util.Set;
Thomas Vachuska22e34922014-11-14 00:40:55 -080066import java.util.Timer;
67import java.util.TimerTask;
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 Vachuska7c27ad72014-11-14 16:20:10 -080081public class TopologyViewWebSocket
82 extends TopologyViewMessages
Thomas Vachuskaba5621e2014-11-12 01:47:19 -080083 implements WebSocket.OnTextMessage, WebSocket.OnControl {
84
85 private static final long MAX_AGE_MS = 15000;
86
87 private static final byte PING = 0x9;
88 private static final byte PONG = 0xA;
89 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
Thomas Vachuska7d638d32014-11-07 10:24:43 -080090
Brian O'Connorabafb502014-12-02 22:26:20 -080091 private static final String APP_ID = "org.onosproject.gui";
Thomas Vachuska4830d392014-11-09 17:09:56 -080092
alshabib0ad84ec2014-12-03 22:48:01 -080093 private static final long TRAFFIC_FREQUENCY = 5000;
Thomas Vachuska164fa5c2014-12-02 21:59:41 -080094 private static final long SUMMARY_FREQUENCY = 30000;
Thomas Vachuska22e34922014-11-14 00:40:55 -080095
Thomas Vachuska47635c62014-11-22 01:21:36 -080096 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
97 new Comparator<ControllerNode>() {
98 @Override
99 public int compare(ControllerNode o1, ControllerNode o2) {
100 return o1.id().toString().compareTo(o2.id().toString());
101 }
102 };
103
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800104
105 private final Timer timer = new Timer("topology-view");
106
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800107 private static final int MAX_EVENTS = 1000;
108 private static final int MAX_BATCH_MS = 5000;
109 private static final int MAX_IDLE_MS = 1000;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800110
Thomas Vachuska4830d392014-11-09 17:09:56 -0800111 private final ApplicationId appId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800112
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800113 private Connection connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800114 private FrameConnection control;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800115
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800116 private final ClusterEventListener clusterListener = new InternalClusterListener();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800117 private final MastershipListener mastershipListener = new InternalMastershipListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800118 private final DeviceListener deviceListener = new InternalDeviceListener();
119 private final LinkListener linkListener = new InternalLinkListener();
120 private final HostListener hostListener = new InternalHostListener();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800121 private final IntentListener intentListener = new InternalIntentListener();
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800122 private final FlowRuleListener flowListener = new InternalFlowListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800123
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800124 private final EventAccumulator eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska47635c62014-11-22 01:21:36 -0800125
126 private TimerTask trafficTask;
127 private ObjectNode trafficEvent;
128
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800129 private TimerTask summaryTask;
130 private ObjectNode summaryEvent;
131
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800132 private long lastActive = System.currentTimeMillis();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800133 private boolean listenersRemoved = false;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800134
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800135 private TopologyViewIntentFilter intentFilter;
136
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800137 // Current selection context
138 private Set<Host> selectedHosts;
139 private Set<Device> selectedDevices;
140 private List<Intent> selectedIntents;
141 private int currentIntentIndex = -1;
142
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800143 /**
144 * Creates a new web-socket for serving data to GUI topology view.
145 *
146 * @param directory service directory
147 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800148 public TopologyViewWebSocket(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800149 super(directory);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800150 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
Thomas Vachuska9edca302014-11-22 17:06:42 -0800151 hostService, linkService);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800152 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800153 }
154
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800155 /**
156 * Issues a close on the connection.
157 */
158 synchronized void close() {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800159 removeListeners();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800160 if (connection.isOpen()) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800161 connection.close();
162 }
163 }
164
165 /**
166 * Indicates if this connection is idle.
Thomas Vachuska3266abf2014-11-13 09:28:46 -0800167 *
168 * @return true if idle or closed
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800169 */
170 synchronized boolean isIdle() {
171 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
172 if (idle || !connection.isOpen()) {
173 return true;
174 }
175 try {
176 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
177 } catch (IOException e) {
178 log.warn("Unable to send ping message due to: ", e);
179 }
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.
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800270 private 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()
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800372 .matchEthType(Ethernet.TYPE_IPV4)
Thomas Vachuska9edca302014-11-22 17:06:42 -0800373 .matchEthDst(dstHost.mac()).build();
374 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
375
376 MultiPointToSinglePointIntent intent =
377 new MultiPointToSinglePointIntent(appId, selector, treatment,
378 ingressPoints, dstHost.location());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800379
Thomas Vachuska9edca302014-11-22 17:06:42 -0800380 intentService.submit(intent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800381 startMonitoringIntent(event, intent);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800382 }
383
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800384
385 private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
386 selectedHosts = new HashSet<>();
387 selectedDevices = new HashSet<>();
388 selectedIntents = new ArrayList<>();
389 selectedIntents.add(intent);
390 currentIntentIndex = -1;
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800391 requestAnotherRelatedIntent(event, +1);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800392 requestSelectedIntentTraffic(event);
393 }
394
395
Thomas Vachuska9edca302014-11-22 17:06:42 -0800396 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
397 Set<ConnectPoint> points = new HashSet<>();
398 for (HostId hostId : hostIds) {
399 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800400 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800401 return points;
402 }
403
404 private HostLocation getHostLocation(HostId hostId) {
405 return hostService.getHost(hostId).location();
406 }
407
408 // Produces a list of host ids from the specified JSON array.
409 private Set<HostId> getHostIds(ArrayNode ids) {
410 Set<HostId> hostIds = new HashSet<>();
411 for (JsonNode id : ids) {
412 hostIds.add(hostId(id.asText()));
413 }
414 return hostIds;
415 }
416
417
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800418 private synchronized long startTrafficMonitoring(ObjectNode event) {
419 stopTrafficMonitoring();
Thomas Vachuska9edca302014-11-22 17:06:42 -0800420 trafficEvent = event;
421 trafficTask = new TrafficMonitor();
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800422 timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800423 return number(event, "sid");
424 }
425
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800426 private synchronized void stopTrafficMonitoring() {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800427 if (trafficTask != null) {
428 trafficTask.cancel();
429 trafficTask = null;
430 trafficEvent = null;
431 }
432 }
433
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800434 // Subscribes for host traffic messages.
435 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800436 long sid = startTrafficMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800437 sendMessage(trafficSummaryMessage(sid));
438 }
439
Thomas Vachuska29617e52014-11-20 03:17:46 -0800440 private void requestDeviceLinkFlows(ObjectNode event) {
441 ObjectNode payload = payload(event);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800442 long sid = startTrafficMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800443
444 // Get the set of selected hosts and their intents.
445 ArrayNode ids = (ArrayNode) payload.path("ids");
446 Set<Host> hosts = new HashSet<>();
447 Set<Device> devices = getDevices(ids);
448
449 // If there is a hover node, include it in the hosts and find intents.
450 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800451 if (!isNullOrEmpty(hover)) {
452 addHover(hosts, devices, hover);
453 }
454 sendMessage(flowSummaryMessage(sid, devices));
455 }
456
457
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800458 // Requests related intents message.
459 private synchronized void requestRelatedIntents(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800460 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800461 if (!payload.has("ids")) {
462 return;
463 }
464
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800465 long sid = number(event, "sid");
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800466
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800467 // Cancel any other traffic monitoring mode.
468 stopTrafficMonitoring();
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800469
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800470 // Get the set of selected hosts and their intents.
471 ArrayNode ids = (ArrayNode) payload.path("ids");
472 selectedHosts = getHosts(ids);
473 selectedDevices = getDevices(ids);
474 selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
475 intentService.getIntents());
476 currentIntentIndex = -1;
477
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800478 if (haveSelectedIntents()) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800479 // Send a message to highlight all links of all monitored intents.
480 sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800481 }
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800482
483 // FIXME: Re-introduce one the client click vs hover gesture stuff is sorted out.
484// String hover = string(payload, "hover");
485// if (!isNullOrEmpty(hover)) {
486// // If there is a hover node, include it in the selection and find intents.
487// processHoverExtendedSelection(sid, hover);
488// }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800489 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800490
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800491 private boolean haveSelectedIntents() {
492 return selectedIntents != null && !selectedIntents.isEmpty();
493 }
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800494
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800495 // Processes the selection extended with hovered item to segregate items
496 // into primary (those including the hover) vs secondary highlights.
497 private void processHoverExtendedSelection(long sid, String hover) {
498 Set<Host> hoverSelHosts = new HashSet<>(selectedHosts);
499 Set<Device> hoverSelDevices = new HashSet<>(selectedDevices);
500 addHover(hoverSelHosts, hoverSelDevices, hover);
501
502 List<Intent> primary = selectedIntents == null ? new ArrayList<>() :
503 intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices,
504 selectedIntents);
505 Set<Intent> secondary = new HashSet<>(selectedIntents);
506 secondary.removeAll(primary);
507
508 // Send a message to highlight all links of all monitored intents.
509 sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
510 new TrafficClass("secondary", secondary)));
511 }
512
513 // Requests next or previous related intent.
514 private void requestAnotherRelatedIntent(ObjectNode event, int offset) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800515 if (haveSelectedIntents()) {
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800516 currentIntentIndex = currentIntentIndex + offset;
517 if (currentIntentIndex < 0) {
518 currentIntentIndex = selectedIntents.size() - 1;
519 } else if (currentIntentIndex >= selectedIntents.size()) {
520 currentIntentIndex = 0;
521 }
522 sendSelectedIntent(event);
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800523 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800524 }
525
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800526 // Sends traffic information on the related intents with the currently
527 // selected intent highlighted.
528 private void sendSelectedIntent(ObjectNode event) {
529 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
530 log.info("Requested next intent {}", selectedIntent.id());
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800531
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800532 Set<Intent> primary = new HashSet<>();
533 primary.add(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800534
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800535 Set<Intent> secondary = new HashSet<>(selectedIntents);
536 secondary.remove(selectedIntent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800537
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800538 // Send a message to highlight all links of the selected intent.
539 sendMessage(trafficMessage(number(event, "sid"),
540 new TrafficClass("primary", primary),
541 new TrafficClass("secondary", secondary)));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800542 }
543
544 // Requests monitoring of traffic for the selected intent.
545 private void requestSelectedIntentTraffic(ObjectNode event) {
546 if (haveSelectedIntents()) {
Thomas Vachuska6810b7882014-12-03 00:37:25 -0800547 if (currentIntentIndex < 0) {
548 currentIntentIndex = 0;
549 }
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800550 Intent selectedIntent = selectedIntents.get(currentIntentIndex);
551 log.info("Requested traffic for selected {}", selectedIntent.id());
552
553 Set<Intent> primary = new HashSet<>();
554 primary.add(selectedIntent);
555
556 // Send a message to highlight all links of the selected intent.
557 sendMessage(trafficMessage(number(event, "sid"),
558 new TrafficClass("primary", primary, true)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800559 }
560 }
561
562 // Cancels sending traffic messages.
563 private void cancelTraffic(ObjectNode event) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800564 selectedIntents = null;
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800565 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800566 stopTrafficMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800567 }
568
Thomas Vachuska47635c62014-11-22 01:21:36 -0800569
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800570 private synchronized long startSummaryMonitoring(ObjectNode event) {
571 stopSummaryMonitoring();
572 summaryEvent = event;
573 summaryTask = new SummaryMonitor();
574 timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
575 return number(event, "sid");
576 }
577
578 private synchronized void stopSummaryMonitoring() {
579 if (summaryEvent != null) {
580 summaryTask.cancel();
581 summaryTask = null;
582 summaryEvent = null;
583 }
584 }
585
Thomas Vachuska47635c62014-11-22 01:21:36 -0800586 // Subscribes for summary messages.
587 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800588 sendMessage(summmaryMessage(number(event, "sid")));
589 }
590
Thomas Vachuska47635c62014-11-22 01:21:36 -0800591
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800592 // Forces mastership role rebalancing.
593 private void equalizeMasters(ObjectNode event) {
594 directory.get(MastershipAdminService.class).balanceRoles();
595 }
596
597
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800598 // Adds all internal listeners.
599 private void addListeners() {
600 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800601 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800602 deviceService.addListener(deviceListener);
603 linkService.addListener(linkListener);
604 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800605 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800606 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800607 }
608
609 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800610 private synchronized void removeListeners() {
611 if (!listenersRemoved) {
612 listenersRemoved = true;
613 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800614 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800615 deviceService.removeListener(deviceListener);
616 linkService.removeListener(linkListener);
617 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800618 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800619 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800620 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800621 }
622
623 // Cluster event listener.
624 private class InternalClusterListener implements ClusterEventListener {
625 @Override
626 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800627 sendMessage(instanceMessage(event, null));
628 }
629 }
630
631 // Mastership change listener
632 private class InternalMastershipListener implements MastershipListener {
633 @Override
634 public void event(MastershipEvent event) {
635 sendAllInstances("updateInstance");
636 Device device = deviceService.getDevice(event.subject());
637 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800638 }
639 }
640
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800641 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800642 private class InternalDeviceListener implements DeviceListener {
643 @Override
644 public void event(DeviceEvent event) {
645 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800646 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800647 }
648 }
649
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800650 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800651 private class InternalLinkListener implements LinkListener {
652 @Override
653 public void event(LinkEvent event) {
654 sendMessage(linkMessage(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 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800660 private class InternalHostListener implements HostListener {
661 @Override
662 public void event(HostEvent event) {
663 sendMessage(hostMessage(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 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800669 private class InternalIntentListener implements IntentListener {
670 @Override
671 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800672 if (trafficEvent != null) {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800673 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800674 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800675 eventAccummulator.add(event);
676 }
677 }
678
679 // Intent event listener.
680 private class InternalFlowListener implements FlowRuleListener {
681 @Override
682 public void event(FlowRuleEvent event) {
683 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800684 }
685 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800686
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800687 // Periodic update of the traffic information
Thomas Vachuska47635c62014-11-22 01:21:36 -0800688 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800689 @Override
690 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800691 try {
692 if (trafficEvent != null) {
693 String type = string(trafficEvent, "event", "unknown");
694 if (type.equals("requestAllTraffic")) {
695 requestAllTraffic(trafficEvent);
696 } else if (type.equals("requestDeviceLinkFlows")) {
697 requestDeviceLinkFlows(trafficEvent);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800698 } else if (type.equals("requestSelectedIntentTraffic")) {
699 requestSelectedIntentTraffic(trafficEvent);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800700 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800701 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800702 } catch (Exception e) {
703 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800704 log.debug("Boom!", e);
Thomas Vachuska22e34922014-11-14 00:40:55 -0800705 }
706 }
707 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800708
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800709 // Periodic update of the summary information
710 private class SummaryMonitor extends TimerTask {
711 @Override
712 public void run() {
713 try {
714 if (summaryEvent != null) {
715 requestSummary(summaryEvent);
716 }
717 } catch (Exception e) {
718 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800719 log.debug("Boom!", e);
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800720 }
721 }
722 }
723
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800724 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800725 private class InternalEventAccummulator extends AbstractEventAccumulator {
726 protected InternalEventAccummulator() {
727 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
728 }
729
Thomas Vachuska47635c62014-11-22 01:21:36 -0800730 @Override
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800731 public void processEvents(List<Event> events) {
732 try {
Thomas Vachuska164fa5c2014-12-02 21:59:41 -0800733 if (summaryEvent != null) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800734 sendMessage(summmaryMessage(0));
735 }
736 } catch (Exception e) {
737 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuskab7e40642014-12-03 11:16:06 -0800738 log.debug("Boom!", e);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800739 }
740 }
741 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800742}
743