blob: 84e2e0284f8290613abf202d9b658e8cf7cd7094 [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 */
16package org.onlab.onos.gui;
17
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;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080022import org.onlab.onos.cluster.ClusterEvent;
23import org.onlab.onos.cluster.ClusterEventListener;
24import org.onlab.onos.cluster.ControllerNode;
Thomas Vachuska4830d392014-11-09 17:09:56 -080025import org.onlab.onos.core.ApplicationId;
26import org.onlab.onos.core.CoreService;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080027import org.onlab.onos.event.AbstractEventAccumulator;
28import org.onlab.onos.event.Event;
29import org.onlab.onos.event.EventAccumulator;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080030import org.onlab.onos.mastership.MastershipEvent;
31import org.onlab.onos.mastership.MastershipListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080032import org.onlab.onos.net.ConnectPoint;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080033import org.onlab.onos.net.Device;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080034import org.onlab.onos.net.Host;
35import org.onlab.onos.net.HostId;
Thomas Vachuska9edca302014-11-22 17:06:42 -080036import org.onlab.onos.net.HostLocation;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080037import org.onlab.onos.net.Link;
38import org.onlab.onos.net.device.DeviceEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080039import org.onlab.onos.net.device.DeviceListener;
Thomas Vachuska4830d392014-11-09 17:09:56 -080040import org.onlab.onos.net.flow.DefaultTrafficSelector;
41import org.onlab.onos.net.flow.DefaultTrafficTreatment;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080042import org.onlab.onos.net.flow.FlowRuleEvent;
43import org.onlab.onos.net.flow.FlowRuleListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080044import org.onlab.onos.net.flow.TrafficSelector;
45import org.onlab.onos.net.flow.TrafficTreatment;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080046import org.onlab.onos.net.host.HostEvent;
47import org.onlab.onos.net.host.HostListener;
Thomas Vachuska4830d392014-11-09 17:09:56 -080048import org.onlab.onos.net.intent.HostToHostIntent;
49import org.onlab.onos.net.intent.Intent;
50import org.onlab.onos.net.intent.IntentEvent;
Thomas Vachuska4830d392014-11-09 17:09:56 -080051import org.onlab.onos.net.intent.IntentListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080052import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080053import org.onlab.onos.net.link.LinkEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080054import org.onlab.onos.net.link.LinkListener;
Thomas Vachuska7d638d32014-11-07 10:24:43 -080055import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080056import org.onlab.packet.Ethernet;
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;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080069import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080070import static org.onlab.onos.net.DeviceId.deviceId;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080071import static org.onlab.onos.net.HostId.hostId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080072import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080073import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_UPDATED;
Thomas Vachuska4830d392014-11-09 17:09:56 -080074import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080075import static org.onlab.onos.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
Thomas Vachuska4830d392014-11-09 17:09:56 -080090 private static final String APP_ID = "org.onlab.onos.gui";
Thomas Vachuska4830d392014-11-09 17:09:56 -080091
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080092 private static final long TRAFFIC_FREQUENCY_SEC = 2000;
Thomas Vachuska22e34922014-11-14 00:40:55 -080093
Thomas Vachuska47635c62014-11-22 01:21:36 -080094 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
95 new Comparator<ControllerNode>() {
96 @Override
97 public int compare(ControllerNode o1, ControllerNode o2) {
98 return o1.id().toString().compareTo(o2.id().toString());
99 }
100 };
101
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800102
103 private final Timer timer = new Timer("topology-view");
104
105 private static final int MAX_EVENTS = 500;
106 private static final int MAX_BATCH_MS = 1000;
107 private static final int MAX_IDLE_MS = 500;
108
Thomas Vachuska4830d392014-11-09 17:09:56 -0800109 private final ApplicationId appId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -0800110
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800111 private Connection connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800112 private FrameConnection control;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800113
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800114 private final ClusterEventListener clusterListener = new InternalClusterListener();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800115 private final MastershipListener mastershipListener = new InternalMastershipListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800116 private final DeviceListener deviceListener = new InternalDeviceListener();
117 private final LinkListener linkListener = new InternalLinkListener();
118 private final HostListener hostListener = new InternalHostListener();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800119 private final IntentListener intentListener = new InternalIntentListener();
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800120 private final FlowRuleListener flowListener = new InternalFlowListener();
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800121
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800122 private final EventAccumulator eventAccummulator = new InternalEventAccummulator();
Thomas Vachuska47635c62014-11-22 01:21:36 -0800123
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800124 private boolean summaryEnabled = true;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800125 private TimerTask trafficTask;
126 private ObjectNode trafficEvent;
127
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800128 private long lastActive = System.currentTimeMillis();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800129 private boolean listenersRemoved = false;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800130
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800131 private TopologyViewIntentFilter intentFilter;
132
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800133 /**
134 * Creates a new web-socket for serving data to GUI topology view.
135 *
136 * @param directory service directory
137 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800138 public TopologyViewWebSocket(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800139 super(directory);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800140 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
Thomas Vachuska9edca302014-11-22 17:06:42 -0800141 hostService, linkService);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800142 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800143 }
144
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800145 /**
146 * Issues a close on the connection.
147 */
148 synchronized void close() {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800149 removeListeners();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800150 if (connection.isOpen()) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800151 connection.close();
152 }
153 }
154
155 /**
156 * Indicates if this connection is idle.
Thomas Vachuska3266abf2014-11-13 09:28:46 -0800157 *
158 * @return true if idle or closed
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800159 */
160 synchronized boolean isIdle() {
161 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
162 if (idle || !connection.isOpen()) {
163 return true;
164 }
165 try {
166 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
167 } catch (IOException e) {
168 log.warn("Unable to send ping message due to: ", e);
169 }
170 return false;
171 }
172
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800173 @Override
174 public void onOpen(Connection connection) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800175 log.info("GUI client connected");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800176 this.connection = connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800177 this.control = (FrameConnection) connection;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800178 addListeners();
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800179
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800180 sendAllInstances(null);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800181 sendAllDevices();
182 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800183 sendAllHosts();
184 }
185
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800186 @Override
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800187 public synchronized void onClose(int closeCode, String message) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800188 removeListeners();
Thomas Vachuska22e34922014-11-14 00:40:55 -0800189 timer.cancel();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800190 log.info("GUI client disconnected");
191 }
192
193 @Override
194 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
195 lastActive = System.currentTimeMillis();
196 return true;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800197 }
198
199 @Override
200 public void onMessage(String data) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800201 lastActive = System.currentTimeMillis();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800202 try {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800203 processMessage((ObjectNode) mapper.reader().readTree(data));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800204 } catch (Exception e) {
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800205 log.warn("Unable to parse GUI request {} due to {}", data, e);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800206 log.warn("Boom!!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800207 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800208 }
209
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800210 // Processes the specified event.
211 private void processMessage(ObjectNode event) {
212 String type = string(event, "event", "unknown");
213 if (type.equals("requestDetails")) {
214 requestDetails(event);
215 } else if (type.equals("updateMeta")) {
216 updateMetaUi(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800217
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800218 } else if (type.equals("addHostIntent")) {
219 createHostIntent(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800220 } else if (type.equals("addMultiSourceIntent")) {
221 createMultiSourceIntent(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800222
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800223 } else if (type.equals("requestTraffic")) {
224 requestTraffic(event);
225 } else if (type.equals("requestAllTraffic")) {
226 requestAllTraffic(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800227 } else if (type.equals("requestDeviceLinkFlows")) {
228 requestDeviceLinkFlows(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800229 } else if (type.equals("cancelTraffic")) {
230 cancelTraffic(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800231
232 } else if (type.equals("requestSummary")) {
233 requestSummary(event);
234 } else if (type.equals("cancelSummary")) {
235 cancelSummary(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800236 }
237 }
238
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800239 // Sends the specified data to the client.
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800240 private synchronized void sendMessage(ObjectNode data) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800241 try {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800242 if (connection.isOpen()) {
243 connection.sendMessage(data.toString());
244 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800245 } catch (IOException e) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800246 log.warn("Unable to send message {} to GUI due to {}", data, e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800247 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800248 }
249
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800250 // Sends all controller nodes to the client as node-added messages.
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800251 private void sendAllInstances(String messageType) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800252 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
253 Collections.sort(nodes, NODE_COMPARATOR);
254 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800255 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
256 messageType));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800257 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800258 }
259
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800260 // Sends all devices to the client as device-added messages.
261 private void sendAllDevices() {
262 for (Device device : deviceService.getDevices()) {
263 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800264 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800265 }
266
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800267 // Sends all links to the client as link-added messages.
268 private void sendAllLinks() {
269 for (Link link : linkService.getLinks()) {
270 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800271 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800272 }
273
274 // Sends all hosts to the client as host-added messages.
275 private void sendAllHosts() {
276 for (Host host : hostService.getHosts()) {
277 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
278 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800279 }
280
281 // Sends back device or host details.
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800282 private void requestDetails(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800283 ObjectNode payload = payload(event);
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800284 String type = string(payload, "class", "unknown");
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800285 long sid = number(event, "sid");
286
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800287 if (type.equals("device")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800288 sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800289 } else if (type.equals("host")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800290 sendMessage(hostDetails(hostId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800291 }
292 }
293
Thomas Vachuska9edca302014-11-22 17:06:42 -0800294
Thomas Vachuska4830d392014-11-09 17:09:56 -0800295 // Creates host-to-host intent.
296 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800297 ObjectNode payload = payload(event);
298 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800299 // TODO: add protection against device ids and non-existent hosts.
300 HostId one = hostId(string(payload, "one"));
301 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800302
Thomas Vachuska9edca302014-11-22 17:06:42 -0800303 HostToHostIntent intent =
304 new HostToHostIntent(appId, one, two,
305 DefaultTrafficSelector.builder().build(),
306 DefaultTrafficTreatment.builder().build());
307 startMonitoring(event);
308 intentService.submit(intent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800309 }
310
Thomas Vachuska9edca302014-11-22 17:06:42 -0800311 // Creates multi-source-to-single-dest intent.
312 private void createMultiSourceIntent(ObjectNode event) {
313 ObjectNode payload = payload(event);
314 long id = number(event, "sid");
315 // TODO: add protection against device ids and non-existent hosts.
316 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
317 HostId dst = hostId(string(payload, "dst"));
318 Host dstHost = hostService.getHost(dst);
319
320 Set<ConnectPoint> ingressPoints = getHostLocations(src);
321
322 // FIXME: clearly, this is not enough
323 TrafficSelector selector = DefaultTrafficSelector.builder()
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800324 .matchEthType(Ethernet.TYPE_IPV4)
Thomas Vachuska9edca302014-11-22 17:06:42 -0800325 .matchEthDst(dstHost.mac()).build();
326 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
327
328 MultiPointToSinglePointIntent intent =
329 new MultiPointToSinglePointIntent(appId, selector, treatment,
330 ingressPoints, dstHost.location());
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800331 startMonitoring(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800332 intentService.submit(intent);
333 }
334
335 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
336 Set<ConnectPoint> points = new HashSet<>();
337 for (HostId hostId : hostIds) {
338 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800339 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800340 return points;
341 }
342
343 private HostLocation getHostLocation(HostId hostId) {
344 return hostService.getHost(hostId).location();
345 }
346
347 // Produces a list of host ids from the specified JSON array.
348 private Set<HostId> getHostIds(ArrayNode ids) {
349 Set<HostId> hostIds = new HashSet<>();
350 for (JsonNode id : ids) {
351 hostIds.add(hostId(id.asText()));
352 }
353 return hostIds;
354 }
355
356
357 private synchronized long startMonitoring(ObjectNode event) {
358 if (trafficTask != null) {
359 stopMonitoring();
360 }
361 trafficEvent = event;
362 trafficTask = new TrafficMonitor();
363 timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800364 return number(event, "sid");
365 }
366
367 private synchronized void stopMonitoring() {
368 if (trafficTask != null) {
369 trafficTask.cancel();
370 trafficTask = null;
371 trafficEvent = null;
372 }
373 }
374
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800375 // Subscribes for host traffic messages.
376 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800377 long sid = startMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800378 sendMessage(trafficSummaryMessage(sid));
379 }
380
Thomas Vachuska29617e52014-11-20 03:17:46 -0800381 private void requestDeviceLinkFlows(ObjectNode event) {
382 ObjectNode payload = payload(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800383 long sid = startMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800384
385 // Get the set of selected hosts and their intents.
386 ArrayNode ids = (ArrayNode) payload.path("ids");
387 Set<Host> hosts = new HashSet<>();
388 Set<Device> devices = getDevices(ids);
389
390 // If there is a hover node, include it in the hosts and find intents.
391 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800392 if (!isNullOrEmpty(hover)) {
393 addHover(hosts, devices, hover);
394 }
395 sendMessage(flowSummaryMessage(sid, devices));
396 }
397
398
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800399 // Subscribes for host traffic messages.
Thomas Vachuska22e34922014-11-14 00:40:55 -0800400 private synchronized void requestTraffic(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800401 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800402 if (!payload.has("ids")) {
403 return;
404 }
405
Thomas Vachuska47635c62014-11-22 01:21:36 -0800406 long sid = startMonitoring(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800407
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800408 // Get the set of selected hosts and their intents.
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800409 ArrayNode ids = (ArrayNode) payload.path("ids");
410 Set<Host> hosts = getHosts(ids);
411 Set<Device> devices = getDevices(ids);
412 Set<Intent> intents = intentFilter.findPathIntents(hosts, devices);
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800413
414 // If there is a hover node, include it in the hosts and find intents.
415 String hover = string(payload, "hover");
416 Set<Intent> hoverIntents;
417 if (!isNullOrEmpty(hover)) {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800418 addHover(hosts, devices, hover);
419 hoverIntents = intentFilter.findPathIntents(hosts, devices);
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800420 intents.removeAll(hoverIntents);
421
422 // Send an initial message to highlight all links of all monitored intents.
423 sendMessage(trafficMessage(sid,
424 new TrafficClass("primary", hoverIntents),
425 new TrafficClass("secondary", intents)));
426
427 } else {
428 // Send an initial message to highlight all links of all monitored intents.
429 sendMessage(trafficMessage(sid, new TrafficClass("primary", intents)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800430 }
431 }
432
433 // Cancels sending traffic messages.
434 private void cancelTraffic(ObjectNode event) {
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800435 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800436 stopMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800437 }
438
Thomas Vachuska47635c62014-11-22 01:21:36 -0800439
440 // Subscribes for summary messages.
441 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800442 summaryEnabled = true;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800443 sendMessage(summmaryMessage(number(event, "sid")));
444 }
445
446 // Cancels sending summary messages.
447 private synchronized void cancelSummary(ObjectNode event) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800448 summaryEnabled = false;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800449 }
450
451
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800452 // Adds all internal listeners.
453 private void addListeners() {
454 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800455 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800456 deviceService.addListener(deviceListener);
457 linkService.addListener(linkListener);
458 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800459 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800460 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800461 }
462
463 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800464 private synchronized void removeListeners() {
465 if (!listenersRemoved) {
466 listenersRemoved = true;
467 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800468 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800469 deviceService.removeListener(deviceListener);
470 linkService.removeListener(linkListener);
471 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800472 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800473 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800474 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800475 }
476
477 // Cluster event listener.
478 private class InternalClusterListener implements ClusterEventListener {
479 @Override
480 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800481 sendMessage(instanceMessage(event, null));
482 }
483 }
484
485 // Mastership change listener
486 private class InternalMastershipListener implements MastershipListener {
487 @Override
488 public void event(MastershipEvent event) {
489 sendAllInstances("updateInstance");
490 Device device = deviceService.getDevice(event.subject());
491 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800492 }
493 }
494
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800495 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800496 private class InternalDeviceListener implements DeviceListener {
497 @Override
498 public void event(DeviceEvent event) {
499 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800500 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800501 }
502 }
503
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800504 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800505 private class InternalLinkListener implements LinkListener {
506 @Override
507 public void event(LinkEvent event) {
508 sendMessage(linkMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800509 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800510 }
511 }
512
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800513 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800514 private class InternalHostListener implements HostListener {
515 @Override
516 public void event(HostEvent event) {
517 sendMessage(hostMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800518 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800519 }
520 }
521
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800522 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800523 private class InternalIntentListener implements IntentListener {
524 @Override
525 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800526 if (trafficEvent != null) {
527 requestTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800528 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800529 eventAccummulator.add(event);
530 }
531 }
532
533 // Intent event listener.
534 private class InternalFlowListener implements FlowRuleListener {
535 @Override
536 public void event(FlowRuleEvent event) {
537 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800538 }
539 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800540
Thomas Vachuska47635c62014-11-22 01:21:36 -0800541 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800542 @Override
543 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800544 try {
545 if (trafficEvent != null) {
546 String type = string(trafficEvent, "event", "unknown");
547 if (type.equals("requestAllTraffic")) {
548 requestAllTraffic(trafficEvent);
549 } else if (type.equals("requestDeviceLinkFlows")) {
550 requestDeviceLinkFlows(trafficEvent);
551 } else {
552 requestTraffic(trafficEvent);
553 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800554 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800555 } catch (Exception e) {
556 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuska22e34922014-11-14 00:40:55 -0800557 }
558 }
559 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800560
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800561 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800562 private class InternalEventAccummulator extends AbstractEventAccumulator {
563 protected InternalEventAccummulator() {
564 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
565 }
566
Thomas Vachuska47635c62014-11-22 01:21:36 -0800567 @Override
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800568 public void processEvents(List<Event> events) {
569 try {
570 if (summaryEnabled) {
571 sendMessage(summmaryMessage(0));
572 }
573 } catch (Exception e) {
574 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuska47635c62014-11-22 01:21:36 -0800575 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800576
Thomas Vachuska47635c62014-11-22 01:21:36 -0800577 }
578 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800579}
580