ONOS-1479 - GUI Topology Overlay Work - (WIP)
- UiExtension now uses Builder Pattern; added topology overlay factory.
- Refactored UiExtensionTest (and other classes) to use builder.
- Created UiTopoOverlayFactory, UiTopoOverlay, and TopoOverlayCache.
- Started implementation of TrafficOverlay.
- Inject TopoOverlayCache into TopologyViewMessageHandler; added TopoSelectOverlay request handler.
- Modified UiExtensionManager to create traffic overlay.
- Augmented UiWebSocket to create overlays on demand, and inject overlay cache into topo view message handler.
- added client side wiring to switch overlays.
Change-Id: I6f99596aefb3b87382517ce929d268a2447545ee
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java
new file mode 100644
index 0000000..2490041
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui.impl;
+
+import org.onosproject.ui.UiTopoOverlay;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A cache of {@link org.onosproject.ui.UiTopoOverlay}'s that were registered
+ * at the time the UI connection was established.
+ */
+public class TopoOverlayCache {
+
+ private final Map<String, UiTopoOverlay> overlays = new HashMap<>();
+
+ /**
+ * Adds a topology overlay to the cache.
+ *
+ * @param overlay a topology overlay
+ */
+ public void add(UiTopoOverlay overlay) {
+ overlays.put(overlay.id(), overlay);
+ }
+
+ /**
+ * Invoked when the cache is no longer needed.
+ */
+ public void destroy() {
+ overlays.clear();
+ }
+
+ /**
+ * Switching currently selected overlay.
+ *
+ * @param deact identity of overlay to deactivate
+ * @param act identity of overlay to activate
+ */
+ public void switchOverlay(String deact, String act) {
+ UiTopoOverlay toDeactivate = getOverlay(deact);
+ UiTopoOverlay toActivate = getOverlay(act);
+ if (toDeactivate != null) {
+ toDeactivate.deactivate();
+ }
+ if (toActivate != null) {
+ toActivate.activate();
+ }
+ }
+
+ private UiTopoOverlay getOverlay(String id) {
+ return id == null ? null : overlays.get(id);
+ }
+
+ /**
+ * Returns the number of overlays in the cache.
+ *
+ * @return number of overlays
+ */
+ public int size() {
+ return overlays.size();
+ }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index a10e016..6d2295a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -103,6 +103,7 @@
private static final String SPRITE_DATA_REQ = "spriteDataRequest";
private static final String TOPO_START = "topoStart";
private static final String TOPO_HEARTBEAT = "topoHeartbeat";
+ private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
private static final String TOPO_STOP = "topoStop";
@@ -135,6 +136,8 @@
private final ExecutorService msgSender =
newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
+ private TopoOverlayCache overlayCache;
+
private TimerTask trafficTask = null;
private TrafficEvent trafficEvent = null;
@@ -172,6 +175,7 @@
return ImmutableSet.of(
new TopoStart(),
new TopoHeartbeat(),
+ new TopoSelectOverlay(),
new TopoStop(),
new ReqSummary(),
new CancelSummary(),
@@ -195,6 +199,15 @@
);
}
+ /**
+ * Injects the topology overlay cache.
+ *
+ * @param overlayCache injected cache
+ */
+ void setOverlayCache(TopoOverlayCache overlayCache) {
+ this.overlayCache = overlayCache;
+ }
+
// ==================================================================
/**
@@ -231,6 +244,19 @@
}
}
+ private final class TopoSelectOverlay extends RequestHandler {
+ private TopoSelectOverlay() {
+ super(TOPO_SELECT_OVERLAY);
+ }
+
+ @Override
+ public void process(long sid, ObjectNode payload) {
+ String deact = string(payload, "deactivate");
+ String act = string(payload, "activate");
+ overlayCache.switchOverlay(deact, act);
+ }
+ }
+
/**
* @deprecated in Cardinal Release
*/
@@ -311,7 +337,7 @@
@Override
public void process(long sid, ObjectNode payload) {
String type = string(payload, "class", "unknown");
- String id = JsonUtils.string(payload, "id");
+ String id = string(payload, "id");
if (type.equals("device")) {
sendMessage(deviceDetails(deviceId(id), sid));
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java
new file mode 100644
index 0000000..3c09bb4
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui.impl;
+
+import org.onosproject.ui.UiTopoOverlay;
+
+/**
+ * Topology Overlay for network traffic.
+ */
+public class TrafficOverlay extends UiTopoOverlay {
+ private static final String TRAFFIC_ID = "traffic";
+
+ /**
+ * Constructs the traffic overlay.
+ */
+ public TrafficOverlay() {
+ super(TRAFFIC_ID);
+ }
+
+ // TODO : override init(), activate(), deactivate(), destroy()
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 5706734..c3c9538 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -30,6 +30,7 @@
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
import org.onosproject.ui.impl.topo.OverlayService;
@@ -54,6 +55,10 @@
public class UiExtensionManager
implements UiExtensionService, SpriteService, OverlayService {
+ private static final ClassLoader CL =
+ UiExtensionManager.class.getClassLoader();
+ private static final String CORE = "core";
+
private final Logger log = LoggerFactory.getLogger(getClass());
// List of all extensions
@@ -104,8 +109,16 @@
new ClusterViewMessageHandler()
);
- return new UiExtension(coreViews, messageHandlerFactory, "core",
- UiExtensionManager.class.getClassLoader());
+ UiTopoOverlayFactory topoOverlayFactory =
+ () -> ImmutableList.of(
+ new TrafficOverlay()
+ );
+
+ return new UiExtension.Builder(CL, coreViews)
+ .messageHandlerFactory(messageHandlerFactory)
+ .topoOverlayFactory(topoOverlayFactory)
+ .resourcePath(CORE)
+ .build();
}
@Activate
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index b80c11f..761c2a3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -27,6 +27,7 @@
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.UiTopoOverlayFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,6 +59,7 @@
private long lastActive = System.currentTimeMillis();
private Map<String, UiMessageHandler> handlers;
+ private TopoOverlayCache overlayCache;
/**
* Creates a new web-socket for serving data to GUI.
@@ -72,7 +74,7 @@
* Issues a close on the connection.
*/
synchronized void close() {
- destroyHandlers();
+ destroyHandlersAndOverlays();
if (connection.isOpen()) {
connection.close();
}
@@ -104,7 +106,7 @@
this.connection = connection;
this.control = (FrameConnection) connection;
try {
- createHandlers();
+ createHandlersAndOverlays();
sendInstanceData();
log.info("GUI client connected");
@@ -118,7 +120,7 @@
@Override
public synchronized void onClose(int closeCode, String message) {
- destroyHandlers();
+ destroyHandlersAndOverlays();
log.info("GUI client disconnected [close-code={}, message={}]",
closeCode, message);
}
@@ -131,6 +133,7 @@
@Override
public void onMessage(String data) {
+ log.debug("onMessage: {}", data);
lastActive = System.currentTimeMillis();
try {
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
@@ -172,8 +175,11 @@
}
// Creates new message handlers.
- private synchronized void createHandlers() {
+ private synchronized void createHandlersAndOverlays() {
+ log.debug("creating handlers and overlays...");
handlers = new HashMap<>();
+ overlayCache = new TopoOverlayCache();
+
UiExtensionService service = directory.get(UiExtensionService.class);
service.getExtensions().forEach(ext -> {
UiMessageHandlerFactory factory = ext.messageHandlerFactory();
@@ -181,15 +187,33 @@
factory.newHandlers().forEach(handler -> {
handler.init(this, directory);
handler.messageTypes().forEach(type -> handlers.put(type, handler));
+
+ // need to inject the overlay cache into topology message handler
+ if (handler instanceof TopologyViewMessageHandler) {
+ ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
+ }
});
}
+
+ UiTopoOverlayFactory overlayFactory = ext.topoOverlayFactory();
+ if (overlayFactory != null) {
+ overlayFactory.newOverlays().forEach(overlayCache::add);
+ }
});
+ log.debug("#handlers = {}, #overlays = {}", handlers.size(),
+ overlayCache.size());
}
// Destroys message handlers.
- private synchronized void destroyHandlers() {
+ private synchronized void destroyHandlersAndOverlays() {
+ log.debug("destroying handlers and overlays...");
handlers.forEach((type, handler) -> handler.destroy());
handlers.clear();
+
+ if (overlayCache != null) {
+ overlayCache.destroy();
+ overlayCache = null;
+ }
}
// Sends cluster node/instance information to allow GUI to fail-over.
diff --git a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
index e1efe47..fd704a6 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -30,10 +30,11 @@
var tos = 'TopoOverlayService: ';
// injected refs
- var $log, fs, gs;
+ var $log, fs, gs, wss;
// internal state
- var overlays = {};
+ var overlays = {},
+ current = null;
function error(fn, msg) {
$log.error(tos + fn + '(): ' + msg);
@@ -108,20 +109,42 @@
return overlays[id];
}
+ // an overlay was selected via toolbar radio button press from user
+ function tbSelection(id) {
+ var same = current && current.overlayId === id,
+ payload = {};
+
+ function doop(op) {
+ var oid = current.overlayId;
+ $log.debug('Overlay:', op, oid);
+ current[op]();
+ payload[op] = oid;
+ }
+
+ if (!same) {
+ current && doop('deactivate');
+ current = overlay(id);
+ current && doop('activate');
+ wss.sendEvent('topoSelectOverlay', payload);
+ }
+ }
+
angular.module('ovTopo')
.factory('TopoOverlayService',
- ['$log', 'FnService', 'GlyphService',
+ ['$log', 'FnService', 'GlyphService', 'WebSocketService',
- function (_$log_, _fs_, _gs_) {
+ function (_$log_, _fs_, _gs_, _wss_) {
$log = _$log_;
fs = _fs_;
gs = _gs_;
+ wss = _wss_;
return {
register: register,
unregister: unregister,
list: list,
- overlay: overlay
+ overlay: overlay,
+ tbSelection: tbSelection
}
}]);
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 342e108..cbf443a 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -50,14 +50,14 @@
L: { id: 'cycleLabels-btn', gid: 'cycleLabels' },
R: { id: 'resetZoom-btn', gid: 'resetZoom' },
+ E: { id: 'eqMaster-btn', gid: 'eqMaster' },
+
V: { id: 'relatedIntents-btn', gid: 'relatedIntents' },
leftArrow: { id: 'prevIntent-btn', gid: 'prevIntent' },
rightArrow: { id: 'nextIntent-btn', gid: 'nextIntent' },
W: { id: 'intentTraffic-btn', gid: 'intentTraffic' },
A: { id: 'allTraffic-btn', gid: 'allTraffic' },
- F: { id: 'flows-btn', gid: 'flows' },
-
- E: { id: 'eqMaster-btn', gid: 'eqMaster' }
+ F: { id: 'flows-btn', gid: 'flows' }
};
// initial toggle state: default settings and tag to key mapping
@@ -153,14 +153,6 @@
addButton('E');
}
- function setOverlay(overlayId) {
- if (!overlayId) {
- $log.debug('CLEAR overlay');
- } else {
- $log.debug('SET overlay', overlayId);
- }
- }
-
function addOverlays() {
toolbar.addSeparator();
@@ -168,7 +160,9 @@
var rset = [{
gid: 'unknown',
tooltip: 'No Overlay',
- cb: function () { setOverlay(); }
+ cb: function () {
+ tov.tbSelection(null);
+ }
}];
tov.list().forEach(function (key) {
@@ -177,7 +171,7 @@
gid: ov._glyphId,
tooltip: (ov.tooltip || '(no tooltip)'),
cb: function () {
- setOverlay(ov.overlayId);
+ tov.tbSelection(ov.overlayId);
}
});
});
@@ -186,6 +180,7 @@
}
// TODO: 3rd row needs to be swapped in/out based on selected overlay
+ // NOTE: This particular row of buttons is for the traffic overlay
function addThirdRow() {
addButton('V');
addButton('leftArrow');