3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.topology.web;
12 import java.awt.Dimension;
13 import java.nio.ByteBuffer;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.LinkedList;
18 import java.util.List;
22 import javax.servlet.http.HttpServletRequest;
24 import org.opendaylight.controller.sal.authorization.UserLevel;
25 import org.opendaylight.controller.sal.core.Bandwidth;
26 import org.opendaylight.controller.sal.core.Edge;
27 import org.opendaylight.controller.sal.core.Host;
28 import org.opendaylight.controller.sal.core.Node;
29 import org.opendaylight.controller.sal.core.NodeConnector;
30 import org.opendaylight.controller.sal.core.Property;
31 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
32 import org.opendaylight.controller.sal.utils.ServiceHelper;
33 import org.opendaylight.controller.switchmanager.ISwitchManager;
34 import org.opendaylight.controller.switchmanager.Switch;
35 import org.opendaylight.controller.switchmanager.SwitchConfig;
36 import org.opendaylight.controller.topologymanager.ITopologyManager;
37 import org.opendaylight.controller.usermanager.IUserManager;
38 import org.springframework.stereotype.Controller;
39 import org.springframework.web.bind.annotation.PathVariable;
40 import org.springframework.web.bind.annotation.RequestMapping;
41 import org.springframework.web.bind.annotation.RequestMethod;
42 import org.springframework.web.bind.annotation.RequestParam;
43 import org.springframework.web.bind.annotation.ResponseBody;
45 import edu.uci.ics.jung.algorithms.layout.CircleLayout;
46 import edu.uci.ics.jung.graph.Graph;
47 import edu.uci.ics.jung.graph.SparseMultigraph;
51 public class Topology {
53 protected Map<String, Map<String, Object>> cache = new HashMap<String, Map<String, Object>>();
54 protected Map<String, Map<String, Object>> stage;
55 protected Map<String, Map<String, Object>> newNodes;
56 protected Integer nodeHash = null;
57 protected Integer hostHash = null;
58 protected Integer nodeSingleHash = null;
59 protected Integer nodeConfigurationHash = null;
62 * Topology of nodes and hosts in the network in JSON format.
64 * Mainly intended for consumption by the visual topology.
66 * @return - JSON output for visual topology
68 @RequestMapping(value = "/visual.json", method = RequestMethod.GET)
70 public Collection<Map<String, Object>> getLinkData() {
71 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
72 .getInstance(ITopologyManager.class, "default", this);
73 if (topologyManager == null) return null;
74 ISwitchManager switchManager = (ISwitchManager) ServiceHelper
75 .getInstance(ISwitchManager.class, "default", this);
76 if (switchManager == null) return null;
78 Map<Node, Set<Edge>> nodeEdges = topologyManager.getNodeEdges();
79 Map<Node, Set<NodeConnector>> hostEdges = topologyManager
80 .getNodesWithNodeConnectorHost();
81 List<Switch> nodes = switchManager.getNetworkDevices();
83 List<SwitchConfig> switchConfigurations = new ArrayList<SwitchConfig>();
84 for(Switch sw : nodes) {
85 Node n = sw.getNode();
86 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
87 switchConfigurations.add(config);
90 // return cache if topology hasn't changed
92 (nodeHash != null && hostHash != null && nodeSingleHash != null && nodeConfigurationHash != null) &&
93 nodeHash == nodeEdges.hashCode() && hostHash == hostEdges.hashCode() && nodeSingleHash == nodes.hashCode() && nodeConfigurationHash == switchConfigurations.hashCode()
95 return cache.values();
98 // cache has changed, we must assign the new values
99 nodeHash = nodeEdges.hashCode();
100 hostHash = hostEdges.hashCode();
101 nodeSingleHash = nodes.hashCode();
102 nodeConfigurationHash = switchConfigurations.hashCode();
104 stage = new HashMap<String, Map<String, Object>>();
105 newNodes = new HashMap<String, Map<String, Object>>();
107 // nodeEdges addition
108 addNodes(nodeEdges, topologyManager, switchManager);
110 // single nodes addition
111 addSingleNodes(nodes, switchManager);
113 // hostNodes addition
114 addHostNodes(hostEdges, topologyManager);
116 repositionTopology();
118 return cache.values();
122 * Add regular nodes to main topology
124 * @param nodeEdges - node-edges mapping derived from topology
125 * @param topology - the topology instance
127 private void addNodes(Map<Node, Set<Edge>> nodeEdges,
128 ITopologyManager topology, ISwitchManager switchManager) {
129 Bandwidth bandwidth = new Bandwidth(0);
130 Map<Edge, Set<Property>> properties = topology.getEdges();
132 for (Map.Entry<Node, Set<Edge>> e : nodeEdges.entrySet()) {
134 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
135 NodeBean node = createNodeBean(config, n);
137 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
138 Set<Edge> links = e.getValue();
139 for (Edge link : links) {
140 for (Property p : properties.get(link)) {
141 if (p instanceof Bandwidth) {
142 bandwidth = (Bandwidth) p;
146 EdgeBean edge = new EdgeBean(link, bandwidth);
147 adjacencies.add(edge.out());
150 node.setLinks(adjacencies);
151 if (cache.containsKey(node.id())) {
152 Map<String, Object> nodeEntry = cache.get(node.id());
153 if (config != null) {
154 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
155 data.put("$desc", config.getNodeDescription());
156 nodeEntry.put("data", data);
158 stage.put(node.id(), nodeEntry);
160 newNodes.put(node.id(), node.out());
165 protected NodeBean createNodeBean(SwitchConfig config, Node node) {
166 NodeBean bean = null;
167 if (config != null) {
168 bean = new NodeBean(node.toString(), config.getNodeDescription(), NodeType.NODE);
170 bean = new NodeBean(node.toString(), node.toString(), NodeType.NODE);
176 private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager) {
177 if (nodes == null) return;
178 for (Switch sw : nodes) {
179 Node n = sw.getNode();
180 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
181 if (cache.containsKey(n.toString()) || newNodes.containsKey(n.toString())) continue;
182 NodeBean node = createNodeBean(config, n);
183 if (cache.containsKey(node.id())) {
184 Map<String, Object> nodeEntry = cache.get(node.id());
185 if (config != null) {
186 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
187 data.put("$desc", config.getNodeDescription());
188 nodeEntry.put("data", data);
190 stage.put(node.id(), nodeEntry);
192 newNodes.put(node.id(), node.out());
198 * Add regular hosts to main topology
200 * @param hostEdges - node-nodeconnectors host-specific mapping from topology
201 * @param topology - topology instance
203 private void addHostNodes(Map<Node, Set<NodeConnector>> hostEdges,
204 ITopologyManager topology) {
205 for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
206 for (NodeConnector connector : e.getValue()) {
207 Host host = topology.getHostAttachedToNodeConnector(connector);
208 EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
210 ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
211 addressByteBuffer.putShort((short) 0);
212 addressByteBuffer.put(dmac.getValue());
213 addressByteBuffer.rewind();
215 long hid = addressByteBuffer.getLong();
217 NodeBean hostBean = new NodeBean(String.valueOf(hid), host.getNetworkAddressAsString(), NodeType.HOST);
218 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
219 EdgeBean edge = new EdgeBean(connector, hid);
220 adjacencies.add(edge.out());
221 hostBean.setLinks(adjacencies);
223 if (cache.containsKey(String.valueOf(hid))) {
224 stage.put(String.valueOf(hid), cache.get(String.valueOf(hid)));
226 newNodes.put(String.valueOf(hid), hostBean.out());
233 * Re-position nodes in circular layout
235 private void repositionTopology() {
236 Graph<String, String> graph = new SparseMultigraph<String, String>();
239 cache.putAll(newNodes);
240 for (Map<String, Object> on : cache.values()) {
241 graph.addVertex(on.toString());
243 List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
245 for (Map<String, Object> adj : adjacencies) {
247 adj.toString(), adj.get("nodeFrom").toString(),
248 adj.get("nodeTo").toString()
253 CircleLayout layout = new CircleLayout(graph);
254 layout.setSize(new Dimension(1200, 365));
255 for (Map.Entry<String, Map<String, Object>> v : newNodes.entrySet()) {
256 Double x = layout.transform(v.getKey()).getX();
257 Double y = layout.transform(v.getKey()).getY();
259 Map<String, String> nodeData = (HashMap<String, String>) v.getValue().get("data");
260 nodeData.put("$x", (x - 600) + "");
261 nodeData.put("$y", (y - 225) + "");
263 newNodes.get(v.getKey()).put("data", nodeData);
268 * Update node position
270 * This method is mainly used by the visual topology
272 * @param nodeId - The node to update
273 * @return The node object
275 @RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
277 public Map<String, Object> post(@PathVariable String nodeId, @RequestParam(required = true) String x,
278 @RequestParam(required = true) String y, HttpServletRequest request) {
279 if (!authorize(UserLevel.NETWORKADMIN, request)) {
280 return new HashMap<String, Object>(); // silently disregard new node position
283 String id = new String(nodeId);
285 if (!cache.containsKey(id))
288 Map<String, Object> node = cache.get(id);
289 Map<String, String> data = (Map<String, String>) node.get("data");
294 node.put("data", data);
300 * Node object for visual topology
302 protected class NodeBean {
304 protected String name;
305 protected Map<String, String> data;
306 protected List<Map<String, Object>> links;
309 data = new HashMap<String, String>();
310 links = new ArrayList<Map<String, Object>>();
313 public NodeBean(String id, String name, String type) {
317 data.put("$desc", name);
318 data.put("$type", type);
321 public void setLinks(List<Map<String, Object>> links) {
325 public Map<String, Object> out() {
326 Map<String, Object> node = new HashMap<String, Object>();
327 node.put("id", this.id);
328 node.put("name", this.name);
329 node.put("data", this.data);
330 node.put("adjacencies", this.links);
335 public String name() {
345 * Edge object for visual topology
347 protected class EdgeBean {
348 protected NodeConnector source;
349 protected NodeConnector destination;
350 protected Map<String, String> data;
351 protected Long hostId;
354 data = new HashMap<String, String>();
357 public EdgeBean(Edge link, Bandwidth bandwidth) {
359 this.source = link.getHeadNodeConnector();
360 this.destination = link.getTailNodeConnector();
363 data.put("$bandwidth", bandwidth.toString());
364 data.put("$color", bandwidthColor(bandwidth));
365 data.put("$nodeToPort", destination.getID().toString());
366 data.put("$nodeFromPort", source.getID().toString());
367 data.put("$descFrom", source.getNode().toString());
368 data.put("$descTo", destination.getNode().toString());
369 data.put("$nodeFromPortName", source.toString());
370 data.put("$nodeToPortName", destination.toString());
373 public EdgeBean(NodeConnector connector, Long hostId) {
376 this.destination = connector;
377 this.hostId = hostId;
379 data.put("$bandwidth", "N/A");
380 data.put("$color", bandwidthColor(new Bandwidth(0)));
381 data.put("$nodeToPort", connector.getNodeConnectorIDString());
382 data.put("$nodeFromPort", connector.getNodeConnectorIDString());
383 data.put("$descTo", "");
384 data.put("$descFrom", "");
385 data.put("$nodeToPortName", "");
386 data.put("$nodeFromPortName", "");
389 public Map<String, Object> out() {
390 Map<String, Object> edge = new HashMap<String, Object>();
392 edge.put("data", data);
393 if (source == null) {
394 edge.put("nodeFrom", String.valueOf(this.hostId));
396 edge.put("nodeFrom", source.getNode().toString());
398 edge.put("nodeTo", destination.getNode().toString());
404 private String bandwidthColor(Bandwidth bandwidth) {
406 long bandwidthValue = bandwidth.getValue();
408 if (bandwidthValue == 0) {
410 } else if (bandwidthValue < Bandwidth.BW1Kbps) {
412 } else if (bandwidthValue < Bandwidth.BW1Mbps) {
414 } else if (bandwidthValue < Bandwidth.BW1Gbps) {
416 } else if (bandwidthValue < Bandwidth.BW1Tbps) {
418 } else if (bandwidthValue < Bandwidth.BW1Pbps) {
426 protected class NodeType {
427 public static final String NODE = "swtch";
428 public static final String HOST = "host";
431 private boolean authorize(UserLevel level, HttpServletRequest request) {
432 IUserManager userManager = (IUserManager) ServiceHelper
433 .getGlobalInstance(IUserManager.class, this);
434 if (userManager == null) {
438 String username = request.getUserPrincipal().getName();
439 UserLevel userLevel = userManager.getUserLevel(username);
440 if (userLevel.toNumber() <= level.toNumber()) {