791358c18a476fbb4ddc0c2a96f822ce06c38e43
[controller.git] / opendaylight / web / topology / src / main / java / org / opendaylight / controller / topology / web / Topology.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 package org.opendaylight.controller.topology.web;
11
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;
19 import java.util.Map;
20 import java.util.Set;
21
22 import javax.servlet.http.HttpServletRequest;
23
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;
44
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;
48
49 @Controller
50 @RequestMapping("/")
51 public class Topology {
52
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;
60     
61     /**
62      * Topology of nodes and hosts in the network in JSON format.
63      * 
64      * Mainly intended for consumption by the visual topology.
65      * 
66      * @return - JSON output for visual topology
67      */
68     @RequestMapping(value = "/visual.json", method = RequestMethod.GET)
69     @ResponseBody
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;
77         
78         Map<Node, Set<Edge>> nodeEdges = topologyManager.getNodeEdges();
79         Map<Node, Set<NodeConnector>> hostEdges = topologyManager
80                 .getNodesWithNodeConnectorHost();
81         List<Switch> nodes = switchManager.getNetworkDevices();
82         
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);
88         }
89         
90         // return cache if topology hasn't changed
91         if (
92                 (nodeHash != null && hostHash != null && nodeSingleHash != null && nodeConfigurationHash != null) &&
93                 nodeHash == nodeEdges.hashCode() && hostHash == hostEdges.hashCode() && nodeSingleHash == nodes.hashCode() && nodeConfigurationHash == switchConfigurations.hashCode()
94         ) {
95                 return cache.values();
96         }
97         
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();
103         
104         stage = new HashMap<String, Map<String, Object>>();
105         newNodes = new HashMap<String, Map<String, Object>>();
106
107         // nodeEdges addition
108         addNodes(nodeEdges, topologyManager, switchManager);
109
110         // single nodes addition
111         addSingleNodes(nodes, switchManager);
112         
113         // hostNodes addition
114         addHostNodes(hostEdges, topologyManager);
115         
116         repositionTopology();
117         
118         return cache.values();
119     }
120
121     /**
122      * Add regular nodes to main topology
123      *
124      * @param nodeEdges - node-edges mapping derived from topology
125      * @param topology - the topology instance
126      */
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();
131         
132         for (Map.Entry<Node, Set<Edge>> e : nodeEdges.entrySet()) {
133             Node n = e.getKey();
134             SwitchConfig config = switchManager.getSwitchConfig(n.getNodeIDString());
135             NodeBean node = createNodeBean(config, n);
136             
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;
143                         break;
144                     }
145                 }
146                 EdgeBean edge = new EdgeBean(link, bandwidth);
147                 adjacencies.add(edge.out());
148             }
149             
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);
157                 }
158                 stage.put(node.id(), nodeEntry);
159             } else {
160                 newNodes.put(node.id(), node.out());
161             }
162         }
163     }
164     
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);
169         } else {
170                 bean = new NodeBean(node.toString(), node.toString(), NodeType.NODE);
171         }
172         
173         return bean;
174     }
175     
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);
189                         }
190                 stage.put(node.id(), nodeEntry);
191             } else {
192                 newNodes.put(node.id(), node.out());
193             }
194         }
195     }
196
197     /**
198      * Add regular hosts to main topology
199      *
200      * @param hostEdges - node-nodeconnectors host-specific mapping from topology
201      * @param topology - topology instance
202      */
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();
209
210                 ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
211                 addressByteBuffer.putShort((short) 0);
212                 addressByteBuffer.put(dmac.getValue());
213                 addressByteBuffer.rewind();
214                 
215                 long hid = addressByteBuffer.getLong();
216                 
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);
222                 
223                 if (cache.containsKey(String.valueOf(hid))) {
224                         stage.put(String.valueOf(hid), cache.get(String.valueOf(hid)));
225                 } else {
226                         newNodes.put(String.valueOf(hid), hostBean.out());
227                 }
228             }
229         }
230     }
231
232     /**
233      * Re-position nodes in circular layout
234      */
235     private void repositionTopology() {
236         Graph<String, String> graph = new SparseMultigraph<String, String>();
237         cache.clear();
238         cache.putAll(stage);
239         cache.putAll(newNodes);
240         for (Map<String, Object> on : cache.values()) {
241             graph.addVertex(on.toString());
242
243             List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
244             
245             for (Map<String, Object> adj : adjacencies) {
246                 graph.addEdge(
247                         adj.toString(), adj.get("nodeFrom").toString(),
248                         adj.get("nodeTo").toString()
249                 );
250             }
251         }
252         
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();
258
259             Map<String, String> nodeData = (HashMap<String, String>) v.getValue().get("data");
260             nodeData.put("$x", (x - 600) + "");
261             nodeData.put("$y", (y - 225) + "");
262
263             newNodes.get(v.getKey()).put("data", nodeData);
264         }
265     }
266
267     /**
268      * Update node position
269      * 
270      * This method is mainly used by the visual topology
271      *
272      * @param nodeId - The node to update
273      * @return The node object
274      */
275     @RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
276     @ResponseBody
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
281         }
282         
283         String id = new String(nodeId);
284         
285         if (!cache.containsKey(id))
286             return null;
287
288         Map<String, Object> node = cache.get(id);
289         Map<String, String> data = (Map<String, String>) node.get("data");
290
291         data.put("$x", x);
292         data.put("$y", y);
293
294         node.put("data", data);
295         
296         return node;
297     }
298     
299     /**
300      * Node object for visual topology
301      */
302     protected class NodeBean {
303         protected String id;
304         protected String name;
305         protected Map<String, String> data;
306         protected List<Map<String, Object>> links;
307         
308         public NodeBean() {
309                 data = new HashMap<String, String>();
310                 links = new ArrayList<Map<String, Object>>();
311         }
312         
313         public NodeBean(String id, String name, String type) {
314                 this();
315                 this.id = id;
316                 this.name = name;
317                 data.put("$desc", name);
318                 data.put("$type", type);
319         }
320         
321         public void setLinks(List<Map<String, Object>> links) {
322                 this.links = links;
323         }
324         
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);
331                 
332                 return node;
333         }
334         
335         public String name() {
336                 return this.name;
337         }
338         
339         public String id() {
340                 return this.id;
341         }
342     }
343     
344     /**
345      * Edge object for visual topology
346      */
347     protected class EdgeBean {
348         protected NodeConnector source;
349         protected NodeConnector destination;
350         protected Map<String, String> data;
351         protected Long hostId;
352         
353         public EdgeBean() {
354                 data = new HashMap<String, String>();
355         }
356         
357         public EdgeBean(Edge link, Bandwidth bandwidth) {
358                 this();
359                 this.source = link.getHeadNodeConnector();
360                 this.destination = link.getTailNodeConnector();
361                 
362                 // data
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());
371         }
372         
373         public EdgeBean(NodeConnector connector, Long hostId) {
374                 this();
375                 this.source = null;
376                 this.destination = connector;
377                 this.hostId = hostId;
378                 
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", "");
387         }
388         
389         public Map<String, Object> out() {
390                 Map<String, Object> edge = new HashMap<String, Object>();
391                 
392                 edge.put("data", data);
393                 if (source == null) {
394                         edge.put("nodeFrom", String.valueOf(this.hostId));
395                 } else {
396                         edge.put("nodeFrom", source.getNode().toString());
397                 }
398                 edge.put("nodeTo", destination.getNode().toString());
399                 
400                 
401                 return edge;
402         }
403         
404         private String bandwidthColor(Bandwidth bandwidth) {
405                 String color = null;
406                 long bandwidthValue = bandwidth.getValue();
407                 
408                 if (bandwidthValue == 0) {
409                 color = "#000";
410             } else if (bandwidthValue < Bandwidth.BW1Kbps) {
411                 color = "#148AC6";
412             } else if (bandwidthValue < Bandwidth.BW1Mbps) {
413                 color = "#2858A0";
414             } else if (bandwidthValue < Bandwidth.BW1Gbps) {
415                 color = "#009393";
416             } else if (bandwidthValue < Bandwidth.BW1Tbps) {
417                 color = "#C6C014";
418             } else if (bandwidthValue < Bandwidth.BW1Pbps) {
419                 color = "#F9F464";
420             }
421                 
422                 return color;
423         }
424     }
425     
426     protected class NodeType {
427         public static final String NODE = "swtch";
428         public static final String HOST = "host";
429     }
430     
431     private boolean authorize(UserLevel level, HttpServletRequest request) {
432         IUserManager userManager = (IUserManager) ServiceHelper
433                 .getGlobalInstance(IUserManager.class, this);
434         if (userManager == null) {
435                 return false;
436         }
437         
438         String username = request.getUserPrincipal().getName();
439         UserLevel userLevel = userManager.getUserLevel(username);
440         if (userLevel.toNumber() <= level.toNumber()) {
441                 return true;
442         }
443         return false;
444     }
445 }