TopoRegions: created skeleton Topo2 UI view for development of the "region-aware" topology.
- Added initial event generation (layout/region/ etc.) -- WIP
Change-Id: I2f93eea7505ff0400085d7f67491f6b61231cb86
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 7ba4609..873184e 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
@@ -185,7 +185,7 @@
private TimerTask summaryTask = null;
private boolean summaryRunning = false;
- private boolean listenersRemoved = false;
+ private volatile boolean listenersRemoved = false;
@Override
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 110cde2..79f3d77 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
@@ -32,7 +32,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -51,11 +50,12 @@
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiPreferencesService;
-import org.onosproject.ui.UiTopoOverlayFactory;
+import org.onosproject.ui.UiTopoMap;
import org.onosproject.ui.UiTopoMapFactory;
+import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
-import org.onosproject.ui.UiTopoMap;
+import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -120,7 +120,8 @@
private final ObjectMapper mapper = new ObjectMapper();
private final ExecutorService eventHandlingExecutor =
- Executors.newSingleThreadExecutor(Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
+ Executors.newSingleThreadExecutor(
+ Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
// Creates core UI extension
private UiExtension createCoreExtension() {
@@ -130,6 +131,10 @@
new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
new UiView(NETWORK, "topo", "Topology", "nav_topo"),
+
+ // FIXME: leave commented out for now, while still under development
+// new UiView(NETWORK, "topo2", "New-Topo"),
+
new UiView(NETWORK, "device", "Devices", "nav_devs"),
new UiViewHidden("flow"),
new UiViewHidden("port"),
@@ -145,6 +150,7 @@
() -> ImmutableList.of(
new UserPreferencesMessageHandler(),
new TopologyViewMessageHandler(),
+ new Topo2ViewMessageHandler(),
new MapSelectorMessageHandler(),
new DeviceViewMessageHandler(),
new LinkViewMessageHandler(),
@@ -196,11 +202,11 @@
@Activate
public void activate() {
Serializer serializer = Serializer.using(KryoNamespaces.API,
- ObjectNode.class, ArrayNode.class,
- JsonNodeFactory.class, LinkedHashMap.class,
- TextNode.class, BooleanNode.class,
- LongNode.class, DoubleNode.class, ShortNode.class,
- IntNode.class, NullNode.class);
+ ObjectNode.class, ArrayNode.class,
+ JsonNodeFactory.class, LinkedHashMap.class,
+ TextNode.class, BooleanNode.class,
+ LongNode.class, DoubleNode.class, ShortNode.class,
+ IntNode.class, NullNode.class);
prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
.withName(ONOS_USER_PREFERENCES)
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 2585bc8..40f2622 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
@@ -92,7 +92,7 @@
this.userName = userName;
this.topoSession =
new UiTopoSession(this, directory.get(UiSharedTopologyModel.class),
- directory.get(UiTopoLayoutService.class));
+ directory.get(UiTopoLayoutService.class));
}
@Override
@@ -122,6 +122,15 @@
}
/**
+ * Provides a reference to the topology session.
+ *
+ * @return topo session reference
+ */
+ public UiTopoSession topoSession() {
+ return topoSession;
+ }
+
+ /**
* Issues a close on the connection.
*/
synchronized void close() {
@@ -175,7 +184,7 @@
topoSession.destroy();
destroyHandlersAndOverlays();
log.info("GUI client disconnected [close-code={}, message={}]",
- closeCode, message);
+ closeCode, message);
}
@Override
@@ -186,13 +195,13 @@
@Override
public void onMessage(String data) {
- log.debug("onMessage: {}", data);
lastActive = System.currentTimeMillis();
try {
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
String type = message.path(EVENT).asText(UNKNOWN);
UiMessageHandler handler = handlers.get(type);
if (handler != null) {
+ log.debug("RX message: {}", message);
handler.process(message);
} else {
log.warn("No GUI message handler for type {}", type);
@@ -208,6 +217,7 @@
try {
if (connection.isOpen()) {
connection.sendMessage(message.toString());
+ log.debug("TX message: {}", message);
}
} catch (IOException e) {
log.warn("Unable to send message {} to GUI due to {}", message, e);
@@ -257,7 +267,7 @@
}
});
log.debug("#handlers = {}, #overlays = {}", handlers.size(),
- overlayCache.size());
+ overlayCache.size());
}
// Destroys message handlers.
@@ -284,7 +294,7 @@
.put(ID, node.id().toString())
.put(IP, node.ip().toString())
.put(TopoConstants.Glyphs.UI_ATTACHED,
- node.equals(service.getLocalNode()));
+ node.equals(service.getLocalNode()));
instances.add(instance);
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
new file mode 100644
index 0000000..be19389
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2016-present 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.topo;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.incubator.net.PortStatisticsService;
+import org.onosproject.incubator.net.tunnel.TunnelService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.region.Region;
+import org.onosproject.net.statistic.StatisticService;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiHost;
+import org.onosproject.ui.model.topo.UiLink;
+import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiTopoLayout;
+
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Facility for creating JSON messages to send to the topology view in the
+ * Web client.
+ */
+class Topo2Jsonifier {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ private ServiceDirectory directory;
+ private ClusterService clusterService;
+ private DeviceService deviceService;
+ private LinkService linkService;
+ private HostService hostService;
+ private MastershipService mastershipService;
+ private IntentService intentService;
+ private FlowRuleService flowService;
+ private StatisticService flowStatsService;
+ private PortStatisticsService portStatsService;
+ private TopologyService topologyService;
+ private TunnelService tunnelService;
+
+
+ /**
+ * Creates an instance with a reference to the services directory, so that
+ * additional information about network elements may be looked up on
+ * on the fly.
+ *
+ * @param directory service directory
+ */
+ Topo2Jsonifier(ServiceDirectory directory) {
+ this.directory = checkNotNull(directory, "Directory cannot be null");
+
+ clusterService = directory.get(ClusterService.class);
+ deviceService = directory.get(DeviceService.class);
+ linkService = directory.get(LinkService.class);
+ hostService = directory.get(HostService.class);
+ mastershipService = directory.get(MastershipService.class);
+ intentService = directory.get(IntentService.class);
+ flowService = directory.get(FlowRuleService.class);
+ flowStatsService = directory.get(StatisticService.class);
+ portStatsService = directory.get(PortStatisticsService.class);
+ topologyService = directory.get(TopologyService.class);
+ tunnelService = directory.get(TunnelService.class);
+
+ }
+
+ private ObjectNode objectNode() {
+ return mapper.createObjectNode();
+ }
+
+ private ArrayNode arrayNode() {
+ return mapper.createArrayNode();
+ }
+
+ private String nullIsEmpty(Object o) {
+ return o == null ? "" : o.toString();
+ }
+
+
+ /**
+ * Returns a JSON representation of the cluster members (ONOS instances).
+ *
+ * @param instances the instance model objects
+ * @return a JSON representation of the data
+ */
+ ObjectNode instances(List<UiClusterMember> instances) {
+ NodeId local = clusterService.getLocalNode().id();
+ ObjectNode payload = objectNode();
+
+ ArrayNode members = arrayNode();
+ payload.set("members", members);
+ for (UiClusterMember member : instances) {
+ members.add(json(member, member.id().equals(local)));
+ }
+
+ return payload;
+ }
+
+ private ObjectNode json(UiClusterMember member, boolean isUiAttached) {
+ return objectNode()
+ .put("id", member.id().toString())
+ .put("ip", member.ip().toString())
+ .put("online", member.isOnline())
+ .put("ready", member.isReady())
+ .put("uiAttached", isUiAttached)
+ .put("switches", member.deviceCount());
+ }
+
+ /**
+ * Returns a JSON representation of the layout to use for displaying in
+ * the topology view.
+ *
+ * @param layout the layout to transform
+ * @return a JSON representation of the data
+ */
+ ObjectNode layout(UiTopoLayout layout) {
+ return objectNode()
+ .put("id", layout.id().toString())
+ .put("parent", nullIsEmpty(layout.parent()))
+ .put("region", nullIsEmpty(layout.regionId()))
+ .put("regionName", regionName(layout.region()));
+ }
+
+ private String regionName(Region region) {
+ return region == null ? "" : region.name();
+ }
+
+ /**
+ * Returns a JSON representation of the region to display in the topology
+ * view.
+ *
+ * @param region the region to transform to JSON
+ * @return a JSON representation of the data
+ */
+ ObjectNode region(UiRegion region) {
+ ObjectNode payload = objectNode();
+
+ if (region == null) {
+ payload.put("note", "no-region");
+ return payload;
+ }
+
+ payload.put("id", region.id().toString());
+
+ ArrayNode layerOrder = arrayNode();
+ payload.set("layerOrder", layerOrder);
+ region.layerOrder().forEach(layerOrder::add);
+
+ ArrayNode devices = arrayNode();
+ payload.set("devices", devices);
+ for (UiDevice device : region.devices()) {
+ devices.add(json(device));
+ }
+
+ ArrayNode hosts = arrayNode();
+ payload.set("hosts", hosts);
+ for (UiHost host : region.hosts()) {
+ hosts.add(json(host));
+ }
+
+ ArrayNode links = arrayNode();
+ payload.set("links", links);
+ for (UiLink link : region.links()) {
+ links.add(json(link));
+ }
+
+ return payload;
+ }
+
+ private ObjectNode json(UiDevice device) {
+ ObjectNode node = objectNode()
+ .put("id", device.id().toString())
+ .put("type", device.type())
+ .put("online", device.isOnline())
+ .put("master", device.master().toString())
+ .put("layer", device.layer());
+
+ // TODO: complete device details
+// addLabels(node, device);
+// addProps(node, device);
+// addGeoLocation(node, device);
+// addMetaUi(node, device);
+
+ return node;
+ }
+
+ private void addLabels(ObjectNode node, UiDevice device) {
+
+ }
+
+ private ObjectNode json(UiHost host) {
+ return objectNode()
+ .put("id", host.id().toString())
+ .put("layer", host.layer());
+ // TODO: complete host details
+ }
+
+
+ private ObjectNode json(UiLink link) {
+ return objectNode()
+ .put("id", link.id().toString());
+ // TODO: complete link details
+ }
+
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
new file mode 100644
index 0000000..21023fa
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2016-present 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.topo;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiConnection;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.impl.UiWebSocket;
+import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiTopoLayout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+
+/*
+ NOTES:
+
+ The original topology view message handler was broken into two classes
+ TopologyViewMessageHandler, and TopologyViewMessageHandlerBase.
+ We do not need to follow that model necessarily. Starting with a
+ single class, and breaking it apart later if necessary.
+
+ Need to figure out the connection between this message handler and the
+ new way of doing things with UiTopoSession...
+
+ */
+
+/**
+ * Server-side component for interacting with the new "Region aware" topology
+ * view in the Web UI.
+ */
+public class Topo2ViewMessageHandler extends UiMessageHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ // === Inbound event identifiers
+ private static final String TOPO2_START = "topo2Start";
+ private static final String TOPO2_STOP = "topo2Stop";
+
+ // === Outbound event identifiers
+ private static final String CURRENT_LAYOUT = "topo2CurrentLayout";
+ private static final String CURRENT_REGION = "topo2CurrentRegion";
+ private static final String ALL_INSTANCES = "topo2AllInstances";
+ private static final String TOPO_START_DONE = "topo2StartDone";
+
+ private UiTopoSession topoSession;
+ private Topo2Jsonifier t2json;
+
+
+ @Override
+ public void init(UiConnection connection, ServiceDirectory directory) {
+ super.init(connection, directory);
+
+ // get the topo session from the UiWebSocket
+ topoSession = ((UiWebSocket) connection).topoSession();
+ t2json = new Topo2Jsonifier(directory);
+ }
+
+ @Override
+ protected Collection<RequestHandler> createRequestHandlers() {
+ return ImmutableSet.of(
+ new Topo2Start(),
+ new Topo2Stop()
+ );
+ }
+
+ // ==================================================================
+
+
+ private final class Topo2Start extends RequestHandler {
+ private Topo2Start() {
+ super(TOPO2_START);
+ }
+
+ @Override
+ public void process(long sid, ObjectNode payload) {
+ // client view is ready to receive data to display; so start up
+ // server-side processing, and send over initial state
+
+ log.debug("topo2Start: {}", payload);
+
+ List<UiClusterMember> instances = topoSession.getAllInstances();
+ sendMessage(ALL_INSTANCES, t2json.instances(instances));
+
+ UiTopoLayout currentLayout = topoSession.currentLayout();
+ sendMessage(CURRENT_LAYOUT, t2json.layout(currentLayout));
+
+ UiRegion region = topoSession.getRegion(currentLayout);
+ sendMessage(CURRENT_REGION, t2json.region(region));
+
+ // TODO: send information about devices/hosts/links in non-region
+ // TODO: send information about "linked, peer" regions
+
+ sendMessage(TOPO_START_DONE, null);
+
+
+ // OLD CODE DID THE FOLLOWING...
+// addListeners();
+// sendAllInstances(null);
+// sendAllDevices();
+// sendAllLinks();
+// sendAllHosts();
+// sendTopoStartDone();
+ }
+ }
+
+ private final class Topo2Stop extends RequestHandler {
+ private Topo2Stop() {
+ super(TOPO2_STOP);
+ }
+
+ @Override
+ public void process(long sid, ObjectNode payload) {
+ // client view has gone away; so shut down server-side processing
+ // TODO: implement...
+
+ log.debug("topo2Stop: {}", payload);
+
+ // OLD CODE DID THE FOLLOWING...
+// removeListeners();
+// stopSummaryMonitoring();
+// traffic.stopMonitoring();
+ }
+ }
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index a693565..1c9fc9d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -21,10 +21,14 @@
import org.onosproject.ui.impl.topo.model.UiModelEvent;
import org.onosproject.ui.impl.topo.model.UiModelListener;
import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
+import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiRegion;
import org.onosproject.ui.model.topo.UiTopoLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
+
/**
* Coordinates with the {@link UiTopoLayoutService} to access
* {@link UiTopoLayout}s, and with the {@link UiSharedTopologyModel} which
@@ -34,6 +38,10 @@
* Note that an instance of this class will be created for each
* {@link UiWebSocket} connection, and will contain
* the state of how the topology is laid out for the logged-in user.
+ * <p>
+ * The expected pattern is for the {@link Topo2ViewMessageHandler} to obtain
+ * a reference to the session instance (via the {@link UiWebSocket}), and
+ * interact with it when topo-related events come in from the client.
*/
public class UiTopoSession implements UiModelListener {
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -129,4 +137,23 @@
public void enableEvent(boolean enabled) {
messagesEnabled = enabled;
}
+
+ /**
+ * Returns the list of ONOS instances (cluster members).
+ *
+ * @return the list of ONOS instances
+ */
+ public List<UiClusterMember> getAllInstances() {
+ return sharedModel.getClusterMembers();
+ }
+
+ /**
+ * Returns the region for the specified layout.
+ *
+ * @param layout layout filter
+ * @return region that the layout is based upon
+ */
+ public UiRegion getRegion(UiTopoLayout layout) {
+ return sharedModel.getRegion(layout);
+ }
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
index 6566511..8754550 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
@@ -41,6 +41,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
import java.util.Set;
import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
@@ -153,6 +154,10 @@
}
}
+ List<UiClusterMember> getAllClusterMembers() {
+ return uiTopology.allClusterMembers();
+ }
+
// === MASTERSHIP CHANGES
@@ -199,7 +204,7 @@
// package private for unit test access
UiRegion accessRegion(RegionId id) {
- return uiTopology.findRegion(id);
+ return id == null ? null : uiTopology.findRegion(id);
}
// invoked from UiSharedTopologyModel region listener
@@ -484,4 +489,5 @@
public int hostCount() {
return uiTopology.hostCount();
}
+
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
index 37ac126..54c92f6 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
@@ -16,9 +16,6 @@
package org.onosproject.ui.impl.topo.model;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -64,9 +61,19 @@
import org.onosproject.net.topology.TopologyService;
import org.onosproject.ui.impl.topo.UiTopoSession;
import org.onosproject.ui.model.ServiceBundle;
+import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiElement;
+import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiTopoLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
/**
* Service that creates and maintains the UI-model of the network topology.
*/
@@ -190,6 +197,41 @@
removeListener(session);
}
+
+ // =======================================================================
+ // methods that the topo session will use to extract information from us
+
+ /**
+ * Returns the list of cluster members stored in our model cache.
+ *
+ * @return list of cluster members
+ */
+ public List<UiClusterMember> getClusterMembers() {
+ return cache.getAllClusterMembers();
+ }
+
+ public Set<UiElement> getElements(UiTopoLayout layout) {
+ Set<UiElement> results = new HashSet<>();
+
+ // TODO: figure out how to extract the appropriate nodes
+ // from the cache, for the given layout.
+
+ return results;
+ }
+
+ /**
+ * Returns the region for the given layout.
+ *
+ * @param layout layout filter
+ * @return the region the layout is based upon
+ */
+ public UiRegion getRegion(UiTopoLayout layout) {
+ return cache.accessRegion(layout.regionId());
+ }
+
+ // =====================================================================
+
+
/**
* Default implementation of service bundle to return references to our
* dynamically injected services.
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index 0f370cf..bd1f44d 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -256,6 +256,8 @@
});
}
+ // TODO: simplify listener handling (see theme.js for sample code)
+
function addOpenListener(callback) {
var id = nextListenerId++,
cb = fs.isF(callback),
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
new file mode 100644
index 0000000..63b5dfb
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (theme) -- CSS file
+ */
+
+/* --- Base SVG Layer --- */
+
+#ov-topo2 svg {
+ /*background-color: #f4f4f4;*/
+ background-color: goldenrod; /* just for testing */
+}
+
+/* --- "No Devices" Layer --- */
+
+#ov-topo2 svg .noDevsBird {
+ fill: #db7773;
+}
+
+#ov-topo2 svg #topo2-noDevsLayer text {
+ fill: #7e9aa8;
+}
+
+/* --- Topo Map --- */
+
+#ov-topo2 svg #topo2-map {
+ stroke-width: 2px;
+ stroke: #f4f4f4;
+ fill: #e5e5e6;
+}
+
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.css b/web/gui/src/main/webapp/app/view/topo2/topo2.css
new file mode 100644
index 0000000..7f878a5
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.css
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (layout) -- CSS file
+ */
+
+/* --- Base SVG Layer --- */
+
+#ov-topo2 svg {
+ /* prevents the little cut/copy/paste square that would appear on iPad */
+ -webkit-user-select: none;
+}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.html b/web/gui/src/main/webapp/app/view/topo2/topo2.html
new file mode 100644
index 0000000..9b987a6
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.html
@@ -0,0 +1,7 @@
+<!-- Topology View partial HTML -->
+<div id="ov-topo2">
+ <svg viewBox="0 0 1000 1000"
+ resize offset-height="56" offset-width="12"
+ notifier="notifyResize()">
+ </svg>
+</div>
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
new file mode 100644
index 0000000..ce90af6
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+/*
+ ONOS GUI -- Topology View Module
+
+ NOTE: currently under development to support Regions.
+ */
+
+(function () {
+ 'use strict';
+
+ // references to injected services
+ var $scope, $log, $loc,
+ fs, mast, ks, zs,
+ gs, ms, sus, flash,
+ wss, ps, th,
+ t2es, t2fs;
+
+ // DOM elements
+ var ovtopo2, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
+
+ // Internal state
+ var zoomer, actionMap;
+
+
+ // === Helper Functions
+
+ // callback invoked when the SVG view has been resized..
+ function svgResized(s) {
+ $log.debug("topo2 view resized", s);
+ }
+
+ function setUpKeys(overlayKeys) {
+ $log.debug('topo2: set up keys....');
+ }
+
+ // === Controller Definition -----------------------------------------
+
+ angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote'])
+ .controller('OvTopo2Ctrl',
+ ['$scope', '$log', '$location',
+ 'FnService', 'MastService', 'KeyService', 'ZoomService',
+ 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
+ 'WebSocketService', 'PrefsService', 'ThemeService',
+ 'Topo2EventService', 'Topo2ForceService',
+
+ function (_$scope_, _$log_, _$loc_,
+ _fs_, _mast_, _ks_, _zs_,
+ _gs_, _ms_, _sus_, _flash_,
+ _wss_, _ps_, _th_,
+ _t2es_, _t2fs_) {
+
+ var params = _$loc_.search(),
+ projection,
+ dim,
+ wh,
+ uplink = {
+ // provides function calls back into this space
+ // showNoDevs: showNoDevs,
+ // projection: function () { return projection; },
+ // zoomLayer: function () { return zoomLayer; },
+ // zoomer: function () { return zoomer; },
+ // opacifyMap: opacifyMap,
+ // topoStartDone: topoStartDone
+ };
+
+ $scope = _$scope_;
+ $log = _$log_;
+ $loc = _$loc_;
+
+ fs = _fs_;
+ mast = _mast_;
+ ks = _ks_;
+ zs = _zs_;
+
+ gs = _gs_;
+ ms = _ms_;
+ sus = _sus_;
+ flash = _flash_;
+
+ wss = _wss_;
+ ps = _ps_;
+ th = _th_;
+
+ t2es = _t2es_;
+ t2fs = _t2fs_;
+
+ // capture selected intent parameters (if they are set in the
+ // query string) so that the traffic overlay can highlight
+ // the path for that intent
+ if (params.intentKey && params.intentAppId && params.intentAppName) {
+ $scope.intentData = {
+ key: params.intentKey,
+ appId: params.intentAppId,
+ appName: params.intentAppName
+ };
+ }
+
+ $scope.notifyResize = function () {
+ svgResized(fs.windowSize(mast.mastHeight()));
+ };
+
+ // Cleanup on destroyed scope..
+ $scope.$on('$destroy', function () {
+ $log.log('OvTopo2Ctrl is saying Buh-Bye!');
+ t2es.stop();
+ ks.unbindKeys();
+ t2fs.destroy();
+ });
+
+ // svg layer and initialization of components
+ ovtopo2 = d3.select('#ov-topo2');
+ svg = ovtopo2.select('svg');
+ // set the svg size to match that of the window, less the masthead
+ wh = fs.windowSize(mast.mastHeight());
+ $log.debug('setting topo SVG size to', wh);
+ svg.attr(wh);
+ dim = [wh.width, wh.height];
+
+
+ // set up our keyboard shortcut bindings
+ setUpKeys();
+
+ // make sure we can respond to topology events from the server
+ t2es.bindHandlers();
+
+ // initialize the force layout, ready to render the topology
+ t2fs.init();
+
+
+ // =-=-=-=-=-=-=-=-
+ // TODO: in future, we will load background map data
+ // asynchronously (hence the promise) and then chain off
+ // there to send the topo2start event to the server.
+ // For now, we'll send the event inline...
+ t2es.start();
+
+
+ // === ORIGINAL CODE ===
+
+ // setUpKeys();
+ // setUpToolbar();
+ // setUpDefs();
+ // setUpZoom();
+ // setUpNoDevs();
+ /*
+ setUpMap().then(
+ function (proj) {
+ var z = ps.getPrefs('topo_zoom', { tx:0, ty:0, sc:1 });
+ zoomer.panZoom([z.tx, z.ty], z.sc);
+ $log.debug('** Zoom restored:', z);
+
+ projection = proj;
+ $log.debug('** We installed the projection:', proj);
+ flash.enable(false);
+ toggleMap(prefsState.bg);
+ flash.enable(true);
+ mapShader(true);
+
+ // now we have the map projection, we are ready for
+ // the server to send us device/host data...
+ tes.start();
+ // need to do the following so we immediately get
+ // the summary panel data back from the server
+ restoreSummaryFromPrefs();
+ }
+ );
+ */
+ // tes.bindHandlers();
+ // setUpSprites();
+
+ // forceG = zoomLayer.append('g').attr('id', 'topo-force');
+ // tfs.initForce(svg, forceG, uplink, dim);
+ // tis.initInst({ showMastership: tfs.showMastership });
+ // tps.initPanels();
+
+ // restoreConfigFromPrefs();
+ // ttbs.setDefaultOverlay(prefsState.ovidx);
+
+ // $log.debug('registered overlays...', tov.list());
+
+ $log.log('OvTopo2Ctrl has been created');
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
new file mode 100644
index 0000000..83dcae2
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+/*
+ ONOS GUI -- Topology Event Module.
+
+ Defines the conduit between the client and the server:
+ - provides a clean API for sending events to the server
+ - dispatches incoming events from the server to the appropriate sub-module
+
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, wss, t2fs;
+
+ // internal state
+ var handlerMap,
+ openListener;
+
+ // TODO: only add heartbeat timer etc. if we really need to be doing that..
+
+ // ========================== Helper Functions
+
+ function createHandlerMap() {
+ handlerMap = {
+ topo2AllInstances: t2fs,
+ topo2CurrentLayout: t2fs,
+ topo2CurrentRegion: t2fs,
+ topo2StartDone: t2fs
+
+ // Add further event names / module references as needed
+ };
+ }
+
+ function wsOpen(host, url) {
+ $log.debug('topo2Event: WSopen - cluster node:', host, 'URL:', url);
+ // tell the server we are ready to receive topo events
+ wss.sendEvent('topo2Start');
+ }
+
+ // bind our event handlers to the web socket service, so that our
+ // callbacks get invoked for incoming events
+ function bindHandlers() {
+ wss.bindHandlers(handlerMap);
+ $log.debug('topo2 event handlers bound');
+ }
+
+ // tell the server we are ready to receive topology events
+ function start() {
+ // in case we fail over to a new server,
+ // listen for wsock-open events
+ openListener = wss.addOpenListener(wsOpen);
+ wss.sendEvent('topo2Start');
+ $log.debug('topo2 comms started');
+ }
+
+ // tell the server we no longer wish to receive topology events
+ function stop() {
+ wss.sendEvent('topo2Stop');
+ wss.unbindHandlers(handlerMap);
+ wss.removeOpenListener(openListener);
+ openListener = null;
+ $log.debug('topo2 comms stopped');
+ }
+
+ // ========================== Main Service Definition
+
+ angular.module('ovTopo2')
+ .factory('Topo2EventService',
+ ['$log', 'WebSocketService', 'Topo2ForceService',
+
+ function (_$log_, _wss_, _t2fs_) {
+ $log = _$log_;
+ wss = _wss_;
+ t2fs = _t2fs_;
+
+ // deferred creation of handler map, so module references are good
+ createHandlerMap();
+
+ return {
+ bindHandlers: bindHandlers,
+ start: start,
+ stop: stop
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
new file mode 100644
index 0000000..db6a1c2
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+/*
+ ONOS GUI -- Topology Force Module.
+ Visualization of the topology in an SVG layer, using a D3 Force Layout.
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, wss;
+
+ // ========================== Helper Functions
+
+ function init() {
+ $log.debug('Initialize topo force layout');
+ }
+
+ function destroy() {
+ $log.debug('Destroy topo force layout');
+ }
+
+ // ========================== Event Handlers
+
+ function allInstances(data) {
+ $log.debug('>> topo2AllInstances event:', data)
+ }
+
+ function currentLayout(data) {
+ $log.debug('>> topo2CurrentLayout event:', data)
+ }
+
+ function currentRegion(data) {
+ $log.debug('>> topo2CurrentRegion event:', data)
+ }
+
+ function startDone(data) {
+ $log.debug('>> topo2StartDone event:', data)
+ }
+
+ // ========================== Main Service Definition
+
+ angular.module('ovTopo2')
+ .factory('Topo2ForceService',
+ ['$log', 'WebSocketService',
+
+ function (_$log_, _wss_) {
+ $log = _$log_;
+ wss = _wss_;
+
+ return {
+ init: init,
+ destroy: destroy,
+ topo2AllInstances: allInstances,
+ topo2CurrentLayout: currentLayout,
+ topo2CurrentRegion: currentRegion,
+ topo2StartDone: startDone
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index e4fa6e2..22865a1 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -126,6 +126,13 @@
<link rel="stylesheet" href="app/fw/widget/table.css">
<link rel="stylesheet" href="app/fw/widget/table-theme.css">
+ <!-- Under development for Region support. -->
+ <script src="app/view/topo2/topo2.js"></script>
+ <script src="app/view/topo2/topo2Event.js"></script>
+ <script src="app/view/topo2/topo2Force.js"></script>
+ <link rel="stylesheet" href="app/view/topo2/topo2.css">
+ <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
+
<!-- Builtin views javascript. -->
<script src="app/view/topo/topo.js"></script>
<script src="app/view/topo/topoD3.js"></script>