fixing warnings
[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>> stagedNodes;
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.toString());
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         stagedNodes = 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             String description = switchManager.getNodeDescription(n);
135             NodeBean node = createNodeBean(description, 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                 // retrieve node from cache
153                 Map<String, Object> nodeEntry = cache.get(node.id());
154
155                         Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
156                         data.put("$desc", description);
157                         nodeEntry.put("data", data);
158                         
159                 // always update adjacencies
160                 nodeEntry.put("adjacencies", adjacencies);
161                 // stage this cached node (with position)
162                 stagedNodes.put(node.id(), nodeEntry);
163             } else {
164                 newNodes.put(node.id(), node.out());
165             }
166         }
167     }
168     
169     protected NodeBean createNodeBean(String description, Node node) {
170         String name = (description == null || 
171                         description.trim().isEmpty() ||
172                         description.equalsIgnoreCase("none"))?
173                                         node.toString() : description;
174                 return  new NodeBean(node.toString(), name, NodeType.NODE);
175     }
176     
177     @SuppressWarnings("unchecked")
178         private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager) {
179         if (nodes == null) return;
180         for (Switch sw : nodes) {
181                 Node n = sw.getNode();
182
183                 String description = switchManager.getNodeDescription(n);
184                 
185                 if ((stagedNodes.containsKey(n.toString()) && cache.containsKey(n.toString())) || newNodes.containsKey(n.toString())) {
186                         continue;
187                 }
188                 NodeBean node = createNodeBean(description, n);
189                 
190                 // FIXME still doesn't display standalone node when last remaining link is removed
191                 if (cache.containsKey(node.id()) && !stagedNodes.containsKey(node.id())) {
192                         Map<String, Object> nodeEntry = cache.get(node.id());
193                                 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
194                         data.put("$desc", description);
195                         nodeEntry.put("data", data);
196                 stagedNodes.put(node.id(), nodeEntry);
197             } else {
198                 newNodes.put(node.id(), node.out());
199             }
200         }
201     }
202
203     /**
204      * Add regular hosts to main topology
205      *
206      * @param hostEdges - node-nodeconnectors host-specific mapping from topology
207      * @param topology - topology instance
208      */
209     private void addHostNodes(Map<Node, Set<NodeConnector>> hostEdges,
210             ITopologyManager topology) {
211         for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
212             for (NodeConnector connector : e.getValue()) {
213                 Host host = topology.getHostAttachedToNodeConnector(connector);
214                 EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
215
216                 ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
217                 addressByteBuffer.putShort((short) 0);
218                 addressByteBuffer.put(dmac.getValue());
219                 addressByteBuffer.rewind();
220                 
221                 long hid = addressByteBuffer.getLong();
222                 String hostId = String.valueOf(hid);
223                 
224                 NodeBean hostBean = new NodeBean(hostId, host.getNetworkAddressAsString(), NodeType.HOST);
225                 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
226                 EdgeBean edge = new EdgeBean(connector, hid);
227                 adjacencies.add(edge.out());
228                 hostBean.setLinks(adjacencies);
229                 
230                 if (cache.containsKey(hostId)) {
231                         Map<String, Object> hostEntry = cache.get(hostId);
232                         hostEntry.put("adjacencies", adjacencies);
233                         stagedNodes.put(hostId, hostEntry);
234                 } else {
235                         newNodes.put(String.valueOf(hid), hostBean.out());
236                 }
237             }
238         }
239     }
240
241     /**
242      * Re-position nodes in circular layout
243      */
244     private void repositionTopology() {
245         Graph<String, String> graph = new SparseMultigraph<String, String>();
246         cache.clear();
247         cache.putAll(stagedNodes);
248         cache.putAll(newNodes);
249         for (Map<String, Object> on : cache.values()) {
250             graph.addVertex(on.toString());
251
252             List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
253             
254             for (Map<String, Object> adj : adjacencies) {
255                 graph.addEdge(
256                         adj.toString(), adj.get("nodeFrom").toString(),
257                         adj.get("nodeTo").toString()
258                 );
259             }
260         }
261         
262         CircleLayout<String,String> layout = new CircleLayout<String,String>(graph);
263         layout.setSize(new Dimension(1200, 365));
264         for (Map.Entry<String, Map<String, Object>> v : newNodes.entrySet()) {
265             Double x = layout.transform(v.getKey()).getX();
266             Double y = layout.transform(v.getKey()).getY();
267
268             Map<String, String> nodeData = (HashMap<String, String>) v.getValue().get("data");
269             nodeData.put("$x", (x - 600) + "");
270             nodeData.put("$y", (y - 225) + "");
271
272             newNodes.get(v.getKey()).put("data", nodeData);
273         }
274     }
275
276     /**
277      * Update node position
278      * 
279      * This method is mainly used by the visual topology
280      *
281      * @param nodeId - The node to update
282      * @return The node object
283      */
284     @RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
285     @ResponseBody
286     public Map<String, Object> post(@PathVariable String nodeId, @RequestParam(required = true) String x,
287                 @RequestParam(required = true) String y, HttpServletRequest request) {
288         if (!authorize(UserLevel.NETWORKADMIN, request)) {
289                 return new HashMap<String, Object>(); // silently disregard new node position
290         }
291         
292         String id = new String(nodeId);
293         
294         if (!cache.containsKey(id))
295             return null;
296
297         Map<String, Object> node = cache.get(id);
298         Map<String, String> data = (Map<String, String>) node.get("data");
299
300         data.put("$x", x);
301         data.put("$y", y);
302
303         node.put("data", data);
304         
305         return node;
306     }
307     
308     /**
309      * Node object for visual topology
310      */
311     protected class NodeBean {
312         protected String id;
313         protected String name;
314         protected Map<String, String> data;
315         protected List<Map<String, Object>> links;
316         
317         public NodeBean() {
318                 data = new HashMap<String, String>();
319                 links = new ArrayList<Map<String, Object>>();
320         }
321         
322         public NodeBean(String id, String name, String type) {
323                 this();
324                 this.id = id;
325                 this.name = name;
326                 data.put("$desc", name);
327                 data.put("$type", type);
328         }
329         
330         public void setLinks(List<Map<String, Object>> links) {
331                 this.links = links;
332         }
333         
334         public Map<String, Object> out() {
335                 Map<String, Object> node = new HashMap<String, Object>();
336                 node.put("id", this.id);
337                 node.put("name", this.name);
338                 node.put("data", this.data);
339                 node.put("adjacencies", this.links);
340                 
341                 return node;
342         }
343         
344         public String name() {
345                 return this.name;
346         }
347         
348         public String id() {
349                 return this.id;
350         }
351     }
352     
353     /**
354      * Edge object for visual topology
355      */
356     protected class EdgeBean {
357         protected NodeConnector source;
358         protected NodeConnector destination;
359         protected Map<String, String> data;
360         protected Long hostId;
361         
362         public EdgeBean() {
363                 data = new HashMap<String, String>();
364         }
365         
366         public EdgeBean(Edge link, Bandwidth bandwidth) {
367                 this();
368                 this.source = link.getHeadNodeConnector();
369                 this.destination = link.getTailNodeConnector();
370                 
371                 // data
372                 data.put("$bandwidth", bandwidth.toString());
373                 data.put("$color", bandwidthColor(bandwidth));
374                 data.put("$nodeToPort", destination.getID().toString());
375                 data.put("$nodeFromPort", source.getID().toString());
376                 data.put("$descFrom", source.getNode().toString());
377                 data.put("$descTo", destination.getNode().toString());
378                 data.put("$nodeFromPortName", source.toString());
379                 data.put("$nodeToPortName", destination.toString());
380         }
381         
382         public EdgeBean(NodeConnector connector, Long hostId) {
383                 this();
384                 this.source = null;
385                 this.destination = connector;
386                 this.hostId = hostId;
387                 
388                 data.put("$bandwidth", "N/A");
389                 data.put("$color", bandwidthColor(new Bandwidth(0)));
390                 data.put("$nodeToPort", connector.getNodeConnectorIDString());
391                 data.put("$nodeFromPort", connector.getNodeConnectorIDString());
392                 data.put("$descTo", "");
393                 data.put("$descFrom", "");
394                 data.put("$nodeToPortName", "");
395                 data.put("$nodeFromPortName", "");
396         }
397         
398         public Map<String, Object> out() {
399                 Map<String, Object> edge = new HashMap<String, Object>();
400                 
401                 edge.put("data", data);
402                 if (source == null) {
403                         edge.put("nodeFrom", String.valueOf(this.hostId));
404                 } else {
405                         edge.put("nodeFrom", source.getNode().toString());
406                 }
407                 edge.put("nodeTo", destination.getNode().toString());
408                 
409                 
410                 return edge;
411         }
412         
413         private String bandwidthColor(Bandwidth bandwidth) {
414                 String color = null;
415                 long bandwidthValue = bandwidth.getValue();
416                 
417                 if (bandwidthValue == 0) {
418                 color = "#000";
419             } else if (bandwidthValue < Bandwidth.BW1Kbps) {
420                 color = "#148AC6";
421             } else if (bandwidthValue < Bandwidth.BW1Mbps) {
422                 color = "#2858A0";
423             } else if (bandwidthValue < Bandwidth.BW1Gbps) {
424                 color = "#009393";
425             } else if (bandwidthValue < Bandwidth.BW1Tbps) {
426                 color = "#C6C014";
427             } else if (bandwidthValue < Bandwidth.BW1Pbps) {
428                 color = "#F9F464";
429             }
430                 
431                 return color;
432         }
433     }
434     
435     protected class NodeType {
436         public static final String NODE = "swtch";
437         public static final String HOST = "host";
438     }
439     
440     private boolean authorize(UserLevel level, HttpServletRequest request) {
441         IUserManager userManager = (IUserManager) ServiceHelper
442                 .getGlobalInstance(IUserManager.class, this);
443         if (userManager == null) {
444                 return false;
445         }
446         
447         String username = request.getUserPrincipal().getName();
448         UserLevel userLevel = userManager.getUserLevel(username);
449         if (userLevel.toNumber() <= level.toNumber()) {
450                 return true;
451         }
452         return false;
453     }
454 }