- Added osgi cmds to enable/disable LLDP snooping on each individual node connector
[controller.git] / opendaylight / topologymanager / src / main / java / org / opendaylight / controller / topologymanager / internal / TopologyManagerImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.topologymanager.internal;
10
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Dictionary;
17 import java.util.EnumSet;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26
27 import org.apache.commons.lang3.tuple.ImmutablePair;
28 import org.apache.felix.dm.Component;
29 import org.eclipse.osgi.framework.console.CommandInterpreter;
30 import org.eclipse.osgi.framework.console.CommandProvider;
31 import org.opendaylight.controller.clustering.services.CacheConfigException;
32 import org.opendaylight.controller.clustering.services.CacheExistException;
33 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
34 import org.opendaylight.controller.clustering.services.IClusterServices;
35 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
36 import org.opendaylight.controller.sal.core.ConstructionException;
37 import org.opendaylight.controller.sal.core.Edge;
38 import org.opendaylight.controller.sal.core.Host;
39 import org.opendaylight.controller.sal.core.Node;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
42 import org.opendaylight.controller.sal.core.Property;
43 import org.opendaylight.controller.sal.core.TimeStamp;
44 import org.opendaylight.controller.sal.core.UpdateType;
45 import org.opendaylight.controller.sal.core.Node.NodeIDType;
46 import org.opendaylight.controller.sal.topology.IListenTopoUpdates;
47 import org.opendaylight.controller.sal.topology.ITopologyService;
48 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
49 import org.opendaylight.controller.sal.utils.StatusCode;
50 import org.opendaylight.controller.sal.utils.GlobalConstants;
51 import org.opendaylight.controller.sal.utils.IObjectReader;
52 import org.opendaylight.controller.sal.utils.ObjectReader;
53 import org.opendaylight.controller.sal.utils.ObjectWriter;
54 import org.opendaylight.controller.sal.utils.Status;
55 import org.opendaylight.controller.topologymanager.ITopologyManager;
56 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
57 import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
58 import org.osgi.framework.BundleContext;
59 import org.osgi.framework.FrameworkUtil;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 /**
64  * The class describes TopologyManager which is the central repository of the
65  * network topology. It provides service for applications to interact with
66  * topology database and notifies all the listeners of topology changes.
67  */
68 public class TopologyManagerImpl implements ITopologyManager,
69         IConfigurationContainerAware, IListenTopoUpdates, IObjectReader,
70         CommandProvider {
71     private static final Logger log = LoggerFactory
72             .getLogger(TopologyManagerImpl.class);
73     private ITopologyService topoService = null;
74     private IClusterContainerServices clusterContainerService = null;
75     // DB of all the Edges with properties which constitute our topology
76     private ConcurrentMap<Edge, Set<Property>> edgesDB = null;
77     // DB of all NodeConnector which are part of ISL Edges, meaning they
78     // are connected to another NodeConnector on the other side of an ISL link.
79     // NodeConnector of a Production Edge is not part of this DB.
80     private ConcurrentMap<NodeConnector, Set<Property>> nodeConnectorsDB = null;
81     // DB of all the NodeConnectors with an Host attached to it
82     private ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>> hostsDB = null;
83     // Topology Manager Aware listeners
84     private Set<ITopologyManagerAware> topologyManagerAware = Collections
85             .synchronizedSet(new HashSet<ITopologyManagerAware>());
86
87     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
88     private String userLinksFileName = null;
89     private ConcurrentMap<String, TopologyUserLinkConfig> userLinks;
90
91     void nonClusterObjectCreate() {
92         edgesDB = new ConcurrentHashMap<Edge, Set<Property>>();
93         hostsDB = new ConcurrentHashMap<NodeConnector, ImmutablePair<Host, Set<Property>>>();
94         userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
95         nodeConnectorsDB = new ConcurrentHashMap<NodeConnector, Set<Property>>();
96     }
97
98     void setTopologyManagerAware(ITopologyManagerAware s) {
99         if (this.topologyManagerAware != null) {
100             log.debug("Adding ITopologyManagerAware: {}", s);
101             this.topologyManagerAware.add(s);
102         }
103     }
104
105     void unsetTopologyManagerAware(ITopologyManagerAware s) {
106         if (this.topologyManagerAware != null) {
107             log.debug("Removing ITopologyManagerAware: {}", s);
108             this.topologyManagerAware.remove(s);
109         }
110     }
111
112     void setTopoService(ITopologyService s) {
113         log.debug("Adding ITopologyService: {}", s);
114         this.topoService = s;
115     }
116
117     void unsetTopoService(ITopologyService s) {
118         if (this.topoService == s) {
119             log.debug("Removing ITopologyService: {}", s);
120             this.topoService = null;
121         }
122     }
123
124     void setClusterContainerService(IClusterContainerServices s) {
125         log.debug("Cluster Service set");
126         this.clusterContainerService = s;
127     }
128
129     void unsetClusterContainerService(IClusterContainerServices s) {
130         if (this.clusterContainerService == s) {
131             log.debug("Cluster Service removed!");
132             this.clusterContainerService = null;
133         }
134     }
135
136     /**
137      * Function called by the dependency manager when all the required
138      * dependencies are satisfied
139      * 
140      */
141     void init(Component c) {
142         String containerName = null;
143         Dictionary props = c.getServiceProperties();
144         if (props != null) {
145             containerName = (String) props.get("containerName");
146         } else {
147             // In the Global instance case the containerName is empty
148             containerName = "UNKNOWN";
149         }
150
151         if (this.clusterContainerService == null) {
152             log.error("Cluster Services is null, not expected!");
153             return;
154         }
155
156         if (this.topoService == null) {
157             log.error("Topology Services is null, not expected!");
158             return;
159         }
160
161         try {
162             this.edgesDB = (ConcurrentMap<Edge, Set<Property>>) this.clusterContainerService
163                     .createCache("topologymanager.edgesDB", EnumSet
164                             .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
165         } catch (CacheExistException cee) {
166             log.error("topologymanager.edgesDB Cache already exists - "
167                     + "destroy and recreate if needed");
168         } catch (CacheConfigException cce) {
169             log.error("topologymanager.edgesDB Cache configuration invalid - "
170                     + "check cache mode");
171         }
172
173         try {
174             this.hostsDB = (ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>>) this.clusterContainerService
175                     .createCache("topologymanager.hostsDB", EnumSet
176                             .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
177         } catch (CacheExistException cee) {
178             log.error("topologymanager.hostsDB Cache already exists - "
179                     + "destroy and recreate if needed");
180         } catch (CacheConfigException cce) {
181             log.error("topologymanager.hostsDB Cache configuration invalid - "
182                     + "check cache mode");
183         }
184
185         try {
186             this.nodeConnectorsDB = (ConcurrentMap<NodeConnector, Set<Property>>) this.clusterContainerService
187                     .createCache("topologymanager.nodeConnectorDB", EnumSet
188                             .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
189         } catch (CacheExistException cee) {
190             log.error("topologymanager.nodeConnectorDB Cache already exists"
191                     + " - destroy and recreate if needed");
192         } catch (CacheConfigException cce) {
193             log.error("topologymanager.nodeConnectorDB Cache configuration "
194                     + "invalid - check cache mode");
195         }
196
197         userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
198
199         userLinksFileName = ROOT + "userTopology_" + containerName + ".conf";
200         registerWithOSGIConsole();
201         loadConfiguration();
202     }
203
204     /**
205      * Function called after the topology manager has registered the service in
206      * OSGi service registry.
207      * 
208      */
209     void started() {
210         // SollicitRefresh MUST be called here else if called at init
211         // time it may sollicit refresh too soon.
212         log.debug("Sollicit topology refresh");
213         topoService.sollicitRefresh();
214     }
215
216     /**
217      * Function called by the dependency manager when at least one dependency
218      * become unsatisfied or when the component is shutting down because for
219      * example bundle is being stopped.
220      * 
221      */
222     void destroy() {
223         if (this.clusterContainerService == null) {
224             log.error("Cluster Services is null, not expected!");
225             this.edgesDB = null;
226             this.hostsDB = null;
227             this.nodeConnectorsDB = null;
228             return;
229         }
230         this.clusterContainerService.destroyCache("topologymanager.edgesDB");
231         this.edgesDB = null;
232         this.clusterContainerService.destroyCache("topologymanager.hostsDB");
233         this.hostsDB = null;
234         this.clusterContainerService
235                 .destroyCache("topologymanager.nodeConnectorDB");
236         this.nodeConnectorsDB = null;
237         log.debug("Topology Manager DB Deallocated");
238     }
239
240     @SuppressWarnings("unchecked")
241     private void loadConfiguration() {
242         ObjectReader objReader = new ObjectReader();
243         ConcurrentMap<String, TopologyUserLinkConfig> confList = (ConcurrentMap<String, TopologyUserLinkConfig>) objReader
244                 .read(this, userLinksFileName);
245
246         if (confList == null) {
247             return;
248         }
249
250         for (TopologyUserLinkConfig conf : confList.values()) {
251             addUserLink(conf);
252         }
253     }
254
255     @Override
256     public Status saveConfig() {
257         // Publish the save config event to the cluster nodes
258         /**
259          * Get the CLUSTERING SERVICES WORKING BEFORE TRYING THIS
260          * 
261          * configSaveEvent.put(new Date().getTime(), SAVE);
262          */
263         return saveConfigInternal();
264     }
265
266     public Status saveConfigInternal() {
267         Status retS;
268         ObjectWriter objWriter = new ObjectWriter();
269
270         retS = objWriter
271                 .write(new ConcurrentHashMap<String, TopologyUserLinkConfig>(
272                         userLinks), userLinksFileName);
273
274         if (retS.isSuccess()) {
275             return retS;
276         } else {
277             return new Status(StatusCode.INTERNALERROR, "Save failed");
278         }
279     }
280
281     @Override
282     public Map<Node, Set<Edge>> getNodeEdges() {
283         if (this.edgesDB == null) {
284             return null;
285         }
286
287         HashMap<Node, Set<Edge>> res = new HashMap<Node, Set<Edge>>();
288         for (Edge key : this.edgesDB.keySet()) {
289             // Lets analyze the tail
290             Node node = key.getTailNodeConnector().getNode();
291             Set<Edge> nodeEdges = res.get(node);
292             if (nodeEdges == null) {
293                 nodeEdges = new HashSet<Edge>();
294             }
295             nodeEdges.add(key);
296             // We need to re-add to the MAP even if the element was
297             // already there so in case of clustered services the map
298             // gets updated in the cluster
299             res.put(node, nodeEdges);
300
301             // Lets analyze the head
302             node = key.getHeadNodeConnector().getNode();
303             nodeEdges = res.get(node);
304             if (nodeEdges == null) {
305                 nodeEdges = new HashSet<Edge>();
306             }
307             nodeEdges.add(key);
308             // We need to re-add to the MAP even if the element was
309             // already there so in case of clustered services the map
310             // gets updated in the cluster
311             res.put(node, nodeEdges);
312         }
313
314         return res;
315     }
316
317     @Override
318     public boolean isInternal(NodeConnector p) {
319         if (this.nodeConnectorsDB == null) {
320             return false;
321         }
322
323         // This is an internal NodeConnector if is connected to
324         // another Node i.e it's part of the nodeConnectorsDB
325         return (this.nodeConnectorsDB.get(p) != null);
326     }
327
328     /**
329      * This method returns true if the edge is an ISL link.
330      * 
331      * @param e
332      *            The edge
333      * @return true if it is an ISL link
334      */
335     public boolean isISLink(Edge e) {
336         return (!isProductionLink(e));
337     }
338
339     /**
340      * This method returns true if the edge is a production link.
341      * 
342      * @param e
343      *            The edge
344      * @return true if it is a production link
345      */
346     public boolean isProductionLink(Edge e) {
347         return (e.getHeadNodeConnector().getType()
348                 .equals(NodeConnector.NodeConnectorIDType.PRODUCTION) || e
349                 .getTailNodeConnector().getType()
350                 .equals(NodeConnector.NodeConnectorIDType.PRODUCTION));
351     }
352
353     /**
354      * The Map returned is a copy of the current topology hence if the topology
355      * changes the copy doesn't
356      * 
357      * @return A Map representing the current topology expressed as edges of the
358      *         network
359      */
360     @Override
361     public Map<Edge, Set<Property>> getEdges() {
362         if (this.edgesDB == null) {
363             return null;
364         }
365
366         HashMap<Edge, Set<Property>> res = new HashMap<Edge, Set<Property>>();
367         for (Edge key : this.edgesDB.keySet()) {
368             // Sets of props are copied because the composition of
369             // those properties could change with time
370             HashSet<Property> prop = new HashSet<Property>(
371                     this.edgesDB.get(key));
372             // We can simply reuse the key because the object is
373             // immutable so doesn't really matter that we are
374             // referencing the only owned by a different table, the
375             // meaning is the same because doesn't change with time.
376             res.put(key, prop);
377         }
378
379         return res;
380     }
381
382     // TODO remove with spring-dm removal
383     /**
384      * @param set
385      *            the topologyAware to set
386      */
387     public void setTopologyAware(Set<Object> set) {
388         for (Object s : set) {
389             setTopologyManagerAware((ITopologyManagerAware) s);
390         }
391     }
392
393     @Override
394     public Set<NodeConnector> getNodeConnectorWithHost() {
395         if (this.hostsDB == null) {
396             return null;
397         }
398
399         return (this.hostsDB.keySet());
400     }
401
402     @Override
403     public Map<Node, Set<NodeConnector>> getNodesWithNodeConnectorHost() {
404         if (this.hostsDB == null) {
405             return null;
406         }
407         HashMap<Node, Set<NodeConnector>> res = new HashMap<Node, Set<NodeConnector>>();
408
409         for (NodeConnector p : this.hostsDB.keySet()) {
410             Node n = p.getNode();
411             Set<NodeConnector> pSet = res.get(n);
412             if (pSet == null) {
413                 // Create the HashSet if null
414                 pSet = new HashSet<NodeConnector>();
415                 res.put(n, pSet);
416             }
417
418             // Keep updating the HashSet, given this is not a
419             // clustered map we can just update the set without
420             // worrying to update the hashmap.
421             pSet.add(p);
422         }
423
424         return (res);
425     }
426
427     @Override
428     public Host getHostAttachedToNodeConnector(NodeConnector p) {
429         if (this.hostsDB == null) {
430             return null;
431         }
432
433         return (this.hostsDB.get(p).getLeft());
434     }
435
436     @Override
437     public void updateHostLink(NodeConnector p, Host h, UpdateType t,
438             Set<Property> props) {
439         if (this.hostsDB == null) {
440             return;
441         }
442
443         switch (t) {
444         case ADDED:
445         case CHANGED:
446             // Clone the property set in case non null else just
447             // create an empty one. Caches allocated via infinispan
448             // don't allow null values
449             if (props == null) {
450                 props = new HashSet<Property>();
451             } else {
452                 props = new HashSet<Property>(props);
453             }
454
455             this.hostsDB.put(p, new ImmutablePair(h, props));
456             break;
457         case REMOVED:
458             this.hostsDB.remove(p);
459             break;
460         }
461     }
462
463     private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type,
464             Set<Property> props) {
465         switch (type) {
466         case ADDED:
467             // Make sure the props are non-null
468             if (props == null) {
469                 props = (Set<Property>) new HashSet();
470             } else {
471                 // Copy the set so noone is going to change the content
472                 props = (Set<Property>) new HashSet(props);
473             }
474
475             // Now make sure there is the creation timestamp for the
476             // edge, if not there timestamp with the first update
477             boolean found_create = false;
478             for (Property prop : props) {
479                 if (prop instanceof TimeStamp) {
480                     TimeStamp t = (TimeStamp) prop;
481                     if (t.getTimeStampName().equals("creation")) {
482                         found_create = true;
483                     }
484                 }
485             }
486
487             if (!found_create) {
488                 TimeStamp t = new TimeStamp(System.currentTimeMillis(),
489                         "creation");
490                 props.add(t);
491             }
492
493             // Now add this in the database eventually overriding
494             // something that may have been already existing
495             this.edgesDB.put(e, props);
496
497             // Now populate the DB of NodeConnectors
498             // NOTE WELL: properties are empty sets, not really needed
499             // for now.
500             // The DB only contains ISL ports
501             if (isISLink(e)) {
502                 this.nodeConnectorsDB.put(e.getHeadNodeConnector(),
503                         new HashSet<Property>());
504                 this.nodeConnectorsDB.put(e.getTailNodeConnector(),
505                         new HashSet<Property>());
506             }
507             log.trace("Edge {}  {}", e.toString(), type.name());
508             break;
509         case REMOVED:
510             // Now remove the edge from edgesDB
511             this.edgesDB.remove(e);
512
513             // Now lets update the NodeConnectors DB, the assumption
514             // here is that two NodeConnector are exclusively
515             // connected by 1 and only 1 edge, this is reasonable in
516             // the same plug (virtual of phisical) we can assume two
517             // cables won't be plugged. This could break only in case
518             // of devices in the middle that acts as hubs, but it
519             // should be safe to assume that won't happen.
520             this.nodeConnectorsDB.remove(e.getHeadNodeConnector());
521             this.nodeConnectorsDB.remove(e.getTailNodeConnector());
522             log.trace("Edge {}  {}", e.toString(), type.name());
523             break;
524         case CHANGED:
525             Set<Property> old_props = this.edgesDB.get(e);
526
527             // When property changes lets make sure we can change it
528             // all except the creation time stamp because that should
529             // be changed only when the edge is destroyed and created
530             // again
531             TimeStamp tc = null;
532             for (Property prop : old_props) {
533                 if (prop instanceof TimeStamp) {
534                     TimeStamp t = (TimeStamp) prop;
535                     if (t.getTimeStampName().equals("creation")) {
536                         tc = t;
537                     }
538                 }
539             }
540
541             // Now lets make sure new properties are non-null
542             // Make sure the props are non-null
543             if (props == null) {
544                 props = (Set<Property>) new HashSet();
545             } else {
546                 // Copy the set so noone is going to change the content
547                 props = (Set<Property>) new HashSet(props);
548             }
549
550             // Now lets remove the creation property if exist in the
551             // new props
552             for (Iterator<Property> i = props.iterator(); i.hasNext();) {
553                 Property prop = i.next();
554                 if (prop instanceof TimeStamp) {
555                     TimeStamp t = (TimeStamp) prop;
556                     if (t.getTimeStampName().equals("creation")) {
557                         i.remove();
558                     }
559                 }
560             }
561
562             // Now lets add the creation timestamp in it
563             if (tc != null) {
564                 props.add(tc);
565             }
566
567             // Finally update
568             this.edgesDB.put(e, props);
569             log.trace("Edge {}  {}", e.toString(), type.name());
570             break;
571         }
572         return new TopoEdgeUpdate(e, props, type);
573     }
574
575     @Override
576     public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
577         List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
578         for (int i = 0; i < topoedgeupdateList.size(); i++) {
579             Edge e = topoedgeupdateList.get(i).getEdge();
580             Set<Property> p = topoedgeupdateList.get(i).getProperty();
581             UpdateType type = topoedgeupdateList.get(i).getUpdateType();
582             TopoEdgeUpdate teu = edgeUpdate(e, type, p);
583             teuList.add(teu);
584         }
585
586         // Now update the listeners
587         for (ITopologyManagerAware s : this.topologyManagerAware) {
588             try {
589                 s.edgeUpdate(teuList);
590             } catch (Exception exc) {
591                 log.error("Exception on callback", exc);
592             }
593         }
594
595     }
596
597     private Edge getReverseLinkTuple(TopologyUserLinkConfig link) {
598         TopologyUserLinkConfig rLink = new TopologyUserLinkConfig(
599                 link.getName(), link.getDstNodeIDType(), link.getDstSwitchId(),
600                 link.getDstNodeConnectorIDType(), link.getDstPort(),
601                 link.getSrcNodeIDType(), link.getSrcSwitchId(),
602                 link.getSrcNodeConnectorIDType(), link.getSrcPort());
603         return getLinkTuple(rLink);
604     }
605
606     private Edge getLinkTuple(TopologyUserLinkConfig link) {
607         Edge linkTuple = null;
608
609         // if atleast 1 link exists for the srcPort and atleast 1 link exists
610         // for the dstPort
611         // that makes it ineligible for the Manual link addition
612         // This is just an extra protection to avoid mis-programming.
613         boolean srcLinkExists = false;
614         boolean dstLinkExists = false;
615         // TODO check a way to validate the port with inventory services
616         // if (srcSw.getPorts().contains(srcPort) &&
617         // dstSw.getPorts().contains(srcPort) &&
618         if (!srcLinkExists && !dstLinkExists) {
619             Node sNode = null;
620             Node dNode = null;
621             NodeConnector sPort = null;
622             NodeConnector dPort = null;
623             linkTuple = null;
624             String srcNodeIDType = link.getSrcNodeIDType();
625             String srcNodeConnectorIDType = link.getSrcNodeConnectorIDType();
626             String dstNodeIDType = link.getDstNodeIDType();
627             String dstNodeConnectorIDType = link.getDstNodeConnectorIDType();
628             try {
629                 if (srcNodeIDType.equals(NodeIDType.OPENFLOW)) {
630                     sNode = new Node(srcNodeIDType, link.getSrcSwitchIDLong());
631                 } else {
632                     sNode = new Node(srcNodeIDType, link.getSrcSwitchId());
633                 }
634
635                 if (dstNodeIDType.equals(NodeIDType.OPENFLOW)) {
636                     dNode = new Node(dstNodeIDType, link.getDstSwitchIDLong());
637                 } else {
638                     dNode = new Node(dstNodeIDType, link.getDstSwitchId());
639                 }
640
641                 if (srcNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
642                     Short srcPort = Short.valueOf((short) 0);
643                     if (!link.isSrcPortByName()) {
644                         srcPort = Short.parseShort(link.getSrcPort());
645                     }
646                     sPort = new NodeConnector(srcNodeConnectorIDType, srcPort,
647                             sNode);
648                 } else {
649                     sPort = new NodeConnector(srcNodeConnectorIDType,
650                             link.getSrcPort(), sNode);
651                 }
652
653                 if (dstNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
654                     Short dstPort = Short.valueOf((short) 0);
655                     if (!link.isDstPortByName()) {
656                         dstPort = Short.parseShort(link.getDstPort());
657                     }
658                     dPort = new NodeConnector(dstNodeConnectorIDType, dstPort,
659                             dNode);
660                 } else {
661                     dPort = new NodeConnector(dstNodeConnectorIDType,
662                             link.getDstPort(), dNode);
663                 }
664                 linkTuple = new Edge(sPort, dPort);
665             } catch (ConstructionException cex) {
666                 log.warn("Caught exception ", cex);
667             }
668             return linkTuple;
669         }
670
671         if (srcLinkExists && dstLinkExists) {
672             link.setStatus(TopologyUserLinkConfig.STATUS.INCORRECT);
673         }
674         return null;
675     }
676
677     @Override
678     public ConcurrentMap<String, TopologyUserLinkConfig> getUserLinks() {
679         return userLinks;
680     }
681
682     @Override
683     public Status addUserLink(TopologyUserLinkConfig link) {
684         if (!link.isValid()) {
685             return new Status(StatusCode.BADREQUEST,
686                     "Configuration Invalid. Please check the parameters");
687         }
688         if (userLinks.get(link.getName()) != null) {
689             return new Status(StatusCode.CONFLICT, "Link with name : "
690                     + link.getName()
691                     + " already exists. Please use another name");
692         }
693         if (userLinks.containsValue(link)) {
694             return new Status(StatusCode.CONFLICT, "Link configuration exists");
695         }
696
697         link.setStatus(TopologyUserLinkConfig.STATUS.LINKDOWN);
698         userLinks.put(link.getName(), link);
699
700         Edge linkTuple = getLinkTuple(link);
701         if (linkTuple != null) {
702             try {
703                 linkTuple = getReverseLinkTuple(link);
704                 link.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
705             } catch (Exception e) {
706                 return new Status(StatusCode.INTERNALERROR,
707                         "Exception while adding custom link : "
708                                 + e.getMessage());
709             }
710         }
711         return new Status(StatusCode.SUCCESS, null);
712     }
713
714     @Override
715     public Status deleteUserLink(String linkName) {
716         if (linkName == null) {
717             return new Status(StatusCode.BADREQUEST,
718                     "A valid linkName is required to Delete a link");
719         }
720
721         TopologyUserLinkConfig link = userLinks.get(linkName);
722
723         Edge linkTuple = getLinkTuple(link);
724         userLinks.remove(linkName);
725         if (linkTuple != null) {
726             try {
727                 // oneTopology.deleteUserConfiguredLink(linkTuple);
728             } catch (Exception e) {
729                 log.warn(
730                         "Harmless : Exception while Deleting User Configured link {} {}",
731                         link, e.toString());
732             }
733             linkTuple = getReverseLinkTuple(link);
734             try {
735                 // oneTopology.deleteUserConfiguredLink(linkTuple);
736             } catch (Exception e) {
737                 log.warn(
738                         "Harmless : Exception while Deleting User Configured Reverse link {} {}",
739                         link, e.toString());
740             }
741         }
742         return new Status(StatusCode.SUCCESS, null);
743     }
744
745     private void registerWithOSGIConsole() {
746         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
747                 .getBundleContext();
748         bundleContext.registerService(CommandProvider.class.getName(), this,
749                 null);
750     }
751
752     @Override
753     public String getHelp() {
754         StringBuffer help = new StringBuffer();
755         help.append("---Topology Manager---\n");
756         help.append("\t addTopo name <NodeIDType> <src-sw-id> <NodeConnectorIDType> <port-number> <NodeIDType> <dst-sw-id> <NodeConnectorIDType> <port-number>\n");
757         help.append("\t delTopo name\n");
758         help.append("\t printTopo\n");
759         help.append("\t printNodeEdges\n");
760         return help.toString();
761     }
762
763     public void _printTopo(CommandInterpreter ci) {
764         for (String name : this.userLinks.keySet()) {
765             TopologyUserLinkConfig linkConfig = userLinks.get(name);
766             ci.println("Name : " + name);
767             ci.println(linkConfig);
768             ci.println("Edge " + getLinkTuple(linkConfig));
769             ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
770         }
771     }
772
773     public void _addTopo(CommandInterpreter ci) {
774         String name = ci.nextArgument();
775         if ((name == null)) {
776             ci.println("Please enter a valid Name");
777             return;
778         }
779
780         String srcNodeIDType = ci.nextArgument();
781         if (srcNodeIDType == null) {
782             ci.println("Null source node ID Type. Example: OF or PR");
783             return;
784         }
785
786         String dpid = ci.nextArgument();
787         if (dpid == null) {
788             ci.println("Null source node id");
789             return;
790         }
791
792         String srcNodeConnectorIDType = ci.nextArgument();
793         if (srcNodeConnectorIDType == null) {
794             ci.println("Null source node connector ID Type. Example: OF or PR");
795             return;
796         }
797
798         String port = ci.nextArgument();
799         if (port == null) {
800             ci.println("Null source port number");
801             return;
802         }
803
804         String dstNodeIDType = ci.nextArgument();
805         if (dstNodeIDType == null) {
806             ci.println("Null destination node ID Type. Example: OF or PR");
807             return;
808         }
809
810         String ddpid = ci.nextArgument();
811         if (ddpid == null) {
812             ci.println("Null destination node ID");
813             return;
814         }
815
816         String dstNodeConnectorIDType = ci.nextArgument();
817         if (dstNodeConnectorIDType == null) {
818             ci.println("Null destination node connector ID Type. Example: OF or PR");
819             return;
820         }
821
822         String dport = ci.nextArgument();
823         if (dport == null) {
824             ci.println("Null destination port number");
825             return;
826         }
827         TopologyUserLinkConfig config = new TopologyUserLinkConfig(name,
828                 srcNodeIDType, dpid, srcNodeConnectorIDType, port,
829                 dstNodeIDType, ddpid, dstNodeConnectorIDType, dport);
830         ci.println(this.addUserLink(config));
831     }
832
833     public void _delTopo(CommandInterpreter ci) {
834         String name = ci.nextArgument();
835         if ((name == null)) {
836             ci.println("Please enter a valid Name");
837             return;
838         }
839         this.deleteUserLink(name);
840     }
841
842     public void _printNodeEdges(CommandInterpreter ci) {
843         Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
844         if (nodeEdges == null) {
845             return;
846         }
847         Set<Node> nodeSet = nodeEdges.keySet();
848         if (nodeSet == null) {
849             return;
850         }
851         ci.println("        Node                                         Edge");
852         for (Node node : nodeSet) {
853             Set<Edge> edgeSet = nodeEdges.get(node);
854             if (edgeSet == null) {
855                 continue;
856             }
857             for (Edge edge : edgeSet) {
858                 ci.println(node + "             " + edge);
859             }
860         }
861     }
862
863     @Override
864     public Object readObject(ObjectInputStream ois)
865             throws FileNotFoundException, IOException, ClassNotFoundException {
866         // TODO Auto-generated method stub
867         return ois.readObject();
868     }
869
870     @Override
871     public Status saveConfiguration() {
872         return saveConfig();
873     }
874
875     @Override
876     public void edgeOverUtilized(Edge edge) {
877         log.warn("Link Utilization above normal: {}", edge);
878     }
879
880     @Override
881     public void edgeUtilBackToNormal(Edge edge) {
882         log.warn("Link Utilization back to normal: {}", edge);
883     }
884
885 }