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 org.opendaylight.controller.sal.authorization.UserLevel;
23 import org.opendaylight.controller.sal.core.Bandwidth;
24 import org.opendaylight.controller.sal.core.Edge;
25 import org.opendaylight.controller.sal.core.Host;
26 import org.opendaylight.controller.sal.core.Node;
27 import org.opendaylight.controller.sal.core.NodeConnector;
28 import org.opendaylight.controller.sal.core.Property;
29 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
30 import org.opendaylight.controller.sal.utils.ServiceHelper;
31 import org.opendaylight.controller.switchmanager.ISwitchManager;
32 import org.opendaylight.controller.switchmanager.Switch;
33 import org.opendaylight.controller.switchmanager.SwitchConfig;
34 import org.opendaylight.controller.topologymanager.ITopologyManager;
35 import org.opendaylight.controller.usermanager.IUserManager;
36 import org.springframework.security.core.context.SecurityContextHolder;
37 import org.springframework.stereotype.Controller;
38 import org.springframework.web.bind.annotation.PathVariable;
39 import org.springframework.web.bind.annotation.RequestMapping;
40 import org.springframework.web.bind.annotation.RequestMethod;
41 import org.springframework.web.bind.annotation.RequestParam;
42 import org.springframework.web.bind.annotation.ResponseBody;
44 import edu.uci.ics.jung.algorithms.layout.CircleLayout;
45 import edu.uci.ics.jung.graph.Graph;
46 import edu.uci.ics.jung.graph.SparseMultigraph;
50 public class Topology {
52 protected Map<String, Map<String, Object>> cache = new HashMap<String, Map<String, Object>>();
53 protected Map<String, Map<String, Object>> stage;
54 protected Map<String, Map<String, Object>> newNodes;
55 protected Integer nodeHash = null;
56 protected Integer hostHash = null;
57 protected Integer nodeSingleHash = null;
58 protected Integer nodeConfigurationHash = null;
61 * Topology of nodes and hosts in the network in JSON format.
63 * Mainly intended for consumption by the visual topology.
65 * @return - JSON output for visual topology
67 @RequestMapping(value = "/visual.json", method = RequestMethod.GET)
69 public Collection<Map<String, Object>> getLinkData() {
70 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
71 .getInstance(ITopologyManager.class, "default", this);
72 if (topologyManager == null) return null;
73 ISwitchManager switchManager = (ISwitchManager) ServiceHelper
74 .getInstance(ISwitchManager.class, "default", this);
75 if (switchManager == null) return null;
77 Map<Node, Set<Edge>> nodeEdges = topologyManager.getNodeEdges();
78 Map<Node, Set<NodeConnector>> hostEdges = topologyManager
79 .getNodesWithNodeConnectorHost();
80 List<Switch> nodes = switchManager.getNetworkDevices();
82 List<SwitchConfig> switchConfigurations = new ArrayList<SwitchConfig>();
83 for(Switch sw : nodes) {
84 Node n = sw.getNode();
85 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
86 switchConfigurations.add(config);
89 // return cache if topology hasn't changed
91 (nodeHash != null && hostHash != null && nodeSingleHash != null && nodeConfigurationHash != null) &&
92 nodeHash == nodeEdges.hashCode() && hostHash == hostEdges.hashCode() && nodeSingleHash == nodes.hashCode() && nodeConfigurationHash == switchConfigurations.hashCode()
94 return cache.values();
97 // cache has changed, we must assign the new values
98 nodeHash = nodeEdges.hashCode();
99 hostHash = hostEdges.hashCode();
100 nodeSingleHash = nodes.hashCode();
101 nodeConfigurationHash = switchConfigurations.hashCode();
103 stage = new HashMap<String, Map<String, Object>>();
104 newNodes = new HashMap<String, Map<String, Object>>();
106 // nodeEdges addition
107 addNodes(nodeEdges, topologyManager, switchManager);
109 // single nodes addition
110 addSingleNodes(nodes, switchManager);
112 // hostNodes addition
113 addHostNodes(hostEdges, topologyManager);
115 repositionTopology();
117 return cache.values();
121 * Add regular nodes to main topology
123 * @param nodeEdges - node-edges mapping derived from topology
124 * @param topology - the topology instance
126 private void addNodes(Map<Node, Set<Edge>> nodeEdges,
127 ITopologyManager topology, ISwitchManager switchManager) {
128 Bandwidth bandwidth = new Bandwidth(0);
129 Map<Edge, Set<Property>> properties = topology.getEdges();
131 for (Map.Entry<Node, Set<Edge>> e : nodeEdges.entrySet()) {
133 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
134 NodeBean node = createNodeBean(config, n);
136 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
137 Set<Edge> links = e.getValue();
138 for (Edge link : links) {
139 for (Property p : properties.get(link)) {
140 if (p instanceof Bandwidth) {
141 bandwidth = (Bandwidth) p;
145 EdgeBean edge = new EdgeBean(link, bandwidth);
146 adjacencies.add(edge.out());
149 node.setLinks(adjacencies);
150 if (cache.containsKey(node.id())) {
151 Map<String, Object> nodeEntry = cache.get(node.id());
152 if (config != null) {
153 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
154 data.put("$desc", config.getNodeName());
155 nodeEntry.put("data", data);
157 stage.put(node.id(), nodeEntry);
159 newNodes.put(node.id(), node.out());
164 protected NodeBean createNodeBean(SwitchConfig config, Node node) {
165 NodeBean bean = null;
166 if (config != null) {
167 bean = new NodeBean(node.toString(), config.getNodeName(), NodeType.NODE);
169 bean = new NodeBean(node.toString(), node.toString(), NodeType.NODE);
175 private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager) {
176 if (nodes == null) return;
177 for (Switch sw : nodes) {
178 Node n = sw.getNode();
179 SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
180 if (cache.containsKey(n.toString()) || newNodes.containsKey(n.toString())) continue;
181 NodeBean node = createNodeBean(config, n);
182 if (cache.containsKey(node.id())) {
183 Map<String, Object> nodeEntry = cache.get(node.id());
184 if (config != null) {
185 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
186 data.put("$desc", config.getNodeName());
187 nodeEntry.put("data", data);
189 stage.put(node.id(), nodeEntry);
191 newNodes.put(node.id(), node.out());
197 * Add regular hosts to main topology
199 * @param hostEdges - node-nodeconnectors host-specific mapping from topology
200 * @param topology - topology instance
202 private void addHostNodes(Map<Node, Set<NodeConnector>> hostEdges,
203 ITopologyManager topology) {
204 for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
205 for (NodeConnector connector : e.getValue()) {
206 Host host = topology.getHostAttachedToNodeConnector(connector);
207 EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
209 ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
210 addressByteBuffer.putShort((short) 0);
211 addressByteBuffer.put(dmac.getValue());
212 addressByteBuffer.rewind();
214 long hid = addressByteBuffer.getLong();
216 NodeBean hostBean = new NodeBean(String.valueOf(hid), host.getNetworkAddressAsString(), NodeType.HOST);
217 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
218 EdgeBean edge = new EdgeBean(connector, hid);
219 adjacencies.add(edge.out());
220 hostBean.setLinks(adjacencies);
222 if (cache.containsKey(String.valueOf(hid))) {
223 stage.put(String.valueOf(hid), cache.get(String.valueOf(hid)));
225 newNodes.put(String.valueOf(hid), hostBean.out());
232 * Re-position nodes in circular layout
234 private void repositionTopology() {
235 Graph<String, String> graph = new SparseMultigraph<String, String>();
238 cache.putAll(newNodes);
239 for (Map<String, Object> on : cache.values()) {
240 graph.addVertex(on.toString());
242 List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
244 for (Map<String, Object> adj : adjacencies) {
246 adj.toString(), adj.get("nodeFrom").toString(),
247 adj.get("nodeTo").toString()
252 CircleLayout layout = new CircleLayout(graph);
253 layout.setSize(new Dimension(1200, 365));
254 for (Map.Entry<String, Map<String, Object>> v : newNodes.entrySet()) {
255 Double x = layout.transform(v.getKey()).getX();
256 Double y = layout.transform(v.getKey()).getY();
258 Map<String, String> nodeData = (HashMap<String, String>) v.getValue().get("data");
259 nodeData.put("$x", (x - 600) + "");
260 nodeData.put("$y", (y - 225) + "");
262 newNodes.get(v.getKey()).put("data", nodeData);
267 * Update node position
269 * This method is mainly used by the visual topology
271 * @param nodeId - The node to update
272 * @return The node object
274 @RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
276 public Map<String, Object> post(@PathVariable String nodeId, @RequestParam(required = true) String x,
277 @RequestParam(required = true) String y) {
278 if (!authorize(UserLevel.NETWORKADMIN)) {
279 return new HashMap<String, Object>(); // silently disregard new node position
282 String id = new String(nodeId);
284 if (!cache.containsKey(id))
287 Map<String, Object> node = cache.get(id);
288 Map<String, String> data = (Map<String, String>) node.get("data");
293 node.put("data", data);
299 * Node object for visual topology
301 protected class NodeBean {
303 protected String name;
304 protected Map<String, String> data;
305 protected List<Map<String, Object>> links;
308 data = new HashMap<String, String>();
309 links = new ArrayList<Map<String, Object>>();
312 public NodeBean(String id, String name, String type) {
316 data.put("$desc", name);
317 data.put("$type", type);
320 public void setLinks(List<Map<String, Object>> links) {
324 public Map<String, Object> out() {
325 Map<String, Object> node = new HashMap<String, Object>();
326 node.put("id", this.id);
327 node.put("name", this.name);
328 node.put("data", this.data);
329 node.put("adjacencies", this.links);
334 public String name() {
344 * Edge object for visual topology
346 protected class EdgeBean {
347 protected NodeConnector source;
348 protected NodeConnector destination;
349 protected Map<String, String> data;
350 protected Long hostId;
353 data = new HashMap<String, String>();
356 public EdgeBean(Edge link, Bandwidth bandwidth) {
358 this.source = link.getHeadNodeConnector();
359 this.destination = link.getTailNodeConnector();
362 data.put("$bandwidth", bandwidth.toString());
363 data.put("$color", bandwidthColor(bandwidth));
364 data.put("$nodeToPort", destination.getID().toString());
365 data.put("$nodeFromPort", source.getID().toString());
366 data.put("$descFrom", source.getNode().toString());
367 data.put("$descTo", destination.getNode().toString());
368 data.put("$nodeFromPortName", source.toString());
369 data.put("$nodeToPortName", destination.toString());
372 public EdgeBean(NodeConnector connector, Long hostId) {
375 this.destination = connector;
376 this.hostId = hostId;
378 data.put("$bandwidth", "N/A");
379 data.put("$color", bandwidthColor(new Bandwidth(0)));
380 data.put("$nodeToPort", connector.getNodeConnectorIDString());
381 data.put("$nodeFromPort", connector.getNodeConnectorIDString());
382 data.put("$descTo", "");
383 data.put("$descFrom", "");
384 data.put("$nodeToPortName", "");
385 data.put("$nodeFromPortName", "");
388 public Map<String, Object> out() {
389 Map<String, Object> edge = new HashMap<String, Object>();
391 edge.put("data", data);
392 if (source == null) {
393 edge.put("nodeFrom", String.valueOf(this.hostId));
395 edge.put("nodeFrom", source.getNode().toString());
397 edge.put("nodeTo", destination.getNode().toString());
403 private String bandwidthColor(Bandwidth bandwidth) {
405 long bandwidthValue = bandwidth.getValue();
407 if (bandwidthValue == 0) {
409 } else if (bandwidthValue < Bandwidth.BW1Kbps) {
411 } else if (bandwidthValue < Bandwidth.BW1Mbps) {
413 } else if (bandwidthValue < Bandwidth.BW1Gbps) {
415 } else if (bandwidthValue < Bandwidth.BW1Tbps) {
417 } else if (bandwidthValue < Bandwidth.BW1Pbps) {
425 protected class NodeType {
426 public static final String NODE = "swtch";
427 public static final String HOST = "host";
430 private boolean authorize(UserLevel level) {
431 IUserManager userManager = (IUserManager) ServiceHelper
432 .getGlobalInstance(IUserManager.class, this);
433 if (userManager == null) {
437 String username = SecurityContextHolder.getContext().getAuthentication().getName();
438 UserLevel userLevel = userManager.getUserLevel(username);
439 if (userLevel.toNumber() <= level.toNumber()) {