package org.opendaylight.controller.topology.web;
import java.awt.Dimension;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
+import org.opendaylight.controller.sal.authorization.Resource;
import org.opendaylight.controller.sal.authorization.UserLevel;
import org.opendaylight.controller.sal.core.Bandwidth;
import org.opendaylight.controller.sal.core.Edge;
import org.opendaylight.controller.sal.core.Host;
import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.core.Property;
import org.opendaylight.controller.sal.packet.address.EthernetAddress;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.switchmanager.Switch;
import org.opendaylight.controller.switchmanager.SwitchConfig;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.web.DaylightWebUtil;
+import org.opendaylight.controller.web.IDaylightWeb;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/")
-public class Topology {
+public class Topology implements IObjectReader, IConfigurationAware {
+ private static String ROOT = GlobalConstants.STARTUPHOME.toString();
+ private String topologyWebFileName = null;
- protected Map<String, Map<String, Object>> cache = new HashMap<String, Map<String, Object>>();
+ protected Map<String, Map<String, Map<String, Object>>> metaCache = new HashMap<String, Map<String, Map<String, Object>>>();
protected Map<String, Map<String, Object>> stagedNodes;
protected Map<String, Map<String, Object>> newNodes;
- protected Integer nodeHash = null;
- protected Integer hostHash = null;
- protected Integer nodeSingleHash = null;
- protected Integer nodeConfigurationHash = null;
+
+ protected Map<String, Integer> metaNodeHash = new HashMap<String, Integer>();
+ protected Map<String, Integer> metaHostHash = new HashMap<String, Integer>();
+ protected Map<String, Integer> metaNodeSingleHash = new HashMap<String, Integer>();
+ protected Map<String, Integer> metaNodeConfigurationHash = new HashMap<String, Integer>();
+
+ public Topology() {
+ ServiceHelper.registerGlobalService(IConfigurationAware.class, this, null);
+ topologyWebFileName = ROOT + "topologyCache.sav";
+ loadConfiguration();
+ }
/**
* Topology of nodes and hosts in the network in JSON format.
*/
@RequestMapping(value = "/visual.json", method = RequestMethod.GET)
@ResponseBody
- public Collection<Map<String, Object>> getLinkData() {
+ public Collection<Map<String, Object>> getLinkData(@RequestParam(required = false) String container, HttpServletRequest request) {
+ String containerName = DaylightWebUtil.getAuthorizedContainer(request, container, this);
+
ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
- .getInstance(ITopologyManager.class, "default", this);
- if (topologyManager == null) return null;
+ .getInstance(ITopologyManager.class, containerName, this);
+ if (topologyManager == null) {
+ return null;
+ }
ISwitchManager switchManager = (ISwitchManager) ServiceHelper
- .getInstance(ISwitchManager.class, "default", this);
- if (switchManager == null) return null;
+ .getInstance(ISwitchManager.class, containerName, this);
+ if (switchManager == null) {
+ return null;
+ }
Map<Node, Set<Edge>> nodeEdges = topologyManager.getNodeEdges();
Map<Node, Set<NodeConnector>> hostEdges = topologyManager
switchConfigurations.add(config);
}
+ // initialize cache if needed
+ if (!metaCache.containsKey(containerName)) {
+ metaCache.put(containerName, new HashMap<String, Map<String, Object>>());
+ // initialize hashes
+ metaNodeHash.put(containerName, null);
+ metaHostHash.put(containerName, null);
+ metaNodeSingleHash.put(containerName, null);
+ metaNodeConfigurationHash.put(containerName, null);
+ }
+
// return cache if topology hasn't changed
if (
- (nodeHash != null && hostHash != null && nodeSingleHash != null && nodeConfigurationHash != null) &&
- nodeHash == nodeEdges.hashCode() && hostHash == hostEdges.hashCode() && nodeSingleHash == nodes.hashCode() && nodeConfigurationHash == switchConfigurations.hashCode()
+ (metaNodeHash.get(containerName) != null && metaHostHash.get(containerName) != null && metaNodeSingleHash.get(containerName) != null && metaNodeConfigurationHash.get(containerName) != null) &&
+ metaNodeHash.get(containerName).equals(nodeEdges.hashCode()) && metaHostHash.get(containerName).equals(hostEdges.hashCode()) && metaNodeSingleHash.get(containerName).equals(nodes.hashCode()) && metaNodeConfigurationHash.get(containerName).equals(switchConfigurations.hashCode())
) {
- return cache.values();
+ return metaCache.get(containerName).values();
}
// cache has changed, we must assign the new values
- nodeHash = nodeEdges.hashCode();
- hostHash = hostEdges.hashCode();
- nodeSingleHash = nodes.hashCode();
- nodeConfigurationHash = switchConfigurations.hashCode();
+ metaNodeHash.put(containerName, nodeEdges.hashCode());
+ metaHostHash.put(containerName, hostEdges.hashCode());
+ metaNodeSingleHash.put(containerName, nodes.hashCode());
+ metaNodeConfigurationHash.put(containerName, switchConfigurations.hashCode());
stagedNodes = new HashMap<String, Map<String, Object>>();
newNodes = new HashMap<String, Map<String, Object>>();
// nodeEdges addition
- addNodes(nodeEdges, topologyManager, switchManager);
+ addNodes(nodeEdges, topologyManager, switchManager, containerName);
// single nodes addition
- addSingleNodes(nodes, switchManager);
+ addSingleNodes(nodes, switchManager, containerName);
// hostNodes addition
- addHostNodes(hostEdges, topologyManager);
+ addHostNodes(hostEdges, topologyManager, containerName);
- repositionTopology();
+ repositionTopology(containerName);
- return cache.values();
+ return metaCache.get(containerName).values();
}
/**
* @param topology - the topology instance
*/
private void addNodes(Map<Node, Set<Edge>> nodeEdges,
- ITopologyManager topology, ISwitchManager switchManager) {
+ ITopologyManager topology, ISwitchManager switchManager, String containerName) {
Bandwidth bandwidth = new Bandwidth(0);
Map<Edge, Set<Property>> properties = topology.getEdges();
String description = switchManager.getNodeDescription(n);
NodeBean node = createNodeBean(description, n);
+ // skip production node
+ if (nodeIgnore(n)) {
+ continue;
+ }
+
List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
Set<Edge> links = e.getValue();
for (Edge link : links) {
+ if (edgeIgnore(link)) {
+ continue;
+ }
for (Property p : properties.get(link)) {
if (p instanceof Bandwidth) {
bandwidth = (Bandwidth) p;
}
node.setLinks(adjacencies);
- if (cache.containsKey(node.id())) {
+ if (metaCache.get(containerName).containsKey(node.id())) {
// retrieve node from cache
- Map<String, Object> nodeEntry = cache.get(node.id());
+ Map<String, Object> nodeEntry = metaCache.get(containerName).get(node.id());
Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
data.put("$desc", description);
}
}
+ /**
+ * Check if this node shouldn't appear in the visual topology
+ *
+ * @param node
+ * @return
+ */
+ private boolean nodeIgnore(Node node) {
+ String nodeType = node.getType();
+
+ // add other node types to ignore later
+ if (nodeType.equals(NodeIDType.PRODUCTION)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if this edge shouldn't appear in the visual topology
+ *
+ * @param edge
+ * @return
+ */
+ private boolean edgeIgnore(Edge edge) {
+ NodeConnector headNodeConnector = edge.getHeadNodeConnector();
+ Node headNode = headNodeConnector.getNode();
+ if (nodeIgnore(headNode)) {
+ return true;
+ }
+
+ NodeConnector tailNodeConnector = edge.getTailNodeConnector();
+ Node tailNode = tailNodeConnector.getNode();
+ if (nodeIgnore(tailNode)) {
+ return true;
+ }
+
+ return false;
+ }
+
protected NodeBean createNodeBean(String description, Node node) {
String name = (description == null ||
description.trim().isEmpty() ||
}
@SuppressWarnings("unchecked")
- private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager) {
- if (nodes == null) return;
+ private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager, String containerName) {
+ if (nodes == null) {
+ return;
+ }
for (Switch sw : nodes) {
Node n = sw.getNode();
+
+ // skip production node
+ if (nodeIgnore(n)) {
+ continue;
+ }
String description = switchManager.getNodeDescription(n);
- if ((stagedNodes.containsKey(n.toString()) && cache.containsKey(n.toString())) || newNodes.containsKey(n.toString())) {
+ if ((stagedNodes.containsKey(n.toString()) && metaCache.get(containerName).containsKey(n.toString())) || newNodes.containsKey(n.toString())) {
continue;
}
NodeBean node = createNodeBean(description, n);
// FIXME still doesn't display standalone node when last remaining link is removed
- if (cache.containsKey(node.id()) && !stagedNodes.containsKey(node.id())) {
- Map<String, Object> nodeEntry = cache.get(node.id());
+ if (metaCache.get(containerName).containsKey(node.id()) && !stagedNodes.containsKey(node.id())) {
+ Map<String, Object> nodeEntry = metaCache.get(containerName).get(node.id());
Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
data.put("$desc", description);
nodeEntry.put("data", data);
+ // clear adjacencies since this is now a single node
+ nodeEntry.put("adjacencies", new LinkedList<Map<String, Object>>());
stagedNodes.put(node.id(), nodeEntry);
} else {
newNodes.put(node.id(), node.out());
* @param topology - topology instance
*/
private void addHostNodes(Map<Node, Set<NodeConnector>> hostEdges,
- ITopologyManager topology) {
+ ITopologyManager topology, String containerName) {
for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
for (NodeConnector connector : e.getValue()) {
Host host = topology.getHostAttachedToNodeConnector(connector);
adjacencies.add(edge.out());
hostBean.setLinks(adjacencies);
- if (cache.containsKey(hostId)) {
- Map<String, Object> hostEntry = cache.get(hostId);
+ if (metaCache.get(containerName).containsKey(hostId)) {
+ Map<String, Object> hostEntry = metaCache.get(containerName).get(hostId);
hostEntry.put("adjacencies", adjacencies);
stagedNodes.put(hostId, hostEntry);
} else {
/**
* Re-position nodes in circular layout
*/
- private void repositionTopology() {
+ private void repositionTopology(String containerName) {
Graph<String, String> graph = new SparseMultigraph<String, String>();
- cache.clear();
- cache.putAll(stagedNodes);
- cache.putAll(newNodes);
- for (Map<String, Object> on : cache.values()) {
+
+ metaCache.get(containerName).clear();
+ metaCache.get(containerName).putAll(stagedNodes);
+ metaCache.get(containerName).putAll(newNodes);
+
+ for (Map<String, Object> on : metaCache.get(containerName).values()) {
graph.addVertex(on.toString());
List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
}
}
- CircleLayout<String,String> layout = new CircleLayout<String,String>(graph);
+ CircleLayout<String, String> layout = new CircleLayout<String, String>(graph);
layout.setSize(new Dimension(1200, 365));
for (Map.Entry<String, Map<String, Object>> v : newNodes.entrySet()) {
Double x = layout.transform(v.getKey()).getX();
@RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> post(@PathVariable String nodeId, @RequestParam(required = true) String x,
- @RequestParam(required = true) String y, HttpServletRequest request) {
+ @RequestParam(required = true) String y, @RequestParam(required = false) String container,
+ HttpServletRequest request) {
if (!authorize(UserLevel.NETWORKADMIN, request)) {
return new HashMap<String, Object>(); // silently disregard new node position
}
+ String containerName = getAuthorizedContainer(request, container);
+
String id = new String(nodeId);
- if (!cache.containsKey(id))
+ if (!metaCache.get(containerName).containsKey(id)) {
return null;
+ }
- Map<String, Object> node = cache.get(id);
+ Map<String, Object> node = metaCache.get(containerName).get(id);
Map<String, String> data = (Map<String, String>) node.get("data");
data.put("$x", x);
}
return false;
}
-}
+
+ private String getAuthorizedContainer(HttpServletRequest request, String container) {
+ String username = request.getUserPrincipal().getName();
+ IContainerAuthorization containerAuthorization = (IContainerAuthorization) ServiceHelper.
+ getGlobalInstance(IContainerAuthorization.class, this);
+ if (containerAuthorization != null) {
+ Set<Resource> resources = containerAuthorization.getAllResourcesforUser(username);
+ if (authorizeContainer(container, resources)) {
+ return container;
+ }
+ }
+
+ return GlobalConstants.DEFAULT.toString();
+ }
+
+ private boolean authorizeContainer(String container, Set<Resource> resources) {
+ for(Resource resource : resources) {
+ String containerName = (String) resource.getResource();
+ if (containerName.equals(container)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void loadConfiguration() {
+ ObjectReader objReader = new ObjectReader();
+ metaCache = (Map<String, Map<String, Map<String, Object>>>) objReader.read(this, topologyWebFileName);
+ if (metaCache == null) metaCache = new HashMap<String, Map<String, Map<String, Object>>>();
+ }
+
+ @Override
+ public Status saveConfiguration() {
+ ObjectWriter objWriter = new ObjectWriter();
+ objWriter.write(metaCache, topologyWebFileName);
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+ @Override
+ public Object readObject(ObjectInputStream ois)
+ throws FileNotFoundException, IOException, ClassNotFoundException {
+ // Perform the class deserialization locally, from inside the package where the class is defined
+ return ois.readObject();
+ }
+}
\ No newline at end of file