blob: 9e46da33e3497cffedfaecb0ac5092f316ee15bd [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 Vachuska1e68bdd2014-11-29 13:53:10 -080030import org.onlab.onos.mastership.MastershipAdminService;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080031import org.onlab.onos.mastership.MastershipEvent;
32import org.onlab.onos.mastership.MastershipListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080033import org.onlab.onos.net.ConnectPoint;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080034import org.onlab.onos.net.Device;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080035import org.onlab.onos.net.Host;
36import org.onlab.onos.net.HostId;
Thomas Vachuska9edca302014-11-22 17:06:42 -080037import org.onlab.onos.net.HostLocation;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080038import org.onlab.onos.net.Link;
39import org.onlab.onos.net.device.DeviceEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080040import org.onlab.onos.net.device.DeviceListener;
Thomas Vachuska4830d392014-11-09 17:09:56 -080041import org.onlab.onos.net.flow.DefaultTrafficSelector;
42import org.onlab.onos.net.flow.DefaultTrafficTreatment;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080043import org.onlab.onos.net.flow.FlowRuleEvent;
44import org.onlab.onos.net.flow.FlowRuleListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080045import org.onlab.onos.net.flow.TrafficSelector;
46import org.onlab.onos.net.flow.TrafficTreatment;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080047import org.onlab.onos.net.host.HostEvent;
48import org.onlab.onos.net.host.HostListener;
Thomas Vachuska4830d392014-11-09 17:09:56 -080049import org.onlab.onos.net.intent.HostToHostIntent;
50import org.onlab.onos.net.intent.Intent;
51import org.onlab.onos.net.intent.IntentEvent;
Thomas Vachuska4830d392014-11-09 17:09:56 -080052import org.onlab.onos.net.intent.IntentListener;
Thomas Vachuska9edca302014-11-22 17:06:42 -080053import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080054import org.onlab.onos.net.link.LinkEvent;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080055import org.onlab.onos.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;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -080070import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
Thomas Vachuskad1be50d2014-11-08 16:10:20 -080071import static org.onlab.onos.net.DeviceId.deviceId;
Thomas Vachuska690e5f62014-11-09 08:26:47 -080072import static org.onlab.onos.net.HostId.hostId;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080073import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080074import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_UPDATED;
Thomas Vachuska4830d392014-11-09 17:09:56 -080075import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
Thomas Vachuskad472c6e2014-11-07 19:11:05 -080076import static org.onlab.onos.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
Thomas Vachuska4830d392014-11-09 17:09:56 -080091 private static final String APP_ID = "org.onlab.onos.gui";
Thomas Vachuska4830d392014-11-09 17:09:56 -080092
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080093 private static final long TRAFFIC_FREQUENCY_SEC = 2000;
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
106 private static final int MAX_EVENTS = 500;
107 private static final int MAX_BATCH_MS = 1000;
108 private static final int MAX_IDLE_MS = 500;
109
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
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800125 private boolean summaryEnabled = true;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800126 private TimerTask trafficTask;
127 private ObjectNode trafficEvent;
128
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800129 private long lastActive = System.currentTimeMillis();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800130 private boolean listenersRemoved = false;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800131
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800132 private TopologyViewIntentFilter intentFilter;
133
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800134 /**
135 * Creates a new web-socket for serving data to GUI topology view.
136 *
137 * @param directory service directory
138 */
Thomas Vachuska7c27ad72014-11-14 16:20:10 -0800139 public TopologyViewWebSocket(ServiceDirectory directory) {
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800140 super(directory);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800141 intentFilter = new TopologyViewIntentFilter(intentService, deviceService,
Thomas Vachuska9edca302014-11-22 17:06:42 -0800142 hostService, linkService);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800143 appId = directory.get(CoreService.class).registerApplication(APP_ID);
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800144 }
145
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800146 /**
147 * Issues a close on the connection.
148 */
149 synchronized void close() {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800150 removeListeners();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800151 if (connection.isOpen()) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800152 connection.close();
153 }
154 }
155
156 /**
157 * Indicates if this connection is idle.
Thomas Vachuska3266abf2014-11-13 09:28:46 -0800158 *
159 * @return true if idle or closed
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800160 */
161 synchronized boolean isIdle() {
162 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
163 if (idle || !connection.isOpen()) {
164 return true;
165 }
166 try {
167 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
168 } catch (IOException e) {
169 log.warn("Unable to send ping message due to: ", e);
170 }
171 return false;
172 }
173
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800174 @Override
175 public void onOpen(Connection connection) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800176 log.info("GUI client connected");
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800177 this.connection = connection;
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800178 this.control = (FrameConnection) connection;
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800179 addListeners();
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800180
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800181 sendAllInstances(null);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800182 sendAllDevices();
183 sendAllLinks();
Thomas Vachuska4830d392014-11-09 17:09:56 -0800184 sendAllHosts();
185 }
186
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800187 @Override
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800188 public synchronized void onClose(int closeCode, String message) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800189 removeListeners();
Thomas Vachuska22e34922014-11-14 00:40:55 -0800190 timer.cancel();
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800191 log.info("GUI client disconnected");
192 }
193
194 @Override
195 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
196 lastActive = System.currentTimeMillis();
197 return true;
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800198 }
199
200 @Override
201 public void onMessage(String data) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800202 lastActive = System.currentTimeMillis();
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800203 try {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800204 processMessage((ObjectNode) mapper.reader().readTree(data));
Thomas Vachuska4830d392014-11-09 17:09:56 -0800205 } catch (Exception e) {
Thomas Vachuska0f6baee2014-11-11 15:02:32 -0800206 log.warn("Unable to parse GUI request {} due to {}", data, e);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800207 log.warn("Boom!!!!", e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800208 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800209 }
210
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800211 // Processes the specified event.
212 private void processMessage(ObjectNode event) {
213 String type = string(event, "event", "unknown");
214 if (type.equals("requestDetails")) {
215 requestDetails(event);
216 } else if (type.equals("updateMeta")) {
217 updateMetaUi(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800218
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800219 } else if (type.equals("addHostIntent")) {
220 createHostIntent(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800221 } else if (type.equals("addMultiSourceIntent")) {
222 createMultiSourceIntent(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800223
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800224 } else if (type.equals("requestTraffic")) {
225 requestTraffic(event);
226 } else if (type.equals("requestAllTraffic")) {
227 requestAllTraffic(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800228 } else if (type.equals("requestDeviceLinkFlows")) {
229 requestDeviceLinkFlows(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800230 } else if (type.equals("cancelTraffic")) {
231 cancelTraffic(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800232
233 } else if (type.equals("requestSummary")) {
234 requestSummary(event);
235 } else if (type.equals("cancelSummary")) {
236 cancelSummary(event);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800237
238 } else if (type.equals("equalizeMasters")) {
239 equalizeMasters(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800240 }
241 }
242
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800243 // Sends the specified data to the client.
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800244 private synchronized void sendMessage(ObjectNode data) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800245 try {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800246 if (connection.isOpen()) {
247 connection.sendMessage(data.toString());
248 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800249 } catch (IOException e) {
Thomas Vachuskaba5621e2014-11-12 01:47:19 -0800250 log.warn("Unable to send message {} to GUI due to {}", data, e);
Thomas Vachuskad1be50d2014-11-08 16:10:20 -0800251 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800252 }
253
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800254 // Sends all controller nodes to the client as node-added messages.
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800255 private void sendAllInstances(String messageType) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800256 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
257 Collections.sort(nodes, NODE_COMPARATOR);
258 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800259 sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
260 messageType));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800261 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800262 }
263
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800264 // Sends all devices to the client as device-added messages.
265 private void sendAllDevices() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800266 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800267 for (Device device : deviceService.getDevices()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800268 if (device.type() == Device.Type.ROADM) {
269 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
270 }
271 }
272 for (Device device : deviceService.getDevices()) {
273 if (device.type() != Device.Type.ROADM) {
274 sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
275 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800276 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800277 }
278
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800279 // Sends all links to the client as link-added messages.
280 private void sendAllLinks() {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800281 // Send optical first, others later for layered rendering
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800282 for (Link link : linkService.getLinks()) {
Thomas Vachuska82041f52014-11-30 22:14:02 -0800283 if (link.type() == Link.Type.OPTICAL) {
284 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
285 }
286 }
287 for (Link link : linkService.getLinks()) {
288 if (link.type() != Link.Type.OPTICAL) {
289 sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
290 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800291 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800292 }
293
294 // Sends all hosts to the client as host-added messages.
295 private void sendAllHosts() {
296 for (Host host : hostService.getHosts()) {
297 sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
298 }
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800299 }
300
301 // Sends back device or host details.
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800302 private void requestDetails(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800303 ObjectNode payload = payload(event);
Thomas Vachuskaf1fae002014-11-11 18:22:02 -0800304 String type = string(payload, "class", "unknown");
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800305 long sid = number(event, "sid");
306
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800307 if (type.equals("device")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800308 sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800309 } else if (type.equals("host")) {
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800310 sendMessage(hostDetails(hostId(string(payload, "id")), sid));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800311 }
312 }
313
Thomas Vachuska9edca302014-11-22 17:06:42 -0800314
Thomas Vachuska4830d392014-11-09 17:09:56 -0800315 // Creates host-to-host intent.
316 private void createHostIntent(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800317 ObjectNode payload = payload(event);
318 long id = number(event, "sid");
Thomas Vachuska4830d392014-11-09 17:09:56 -0800319 // TODO: add protection against device ids and non-existent hosts.
320 HostId one = hostId(string(payload, "one"));
321 HostId two = hostId(string(payload, "two"));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800322
Thomas Vachuska9edca302014-11-22 17:06:42 -0800323 HostToHostIntent intent =
324 new HostToHostIntent(appId, one, two,
325 DefaultTrafficSelector.builder().build(),
326 DefaultTrafficTreatment.builder().build());
327 startMonitoring(event);
328 intentService.submit(intent);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800329 }
330
Thomas Vachuska9edca302014-11-22 17:06:42 -0800331 // Creates multi-source-to-single-dest intent.
332 private void createMultiSourceIntent(ObjectNode event) {
333 ObjectNode payload = payload(event);
334 long id = number(event, "sid");
335 // TODO: add protection against device ids and non-existent hosts.
336 Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
337 HostId dst = hostId(string(payload, "dst"));
338 Host dstHost = hostService.getHost(dst);
339
340 Set<ConnectPoint> ingressPoints = getHostLocations(src);
341
342 // FIXME: clearly, this is not enough
343 TrafficSelector selector = DefaultTrafficSelector.builder()
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800344 .matchEthType(Ethernet.TYPE_IPV4)
Thomas Vachuska9edca302014-11-22 17:06:42 -0800345 .matchEthDst(dstHost.mac()).build();
346 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
347
348 MultiPointToSinglePointIntent intent =
349 new MultiPointToSinglePointIntent(appId, selector, treatment,
350 ingressPoints, dstHost.location());
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800351 startMonitoring(event);
Thomas Vachuska9edca302014-11-22 17:06:42 -0800352 intentService.submit(intent);
353 }
354
355 private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) {
356 Set<ConnectPoint> points = new HashSet<>();
357 for (HostId hostId : hostIds) {
358 points.add(getHostLocation(hostId));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800359 }
Thomas Vachuska9edca302014-11-22 17:06:42 -0800360 return points;
361 }
362
363 private HostLocation getHostLocation(HostId hostId) {
364 return hostService.getHost(hostId).location();
365 }
366
367 // Produces a list of host ids from the specified JSON array.
368 private Set<HostId> getHostIds(ArrayNode ids) {
369 Set<HostId> hostIds = new HashSet<>();
370 for (JsonNode id : ids) {
371 hostIds.add(hostId(id.asText()));
372 }
373 return hostIds;
374 }
375
376
377 private synchronized long startMonitoring(ObjectNode event) {
378 if (trafficTask != null) {
379 stopMonitoring();
380 }
381 trafficEvent = event;
382 trafficTask = new TrafficMonitor();
383 timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800384 return number(event, "sid");
385 }
386
387 private synchronized void stopMonitoring() {
388 if (trafficTask != null) {
389 trafficTask.cancel();
390 trafficTask = null;
391 trafficEvent = null;
392 }
393 }
394
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800395 // Subscribes for host traffic messages.
396 private synchronized void requestAllTraffic(ObjectNode event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800397 long sid = startMonitoring(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800398 sendMessage(trafficSummaryMessage(sid));
399 }
400
Thomas Vachuska29617e52014-11-20 03:17:46 -0800401 private void requestDeviceLinkFlows(ObjectNode event) {
402 ObjectNode payload = payload(event);
Thomas Vachuska47635c62014-11-22 01:21:36 -0800403 long sid = startMonitoring(event);
Thomas Vachuska29617e52014-11-20 03:17:46 -0800404
405 // Get the set of selected hosts and their intents.
406 ArrayNode ids = (ArrayNode) payload.path("ids");
407 Set<Host> hosts = new HashSet<>();
408 Set<Device> devices = getDevices(ids);
409
410 // If there is a hover node, include it in the hosts and find intents.
411 String hover = string(payload, "hover");
Thomas Vachuska29617e52014-11-20 03:17:46 -0800412 if (!isNullOrEmpty(hover)) {
413 addHover(hosts, devices, hover);
414 }
415 sendMessage(flowSummaryMessage(sid, devices));
416 }
417
418
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800419 // Subscribes for host traffic messages.
Thomas Vachuska22e34922014-11-14 00:40:55 -0800420 private synchronized void requestTraffic(ObjectNode event) {
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800421 ObjectNode payload = payload(event);
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800422 if (!payload.has("ids")) {
423 return;
424 }
425
Thomas Vachuska47635c62014-11-22 01:21:36 -0800426 long sid = startMonitoring(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800427
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800428 // Get the set of selected hosts and their intents.
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800429 ArrayNode ids = (ArrayNode) payload.path("ids");
430 Set<Host> hosts = getHosts(ids);
431 Set<Device> devices = getDevices(ids);
432 Set<Intent> intents = intentFilter.findPathIntents(hosts, devices);
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800433
434 // If there is a hover node, include it in the hosts and find intents.
435 String hover = string(payload, "hover");
436 Set<Intent> hoverIntents;
437 if (!isNullOrEmpty(hover)) {
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800438 addHover(hosts, devices, hover);
439 hoverIntents = intentFilter.findPathIntents(hosts, devices);
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800440 intents.removeAll(hoverIntents);
441
442 // Send an initial message to highlight all links of all monitored intents.
443 sendMessage(trafficMessage(sid,
444 new TrafficClass("primary", hoverIntents),
445 new TrafficClass("secondary", intents)));
446
447 } else {
448 // Send an initial message to highlight all links of all monitored intents.
449 sendMessage(trafficMessage(sid, new TrafficClass("primary", intents)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800450 }
451 }
452
453 // Cancels sending traffic messages.
454 private void cancelTraffic(ObjectNode event) {
Thomas Vachuskae7591e52014-11-13 21:31:15 -0800455 sendMessage(trafficMessage(number(event, "sid")));
Thomas Vachuska47635c62014-11-22 01:21:36 -0800456 stopMonitoring();
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800457 }
458
Thomas Vachuska47635c62014-11-22 01:21:36 -0800459
460 // Subscribes for summary messages.
461 private synchronized void requestSummary(ObjectNode event) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800462 summaryEnabled = true;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800463 sendMessage(summmaryMessage(number(event, "sid")));
464 }
465
466 // Cancels sending summary messages.
467 private synchronized void cancelSummary(ObjectNode event) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800468 summaryEnabled = false;
Thomas Vachuska47635c62014-11-22 01:21:36 -0800469 }
470
471
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800472 // Forces mastership role rebalancing.
473 private void equalizeMasters(ObjectNode event) {
474 directory.get(MastershipAdminService.class).balanceRoles();
475 }
476
477
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800478 // Adds all internal listeners.
479 private void addListeners() {
480 clusterService.addListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800481 mastershipService.addListener(mastershipListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800482 deviceService.addListener(deviceListener);
483 linkService.addListener(linkListener);
484 hostService.addListener(hostListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800485 intentService.addListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800486 flowService.addListener(flowListener);
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800487 }
488
489 // Removes all internal listeners.
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800490 private synchronized void removeListeners() {
491 if (!listenersRemoved) {
492 listenersRemoved = true;
493 clusterService.removeListener(clusterListener);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800494 mastershipService.removeListener(mastershipListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800495 deviceService.removeListener(deviceListener);
496 linkService.removeListener(linkListener);
497 hostService.removeListener(hostListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800498 intentService.removeListener(intentListener);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800499 flowService.removeListener(flowListener);
Thomas Vachuskadea45ff2014-11-12 18:35:46 -0800500 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800501 }
502
503 // Cluster event listener.
504 private class InternalClusterListener implements ClusterEventListener {
505 @Override
506 public void event(ClusterEvent event) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800507 sendMessage(instanceMessage(event, null));
508 }
509 }
510
511 // Mastership change listener
512 private class InternalMastershipListener implements MastershipListener {
513 @Override
514 public void event(MastershipEvent event) {
515 sendAllInstances("updateInstance");
516 Device device = deviceService.getDevice(event.subject());
517 sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800518 }
519 }
520
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800521 // Device event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800522 private class InternalDeviceListener implements DeviceListener {
523 @Override
524 public void event(DeviceEvent event) {
525 sendMessage(deviceMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800526 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800527 }
528 }
529
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800530 // Link event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800531 private class InternalLinkListener implements LinkListener {
532 @Override
533 public void event(LinkEvent event) {
534 sendMessage(linkMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800535 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800536 }
537 }
538
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800539 // Host event listener.
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800540 private class InternalHostListener implements HostListener {
541 @Override
542 public void event(HostEvent event) {
543 sendMessage(hostMessage(event));
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800544 eventAccummulator.add(event);
Thomas Vachuska690e5f62014-11-09 08:26:47 -0800545 }
546 }
547
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800548 // Intent event listener.
Thomas Vachuska4830d392014-11-09 17:09:56 -0800549 private class InternalIntentListener implements IntentListener {
550 @Override
551 public void event(IntentEvent event) {
Thomas Vachuska47635c62014-11-22 01:21:36 -0800552 if (trafficEvent != null) {
553 requestTraffic(trafficEvent);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800554 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800555 eventAccummulator.add(event);
556 }
557 }
558
559 // Intent event listener.
560 private class InternalFlowListener implements FlowRuleListener {
561 @Override
562 public void event(FlowRuleEvent event) {
563 eventAccummulator.add(event);
Thomas Vachuska4830d392014-11-09 17:09:56 -0800564 }
565 }
Thomas Vachuskaa7c3dd12014-11-11 09:10:19 -0800566
Thomas Vachuska47635c62014-11-22 01:21:36 -0800567 private class TrafficMonitor extends TimerTask {
Thomas Vachuska22e34922014-11-14 00:40:55 -0800568 @Override
569 public void run() {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800570 try {
571 if (trafficEvent != null) {
572 String type = string(trafficEvent, "event", "unknown");
573 if (type.equals("requestAllTraffic")) {
574 requestAllTraffic(trafficEvent);
575 } else if (type.equals("requestDeviceLinkFlows")) {
576 requestDeviceLinkFlows(trafficEvent);
577 } else {
578 requestTraffic(trafficEvent);
579 }
Thomas Vachuska5fedb7a2014-11-20 00:55:08 -0800580 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800581 } catch (Exception e) {
582 log.warn("Unable to handle traffic request due to {}", e.getMessage());
Thomas Vachuska82041f52014-11-30 22:14:02 -0800583 log.warn("Boom!", e);
Thomas Vachuska22e34922014-11-14 00:40:55 -0800584 }
585 }
586 }
Thomas Vachuska47635c62014-11-22 01:21:36 -0800587
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800588 // Accumulates events to drive methodic update of the summary pane.
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800589 private class InternalEventAccummulator extends AbstractEventAccumulator {
590 protected InternalEventAccummulator() {
591 super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
592 }
593
Thomas Vachuska47635c62014-11-22 01:21:36 -0800594 @Override
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800595 public void processEvents(List<Event> events) {
596 try {
597 if (summaryEnabled) {
598 sendMessage(summmaryMessage(0));
599 }
600 } catch (Exception e) {
601 log.warn("Unable to handle summary request due to {}", e.getMessage());
Thomas Vachuska47635c62014-11-22 01:21:36 -0800602 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800603
Thomas Vachuska47635c62014-11-22 01:21:36 -0800604 }
605 }
Thomas Vachuska7d638d32014-11-07 10:24:43 -0800606}
607