Merge "Description: - Made changes in devices.web bundle and sal.core.Config - files...
[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.core.Node.NodeIDType;
32 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
33 import org.opendaylight.controller.sal.utils.ServiceHelper;
34 import org.opendaylight.controller.switchmanager.ISwitchManager;
35 import org.opendaylight.controller.switchmanager.Switch;
36 import org.opendaylight.controller.switchmanager.SwitchConfig;
37 import org.opendaylight.controller.topologymanager.ITopologyManager;
38 import org.opendaylight.controller.usermanager.IUserManager;
39 import org.springframework.stereotype.Controller;
40 import org.springframework.web.bind.annotation.PathVariable;
41 import org.springframework.web.bind.annotation.RequestMapping;
42 import org.springframework.web.bind.annotation.RequestMethod;
43 import org.springframework.web.bind.annotation.RequestParam;
44 import org.springframework.web.bind.annotation.ResponseBody;
45
46 import edu.uci.ics.jung.algorithms.layout.CircleLayout;
47 import edu.uci.ics.jung.graph.Graph;
48 import edu.uci.ics.jung.graph.SparseMultigraph;
49
50 @Controller
51 @RequestMapping("/")
52 public class Topology {
53
54     protected Map<String, Map<String, Object>> cache = new HashMap<String, Map<String, Object>>();
55     protected Map<String, Map<String, Object>> stagedNodes;
56     protected Map<String, Map<String, Object>> newNodes;
57     protected Integer nodeHash = null;
58     protected Integer hostHash = null;
59     protected Integer nodeSingleHash = null;
60     protected Integer nodeConfigurationHash = null;
61     
62     /**
63      * Topology of nodes and hosts in the network in JSON format.
64      * 
65      * Mainly intended for consumption by the visual topology.
66      * 
67      * @return - JSON output for visual topology
68      */
69     @RequestMapping(value = "/visual.json", method = RequestMethod.GET)
70     @ResponseBody
71     public Collection<Map<String, Object>> getLinkData() {
72         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
73                 .getInstance(ITopologyManager.class, "default", this);
74         if (topologyManager == null) return null;
75         ISwitchManager switchManager = (ISwitchManager) ServiceHelper
76                 .getInstance(ISwitchManager.class, "default", this);
77         if (switchManager == null) return null;
78         
79         Map<Node, Set<Edge>> nodeEdges = topologyManager.getNodeEdges();
80         Map<Node, Set<NodeConnector>> hostEdges = topologyManager
81                 .getNodesWithNodeConnectorHost();
82         List<Switch> nodes = switchManager.getNetworkDevices();
83         
84         List<SwitchConfig> switchConfigurations = new ArrayList<SwitchConfig>();
85         for(Switch sw : nodes) {
86                 Node n = sw.getNode();
87                 SwitchConfig config = switchManager.getSwitchConfig(n.toString());
88                 switchConfigurations.add(config);
89         }
90         
91         // return cache if topology hasn't changed
92         if (
93                 (nodeHash != null && hostHash != null && nodeSingleHash != null && nodeConfigurationHash != null) &&
94                 nodeHash == nodeEdges.hashCode() && hostHash == hostEdges.hashCode() && nodeSingleHash == nodes.hashCode() && nodeConfigurationHash == switchConfigurations.hashCode()
95         ) {
96                 return cache.values();
97         }
98         
99         // cache has changed, we must assign the new values
100         nodeHash = nodeEdges.hashCode();
101         hostHash = hostEdges.hashCode();
102         nodeSingleHash = nodes.hashCode();
103         nodeConfigurationHash = switchConfigurations.hashCode();
104         
105         stagedNodes = new HashMap<String, Map<String, Object>>();
106         newNodes = new HashMap<String, Map<String, Object>>();
107
108         // nodeEdges addition
109         addNodes(nodeEdges, topologyManager, switchManager);
110
111         // single nodes addition
112         addSingleNodes(nodes, switchManager);
113         
114         // hostNodes addition
115         addHostNodes(hostEdges, topologyManager);
116         
117         repositionTopology();
118         
119         return cache.values();
120     }
121
122     /**
123      * Add regular nodes to main topology
124      *
125      * @param nodeEdges - node-edges mapping derived from topology
126      * @param topology - the topology instance
127      */
128     private void addNodes(Map<Node, Set<Edge>> nodeEdges,
129             ITopologyManager topology, ISwitchManager switchManager) {
130         Bandwidth bandwidth = new Bandwidth(0);
131         Map<Edge, Set<Property>> properties = topology.getEdges();
132         
133         for (Map.Entry<Node, Set<Edge>> e : nodeEdges.entrySet()) {
134             Node n = e.getKey();
135             
136             // skip production node
137             if (nodeIgnore(n)) {
138                 continue;
139             }
140             
141             String description = switchManager.getNodeDescription(n);
142             NodeBean node = createNodeBean(description, n);
143             
144             List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
145             Set<Edge> links = e.getValue();
146             for (Edge link : links) {
147                 if (edgeIgnore(link)) {
148                     continue;
149                 }
150                 for (Property p : properties.get(link)) {
151                     if (p instanceof Bandwidth) {
152                         bandwidth = (Bandwidth) p;
153                         break;
154                     }
155                 }
156                 EdgeBean edge = new EdgeBean(link, bandwidth);
157                 adjacencies.add(edge.out());
158             }
159             
160             node.setLinks(adjacencies);
161             if (cache.containsKey(node.id())) {
162                 // retrieve node from cache
163                 Map<String, Object> nodeEntry = cache.get(node.id());
164
165                         Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
166                         data.put("$desc", description);
167                         nodeEntry.put("data", data);
168                         
169                 // always update adjacencies
170                 nodeEntry.put("adjacencies", adjacencies);
171                 // stage this cached node (with position)
172                 stagedNodes.put(node.id(), nodeEntry);
173             } else {
174                 newNodes.put(node.id(), node.out());
175             }
176         }
177     }
178     
179     /**
180      * Check if this node shouldn't appear in the visual topology
181      * 
182      * @param node
183      * @return
184      */
185     private boolean nodeIgnore(Node node) {
186         String nodeType = node.getType();
187         
188         // add other node types to ignore later
189         if (nodeType.equals(NodeIDType.PRODUCTION)) {
190             return true;
191         }
192         
193         return false;
194     }
195     
196     /**
197      * Check if this edge shouldn't appear in the visual topology
198      * 
199      * @param edge
200      * @return
201      */
202     private boolean edgeIgnore(Edge edge) {
203         NodeConnector headNodeConnector = edge.getHeadNodeConnector();
204         Node headNode = headNodeConnector.getNode();
205         if (nodeIgnore(headNode)) {
206             return true;
207         }
208         
209         NodeConnector tailNodeConnector = edge.getTailNodeConnector();
210         Node tailNode = tailNodeConnector.getNode();
211         if (nodeIgnore(tailNode)) {
212             return true;
213         }
214         
215         return false;
216     }
217     
218     protected NodeBean createNodeBean(String description, Node node) {
219         String name = (description == null || 
220                         description.trim().isEmpty() ||
221                         description.equalsIgnoreCase("none"))?
222                                         node.toString() : description;
223                 return  new NodeBean(node.toString(), name, NodeType.NODE);
224     }
225     
226     @SuppressWarnings("unchecked")
227         private void addSingleNodes(List<Switch> nodes, ISwitchManager switchManager) {
228         if (nodes == null) return;
229         for (Switch sw : nodes) {
230                 Node n = sw.getNode();
231                 
232                 // skip production node
233                 if (nodeIgnore(n)) {
234                     continue;
235                 }
236
237                 String description = switchManager.getNodeDescription(n);
238                 
239                 if ((stagedNodes.containsKey(n.toString()) && cache.containsKey(n.toString())) || newNodes.containsKey(n.toString())) {
240                         continue;
241                 }
242                 NodeBean node = createNodeBean(description, n);
243                 
244                 // FIXME still doesn't display standalone node when last remaining link is removed
245                 if (cache.containsKey(node.id()) && !stagedNodes.containsKey(node.id())) {
246                         Map<String, Object> nodeEntry = cache.get(node.id());
247                                 Map<String, String> data = (Map<String, String>) nodeEntry.get("data");
248                         data.put("$desc", description);
249                         nodeEntry.put("data", data);
250                 stagedNodes.put(node.id(), nodeEntry);
251             } else {
252                 newNodes.put(node.id(), node.out());
253             }
254         }
255     }
256
257     /**
258      * Add regular hosts to main topology
259      *
260      * @param hostEdges - node-nodeconnectors host-specific mapping from topology
261      * @param topology - topology instance
262      */
263     private void addHostNodes(Map<Node, Set<NodeConnector>> hostEdges,
264             ITopologyManager topology) {
265         for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
266             for (NodeConnector connector : e.getValue()) {
267                 Host host = topology.getHostAttachedToNodeConnector(connector);
268                 EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
269
270                 ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
271                 addressByteBuffer.putShort((short) 0);
272                 addressByteBuffer.put(dmac.getValue());
273                 addressByteBuffer.rewind();
274                 
275                 long hid = addressByteBuffer.getLong();
276                 String hostId = String.valueOf(hid);
277                 
278                 NodeBean hostBean = new NodeBean(hostId, host.getNetworkAddressAsString(), NodeType.HOST);
279                 List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
280                 EdgeBean edge = new EdgeBean(connector, hid);
281                 adjacencies.add(edge.out());
282                 hostBean.setLinks(adjacencies);
283                 
284                 if (cache.containsKey(hostId)) {
285                         Map<String, Object> hostEntry = cache.get(hostId);
286                         hostEntry.put("adjacencies", adjacencies);
287                         stagedNodes.put(hostId, hostEntry);
288                 } else {
289                         newNodes.put(String.valueOf(hid), hostBean.out());
290                 }
291             }
292         }
293     }
294
295     /**
296      * Re-position nodes in circular layout
297      */
298     private void repositionTopology() {
299         Graph<String, String> graph = new SparseMultigraph<String, String>();
300         cache.clear();
301         cache.putAll(stagedNodes);
302         cache.putAll(newNodes);
303         for (Map<String, Object> on : cache.values()) {
304             graph.addVertex(on.toString());
305
306             List<Map<String, Object>> adjacencies = (List<Map<String, Object>>) on.get("adjacencies");
307             
308             for (Map<String, Object> adj : adjacencies) {
309                 graph.addEdge(
310                         adj.toString(), adj.get("nodeFrom").toString(),
311                         adj.get("nodeTo").toString()
312                 );
313             }
314         }
315         
316         CircleLayout<String,String> layout = new CircleLayout<String,String>(graph);
317         layout.setSize(new Dimension(1200, 365));
318         for (Map.Entry<String, Map<String, Object>> v : newNodes.entrySet()) {
319             Double x = layout.transform(v.getKey()).getX();
320             Double y = layout.transform(v.getKey()).getY();
321
322             Map<String, String> nodeData = (HashMap<String, String>) v.getValue().get("data");
323             nodeData.put("$x", (x - 600) + "");
324             nodeData.put("$y", (y - 225) + "");
325
326             newNodes.get(v.getKey()).put("data", nodeData);
327         }
328     }
329
330     /**
331      * Update node position
332      * 
333      * This method is mainly used by the visual topology
334      *
335      * @param nodeId - The node to update
336      * @return The node object
337      */
338     @RequestMapping(value = "/node/{nodeId}", method = RequestMethod.POST)
339     @ResponseBody
340     public Map<String, Object> post(@PathVariable String nodeId, @RequestParam(required = true) String x,
341                 @RequestParam(required = true) String y, HttpServletRequest request) {
342         if (!authorize(UserLevel.NETWORKADMIN, request)) {
343                 return new HashMap<String, Object>(); // silently disregard new node position
344         }
345         
346         String id = new String(nodeId);
347         
348         if (!cache.containsKey(id))
349             return null;
350
351         Map<String, Object> node = cache.get(id);
352         Map<String, String> data = (Map<String, String>) node.get("data");
353
354         data.put("$x", x);
355         data.put("$y", y);
356
357         node.put("data", data);
358         
359         return node;
360     }
361     
362     /**
363      * Node object for visual topology
364      */
365     protected class NodeBean {
366         protected String id;
367         protected String name;
368         protected Map<String, String> data;
369         protected List<Map<String, Object>> links;
370         
371         public NodeBean() {
372                 data = new HashMap<String, String>();
373                 links = new ArrayList<Map<String, Object>>();
374         }
375         
376         public NodeBean(String id, String name, String type) {
377                 this();
378                 this.id = id;
379                 this.name = name;
380                 data.put("$desc", name);
381                 data.put("$type", type);
382         }
383         
384         public void setLinks(List<Map<String, Object>> links) {
385                 this.links = links;
386         }
387         
388         public Map<String, Object> out() {
389                 Map<String, Object> node = new HashMap<String, Object>();
390                 node.put("id", this.id);
391                 node.put("name", this.name);
392                 node.put("data", this.data);
393                 node.put("adjacencies", this.links);
394                 
395                 return node;
396         }
397         
398         public String name() {
399                 return this.name;
400         }
401         
402         public String id() {
403                 return this.id;
404         }
405     }
406     
407     /**
408      * Edge object for visual topology
409      */
410     protected class EdgeBean {
411         protected NodeConnector source;
412         protected NodeConnector destination;
413         protected Map<String, String> data;
414         protected Long hostId;
415         
416         public EdgeBean() {
417                 data = new HashMap<String, String>();
418         }
419         
420         public EdgeBean(Edge link, Bandwidth bandwidth) {
421                 this();
422                 this.source = link.getHeadNodeConnector();
423                 this.destination = link.getTailNodeConnector();
424                 
425                 // data
426                 data.put("$bandwidth", bandwidth.toString());
427                 data.put("$color", bandwidthColor(bandwidth));
428                 data.put("$nodeToPort", destination.getID().toString());
429                 data.put("$nodeFromPort", source.getID().toString());
430                 data.put("$descFrom", source.getNode().toString());
431                 data.put("$descTo", destination.getNode().toString());
432                 data.put("$nodeFromPortName", source.toString());
433                 data.put("$nodeToPortName", destination.toString());
434         }
435         
436         public EdgeBean(NodeConnector connector, Long hostId) {
437                 this();
438                 this.source = null;
439                 this.destination = connector;
440                 this.hostId = hostId;
441                 
442                 data.put("$bandwidth", "N/A");
443                 data.put("$color", bandwidthColor(new Bandwidth(0)));
444                 data.put("$nodeToPort", connector.getNodeConnectorIDString());
445                 data.put("$nodeFromPort", connector.getNodeConnectorIDString());
446                 data.put("$descTo", "");
447                 data.put("$descFrom", "");
448                 data.put("$nodeToPortName", "");
449                 data.put("$nodeFromPortName", "");
450         }
451         
452         public Map<String, Object> out() {
453                 Map<String, Object> edge = new HashMap<String, Object>();
454                 
455                 edge.put("data", data);
456                 if (source == null) {
457                         edge.put("nodeFrom", String.valueOf(this.hostId));
458                 } else {
459                         edge.put("nodeFrom", source.getNode().toString());
460                 }
461                 edge.put("nodeTo", destination.getNode().toString());
462                 
463                 
464                 return edge;
465         }
466         
467         private String bandwidthColor(Bandwidth bandwidth) {
468                 String color = null;
469                 long bandwidthValue = bandwidth.getValue();
470                 
471                 if (bandwidthValue == 0) {
472                 color = "#000";
473             } else if (bandwidthValue < Bandwidth.BW1Kbps) {
474                 color = "#148AC6";
475             } else if (bandwidthValue < Bandwidth.BW1Mbps) {
476                 color = "#2858A0";
477             } else if (bandwidthValue < Bandwidth.BW1Gbps) {
478                 color = "#009393";
479             } else if (bandwidthValue < Bandwidth.BW1Tbps) {
480                 color = "#C6C014";
481             } else if (bandwidthValue < Bandwidth.BW1Pbps) {
482                 color = "#F9F464";
483             }
484                 
485                 return color;
486         }
487     }
488     
489     protected class NodeType {
490         public static final String NODE = "swtch";
491         public static final String HOST = "host";
492     }
493     
494     private boolean authorize(UserLevel level, HttpServletRequest request) {
495         IUserManager userManager = (IUserManager) ServiceHelper
496                 .getGlobalInstance(IUserManager.class, this);
497         if (userManager == null) {
498                 return false;
499         }
500         
501         String username = request.getUserPrincipal().getName();
502         UserLevel userLevel = userManager.getUserLevel(username);
503         if (userLevel.toNumber() <= level.toNumber()) {
504                 return true;
505         }
506         return false;
507     }
508 }