Thomas Vachuska

GUI traffic visualization work on server-side.

Change-Id: I2e903ec028ea40fd325f69c4d7e1f0b2b6db2f42
...@@ -44,6 +44,7 @@ import org.onlab.onos.net.intent.ConnectivityIntent; ...@@ -44,6 +44,7 @@ import org.onlab.onos.net.intent.ConnectivityIntent;
44 import org.onlab.onos.net.intent.Intent; 44 import org.onlab.onos.net.intent.Intent;
45 import org.onlab.onos.net.intent.IntentService; 45 import org.onlab.onos.net.intent.IntentService;
46 import org.onlab.onos.net.intent.LinkCollectionIntent; 46 import org.onlab.onos.net.intent.LinkCollectionIntent;
47 +import org.onlab.onos.net.intent.OpticalConnectivityIntent;
47 import org.onlab.onos.net.intent.PathIntent; 48 import org.onlab.onos.net.intent.PathIntent;
48 import org.onlab.onos.net.link.LinkEvent; 49 import org.onlab.onos.net.link.LinkEvent;
49 import org.onlab.onos.net.link.LinkService; 50 import org.onlab.onos.net.link.LinkService;
...@@ -88,6 +89,7 @@ public abstract class TopologyMessages { ...@@ -88,6 +89,7 @@ public abstract class TopologyMessages {
88 protected final HostService hostService; 89 protected final HostService hostService;
89 protected final MastershipService mastershipService; 90 protected final MastershipService mastershipService;
90 protected final IntentService intentService; 91 protected final IntentService intentService;
92 +// protected final StatisticService statService;
91 93
92 protected final ObjectMapper mapper = new ObjectMapper(); 94 protected final ObjectMapper mapper = new ObjectMapper();
93 95
...@@ -107,6 +109,7 @@ public abstract class TopologyMessages { ...@@ -107,6 +109,7 @@ public abstract class TopologyMessages {
107 hostService = directory.get(HostService.class); 109 hostService = directory.get(HostService.class);
108 mastershipService = directory.get(MastershipService.class); 110 mastershipService = directory.get(MastershipService.class);
109 intentService = directory.get(IntentService.class); 111 intentService = directory.get(IntentService.class);
112 +// statService = directory.get(StatisticService.class);
110 } 113 }
111 114
112 // Retrieves the payload from the specified event. 115 // Retrieves the payload from the specified event.
...@@ -376,10 +379,14 @@ public abstract class TopologyMessages { ...@@ -376,10 +379,14 @@ public abstract class TopologyMessages {
376 379
377 for (TrafficClass trafficClass : trafficClasses) { 380 for (TrafficClass trafficClass : trafficClasses) {
378 for (Intent intent : trafficClass.intents) { 381 for (Intent intent : trafficClass.intents) {
382 + boolean isOptical = intent instanceof OpticalConnectivityIntent;
379 List<Intent> installables = intentService.getInstallableIntents(intent.id()); 383 List<Intent> installables = intentService.getInstallableIntents(intent.id());
384 + if (installables != null) {
380 for (Intent installable : installables) { 385 for (Intent installable : installables) {
386 + String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type;
381 if (installable instanceof ConnectivityIntent) { 387 if (installable instanceof ConnectivityIntent) {
382 - addPathTraffic(paths, trafficClass.type, (ConnectivityIntent) installable); 388 + addPathTraffic(paths, cls, (ConnectivityIntent) installable);
389 + }
383 } 390 }
384 } 391 }
385 } 392 }
...@@ -395,21 +402,35 @@ public abstract class TopologyMessages { ...@@ -395,21 +402,35 @@ public abstract class TopologyMessages {
395 ObjectNode pathNode = mapper.createObjectNode(); 402 ObjectNode pathNode = mapper.createObjectNode();
396 ArrayNode linksNode = mapper.createArrayNode(); 403 ArrayNode linksNode = mapper.createArrayNode();
397 404
398 - Iterable<Link> links; 405 + Iterable<Link> links = pathLinks(installable);
399 - if (installable instanceof PathIntent) { 406 + if (links != null) {
400 - links = ((PathIntent) installable).path().links(); 407 + ArrayNode labels = mapper.createArrayNode();
401 - } else if (installable instanceof LinkCollectionIntent) { 408 + boolean hasTraffic = true; // FIXME
402 - links = ((LinkCollectionIntent) installable).links();
403 - } else {
404 - return;
405 - }
406 -
407 for (Link link : links) { 409 for (Link link : links) {
408 linksNode.add(compactLinkString(link)); 410 linksNode.add(compactLinkString(link));
409 - } 411 +// Load load = statService.load(link);
410 - pathNode.put("type", type).set("links", linksNode); 412 + String label = "";
413 +// if (load.rate() > 0) {
414 +// label = load.toString();
415 +// }
416 + labels.add(label);
417 + }
418 + pathNode.put("class", hasTraffic ? type + " animated" : type);
419 + pathNode.put("traffic", hasTraffic);
420 + pathNode.set("links", linksNode);
421 + pathNode.set("labels", labels);
411 paths.add(pathNode); 422 paths.add(pathNode);
412 } 423 }
424 + }
425 +
426 + private Iterable<Link> pathLinks(ConnectivityIntent intent) {
427 + if (intent instanceof PathIntent) {
428 + return ((PathIntent) intent).path().links();
429 + } else if (intent instanceof LinkCollectionIntent) {
430 + return ((LinkCollectionIntent) intent).links();
431 + }
432 + return null;
433 + }
413 434
414 // Produces compact string representation of a link. 435 // Produces compact string representation of a link.
415 private static String compactLinkString(Link link) { 436 private static String compactLinkString(Link link) {
......
...@@ -24,8 +24,6 @@ import org.onlab.onos.cluster.ClusterEventListener; ...@@ -24,8 +24,6 @@ import org.onlab.onos.cluster.ClusterEventListener;
24 import org.onlab.onos.cluster.ControllerNode; 24 import org.onlab.onos.cluster.ControllerNode;
25 import org.onlab.onos.core.ApplicationId; 25 import org.onlab.onos.core.ApplicationId;
26 import org.onlab.onos.core.CoreService; 26 import org.onlab.onos.core.CoreService;
27 -import org.onlab.onos.mastership.MastershipEvent;
28 -import org.onlab.onos.mastership.MastershipListener;
29 import org.onlab.onos.net.ConnectPoint; 27 import org.onlab.onos.net.ConnectPoint;
30 import org.onlab.onos.net.Device; 28 import org.onlab.onos.net.Device;
31 import org.onlab.onos.net.Host; 29 import org.onlab.onos.net.Host;
...@@ -43,6 +41,7 @@ import org.onlab.onos.net.intent.Intent; ...@@ -43,6 +41,7 @@ import org.onlab.onos.net.intent.Intent;
43 import org.onlab.onos.net.intent.IntentEvent; 41 import org.onlab.onos.net.intent.IntentEvent;
44 import org.onlab.onos.net.intent.IntentListener; 42 import org.onlab.onos.net.intent.IntentListener;
45 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; 43 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
44 +import org.onlab.onos.net.intent.OpticalConnectivityIntent;
46 import org.onlab.onos.net.intent.PathIntent; 45 import org.onlab.onos.net.intent.PathIntent;
47 import org.onlab.onos.net.intent.PointToPointIntent; 46 import org.onlab.onos.net.intent.PointToPointIntent;
48 import org.onlab.onos.net.link.LinkEvent; 47 import org.onlab.onos.net.link.LinkEvent;
...@@ -52,9 +51,9 @@ import org.onlab.osgi.ServiceDirectory; ...@@ -52,9 +51,9 @@ import org.onlab.osgi.ServiceDirectory;
52 import java.io.IOException; 51 import java.io.IOException;
53 import java.util.HashSet; 52 import java.util.HashSet;
54 import java.util.List; 53 import java.util.List;
55 -import java.util.Map;
56 import java.util.Set; 54 import java.util.Set;
57 -import java.util.concurrent.ConcurrentHashMap; 55 +import java.util.Timer;
56 +import java.util.TimerTask;
58 57
59 import static com.google.common.base.Strings.isNullOrEmpty; 58 import static com.google.common.base.Strings.isNullOrEmpty;
60 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED; 59 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
...@@ -62,6 +61,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; ...@@ -62,6 +61,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
62 import static org.onlab.onos.net.HostId.hostId; 61 import static org.onlab.onos.net.HostId.hostId;
63 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 62 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
64 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED; 63 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
64 +import static org.onlab.onos.net.intent.IntentState.INSTALLED;
65 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED; 65 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
66 66
67 /** 67 /**
...@@ -79,6 +79,8 @@ public class TopologyWebSocket ...@@ -79,6 +79,8 @@ public class TopologyWebSocket
79 79
80 private static final String APP_ID = "org.onlab.onos.gui"; 80 private static final String APP_ID = "org.onlab.onos.gui";
81 81
82 + private static final long TRAFFIC_FREQUENCY_SEC = 5000;
83 +
82 private final ApplicationId appId; 84 private final ApplicationId appId;
83 85
84 private Connection connection; 86 private Connection connection;
...@@ -88,11 +90,12 @@ public class TopologyWebSocket ...@@ -88,11 +90,12 @@ public class TopologyWebSocket
88 private final DeviceListener deviceListener = new InternalDeviceListener(); 90 private final DeviceListener deviceListener = new InternalDeviceListener();
89 private final LinkListener linkListener = new InternalLinkListener(); 91 private final LinkListener linkListener = new InternalLinkListener();
90 private final HostListener hostListener = new InternalHostListener(); 92 private final HostListener hostListener = new InternalHostListener();
91 - private final MastershipListener mastershipListener = new InternalMastershipListener();
92 private final IntentListener intentListener = new InternalIntentListener(); 93 private final IntentListener intentListener = new InternalIntentListener();
93 94
94 // Intents that are being monitored for the GUI 95 // Intents that are being monitored for the GUI
95 - private Map<Intent, Long> intentsToMonitor = new ConcurrentHashMap<>(); 96 + private ObjectNode monitorRequest;
97 + private final Timer timer = new Timer("intent-traffic-monitor");
98 + private final TimerTask timerTask = new IntentTrafficMonitor();
96 99
97 private long lastActive = System.currentTimeMillis(); 100 private long lastActive = System.currentTimeMillis();
98 private boolean listenersRemoved = false; 101 private boolean listenersRemoved = false;
...@@ -141,6 +144,7 @@ public class TopologyWebSocket ...@@ -141,6 +144,7 @@ public class TopologyWebSocket
141 this.connection = connection; 144 this.connection = connection;
142 this.control = (FrameConnection) connection; 145 this.control = (FrameConnection) connection;
143 addListeners(); 146 addListeners();
147 + timer.schedule(timerTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC);
144 148
145 sendAllInstances(); 149 sendAllInstances();
146 sendAllDevices(); 150 sendAllDevices();
...@@ -151,6 +155,7 @@ public class TopologyWebSocket ...@@ -151,6 +155,7 @@ public class TopologyWebSocket
151 @Override 155 @Override
152 public synchronized void onClose(int closeCode, String message) { 156 public synchronized void onClose(int closeCode, String message) {
153 removeListeners(); 157 removeListeners();
158 + timer.cancel();
154 log.info("GUI client disconnected"); 159 log.info("GUI client disconnected");
155 } 160 }
156 161
...@@ -245,14 +250,15 @@ public class TopologyWebSocket ...@@ -245,14 +250,15 @@ public class TopologyWebSocket
245 HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two, 250 HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two,
246 DefaultTrafficSelector.builder().build(), 251 DefaultTrafficSelector.builder().build(),
247 DefaultTrafficTreatment.builder().build()); 252 DefaultTrafficTreatment.builder().build());
248 - intentsToMonitor.put(hostIntent, number(event, "sid")); 253 + monitorRequest = event;
249 intentService.submit(hostIntent); 254 intentService.submit(hostIntent);
250 } 255 }
251 256
252 // Sends traffic message. 257 // Sends traffic message.
253 - private void requestTraffic(ObjectNode event) { 258 + private synchronized void requestTraffic(ObjectNode event) {
254 ObjectNode payload = payload(event); 259 ObjectNode payload = payload(event);
255 long sid = number(event, "sid"); 260 long sid = number(event, "sid");
261 + monitorRequest = event;
256 262
257 // Get the set of selected hosts and their intents. 263 // Get the set of selected hosts and their intents.
258 Set<Host> hosts = getHosts((ArrayNode) payload.path("ids")); 264 Set<Host> hosts = getHosts((ArrayNode) payload.path("ids"));
...@@ -274,18 +280,13 @@ public class TopologyWebSocket ...@@ -274,18 +280,13 @@ public class TopologyWebSocket
274 } else { 280 } else {
275 // Send an initial message to highlight all links of all monitored intents. 281 // Send an initial message to highlight all links of all monitored intents.
276 sendMessage(trafficMessage(sid, new TrafficClass("primary", intents))); 282 sendMessage(trafficMessage(sid, new TrafficClass("primary", intents)));
277 -
278 - // Add all those intents to the list of monitored intents & flows.
279 - intentsToMonitor.clear();
280 - for (Intent intent : intents) {
281 - intentsToMonitor.put(intent, sid);
282 - }
283 } 283 }
284 } 284 }
285 285
286 // Cancels sending traffic messages. 286 // Cancels sending traffic messages.
287 private void cancelTraffic(ObjectNode event) { 287 private void cancelTraffic(ObjectNode event) {
288 sendMessage(trafficMessage(number(event, "sid"))); 288 sendMessage(trafficMessage(number(event, "sid")));
289 + monitorRequest = null;
289 } 290 }
290 291
291 // Finds all path (host-to-host or point-to-point) intents that pertains 292 // Finds all path (host-to-host or point-to-point) intents that pertains
...@@ -306,7 +307,10 @@ public class TopologyWebSocket ...@@ -306,7 +307,10 @@ public class TopologyWebSocket
306 return intents; 307 return intents;
307 } 308 }
308 309
310 + Set<OpticalConnectivityIntent> opticalIntents = new HashSet<>();
311 +
309 for (Intent intent : intentService.getIntents()) { 312 for (Intent intent : intentService.getIntents()) {
313 + if (intentService.getIntentState(intent.id()) == INSTALLED) {
310 boolean isRelevant = false; 314 boolean isRelevant = false;
311 if (intent instanceof HostToHostIntent) { 315 if (intent instanceof HostToHostIntent) {
312 isRelevant = isIntentRelevant((HostToHostIntent) intent, hosts); 316 isRelevant = isIntentRelevant((HostToHostIntent) intent, hosts);
...@@ -314,6 +318,8 @@ public class TopologyWebSocket ...@@ -314,6 +318,8 @@ public class TopologyWebSocket
314 isRelevant = isIntentRelevant((PointToPointIntent) intent, edgePoints); 318 isRelevant = isIntentRelevant((PointToPointIntent) intent, edgePoints);
315 } else if (intent instanceof MultiPointToSinglePointIntent) { 319 } else if (intent instanceof MultiPointToSinglePointIntent) {
316 isRelevant = isIntentRelevant((MultiPointToSinglePointIntent) intent, edgePoints); 320 isRelevant = isIntentRelevant((MultiPointToSinglePointIntent) intent, edgePoints);
321 + } else if (intent instanceof OpticalConnectivityIntent) {
322 + opticalIntents.add((OpticalConnectivityIntent) intent);
317 } 323 }
318 // TODO: add other intents, e.g. SinglePointToMultiPointIntent 324 // TODO: add other intents, e.g. SinglePointToMultiPointIntent
319 325
...@@ -321,6 +327,13 @@ public class TopologyWebSocket ...@@ -321,6 +327,13 @@ public class TopologyWebSocket
321 intents.add(intent); 327 intents.add(intent);
322 } 328 }
323 } 329 }
330 + }
331 +
332 + for (OpticalConnectivityIntent intent : opticalIntents) {
333 + if (isIntentRelevant(intent, intents)) {
334 + intents.add(intent);
335 + }
336 + }
324 return intents; 337 return intents;
325 } 338 }
326 339
...@@ -362,6 +375,23 @@ public class TopologyWebSocket ...@@ -362,6 +375,23 @@ public class TopologyWebSocket
362 return true; 375 return true;
363 } 376 }
364 377
378 + // Indicates whether the specified intent involves all of the given edge points.
379 + private boolean isIntentRelevant(OpticalConnectivityIntent opticalIntent,
380 + Set<Intent> intents) {
381 + for (Intent intent : intents) {
382 + List<Intent> installables = intentService.getInstallableIntents(intent.id());
383 + for (Intent installable : installables) {
384 + if (installable instanceof PathIntent) {
385 + Path path = ((PathIntent) installable).path();
386 + if (opticalIntent.getSrcConnectPoint().equals(path.src()) &&
387 + opticalIntent.getDst().equals(path.dst())) {
388 + return true;
389 + }
390 + }
391 + }
392 + }
393 + return false;
394 + }
365 395
366 // Produces a set of all host ids listed in the specified JSON array. 396 // Produces a set of all host ids listed in the specified JSON array.
367 private Set<Host> getHosts(ArrayNode array) { 397 private Set<Host> getHosts(ArrayNode array) {
...@@ -401,7 +431,6 @@ public class TopologyWebSocket ...@@ -401,7 +431,6 @@ public class TopologyWebSocket
401 deviceService.addListener(deviceListener); 431 deviceService.addListener(deviceListener);
402 linkService.addListener(linkListener); 432 linkService.addListener(linkListener);
403 hostService.addListener(hostListener); 433 hostService.addListener(hostListener);
404 - mastershipService.addListener(mastershipListener);
405 intentService.addListener(intentListener); 434 intentService.addListener(intentListener);
406 } 435 }
407 436
...@@ -413,7 +442,6 @@ public class TopologyWebSocket ...@@ -413,7 +442,6 @@ public class TopologyWebSocket
413 deviceService.removeListener(deviceListener); 442 deviceService.removeListener(deviceListener);
414 linkService.removeListener(linkListener); 443 linkService.removeListener(linkListener);
415 hostService.removeListener(hostListener); 444 hostService.removeListener(hostListener);
416 - mastershipService.removeListener(mastershipListener);
417 intentService.removeListener(intentListener); 445 intentService.removeListener(intentListener);
418 } 446 }
419 } 447 }
...@@ -450,35 +478,23 @@ public class TopologyWebSocket ...@@ -450,35 +478,23 @@ public class TopologyWebSocket
450 } 478 }
451 } 479 }
452 480
453 - // Mastership event listener.
454 - private class InternalMastershipListener implements MastershipListener {
455 - @Override
456 - public void event(MastershipEvent event) {
457 - // TODO: Is DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED the same?
458 -
459 - }
460 - }
461 -
462 // Intent event listener. 481 // Intent event listener.
463 private class InternalIntentListener implements IntentListener { 482 private class InternalIntentListener implements IntentListener {
464 @Override 483 @Override
465 public void event(IntentEvent event) { 484 public void event(IntentEvent event) {
466 - Intent intent = event.subject(); 485 + if (monitorRequest != null) {
467 - Long sid = intentsToMonitor.get(intent); 486 + requestTraffic(monitorRequest);
468 - if (sid != null) {
469 - List<Intent> installable = intentService.getInstallableIntents(intent.id());
470 - if (installable != null && !installable.isEmpty()) {
471 - PathIntent pathIntent = (PathIntent) installable.iterator().next();
472 - Path path = pathIntent.path();
473 - ObjectNode payload = pathMessage(path, "host")
474 - .put("intentId", intent.id().toString());
475 - sendMessage(envelope("showPath", sid, payload));
476 - TrafficClass tc = new TrafficClass("animated", intentsToMonitor.keySet());
477 - sendMessage(trafficMessage(sid, tc));
478 - }
479 } 487 }
480 } 488 }
481 } 489 }
482 490
491 + private class IntentTrafficMonitor extends TimerTask {
492 + @Override
493 + public void run() {
494 + if (monitorRequest != null) {
495 + requestTraffic(monitorRequest);
496 + }
497 + }
498 + }
483 } 499 }
484 500
......