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