8c39098a5ec56f532ecd29c99045f2e644ca1d61
[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             if (!isProductionLink(linkTuple)) {
703                 edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
704             }
705
706             linkTuple = getReverseLinkTuple(link);
707             if (linkTuple != null) {
708                 link.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
709                 if (!isProductionLink(linkTuple)) {
710                     edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
711                 }
712             }
713         }
714         return new Status(StatusCode.SUCCESS, null);
715     }
716
717     @Override
718     public Status deleteUserLink(String linkName) {
719         if (linkName == null) {
720             return new Status(StatusCode.BADREQUEST,
721                     "A valid linkName is required to Delete a link");
722         }
723
724         TopologyUserLinkConfig link = userLinks.get(linkName);
725
726         Edge linkTuple = getLinkTuple(link);
727         userLinks.remove(linkName);
728         if (linkTuple != null) {
729             if (!isProductionLink(linkTuple)) {
730                 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
731             }
732
733             linkTuple = getReverseLinkTuple(link);
734             if ((linkTuple != null) && !isProductionLink(linkTuple)) {
735                 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
736             }
737         }
738         return new Status(StatusCode.SUCCESS, null);
739     }
740
741     private void registerWithOSGIConsole() {
742         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
743                 .getBundleContext();
744         bundleContext.registerService(CommandProvider.class.getName(), this,
745                 null);
746     }
747
748     @Override
749     public String getHelp() {
750         StringBuffer help = new StringBuffer();
751         help.append("---Topology Manager---\n");
752         help.append("\t addUserLink <name> <node connector string> <node connector string>\n");
753         help.append("\t deleteUserLink <name>\n");
754         help.append("\t printUserLink\n");
755         help.append("\t printNodeEdges\n");
756         return help.toString();
757     }
758
759     public void _printUserLink(CommandInterpreter ci) {
760         for (String name : this.userLinks.keySet()) {
761             TopologyUserLinkConfig linkConfig = userLinks.get(name);
762             ci.println("Name : " + name);
763             ci.println(linkConfig);
764             ci.println("Edge " + getLinkTuple(linkConfig));
765             ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
766         }
767     }
768
769     public void _addUserLink(CommandInterpreter ci) {
770         String name = ci.nextArgument();
771         if ((name == null)) {
772             ci.println("Please enter a valid Name");
773             return;
774         }
775
776         String ncStr1 = ci.nextArgument();
777         if (ncStr1 == null) {
778             ci.println("Please enter two node connector strings");
779             return;
780         }
781         String ncStr2 = ci.nextArgument();
782         if (ncStr2 == null) {
783             ci.println("Please enter second node connector string");
784             return;
785         }
786
787         NodeConnector nc1 = NodeConnector.fromString(ncStr1);
788         if (nc1 == null) {
789             ci.println("Invalid input node connector 1 string: " + ncStr1);
790             return;
791         }
792         NodeConnector nc2 = NodeConnector.fromString(ncStr2);
793         if (nc2 == null) {
794             ci.println("Invalid input node connector 2 string: " + ncStr2);
795             return;
796         }
797
798         String nodeType1 = nc1.getNode().getType().toString();
799         String nid1 = nc1.getNode().getID().toString();
800         String ncType1 = nc1.getType().toString();
801         String ncid1 = nc1.getID().toString();
802
803         String nodeType2 = nc2.getNode().getType().toString();
804         String nid2 = nc2.getNode().getID().toString();
805         String ncType2 = nc2.getType().toString();
806         String ncid2 = nc2.getID().toString();
807
808         TopologyUserLinkConfig config = new TopologyUserLinkConfig(name,
809                 nodeType1, nid1, ncType1, ncid1, nodeType2, nid2, ncType2,
810                 ncid2);
811         ci.println(this.addUserLink(config));
812     }
813
814     public void _deleteUserLink(CommandInterpreter ci) {
815         String name = ci.nextArgument();
816         if ((name == null)) {
817             ci.println("Please enter a valid Name");
818             return;
819         }
820         this.deleteUserLink(name);
821     }
822
823     public void _printNodeEdges(CommandInterpreter ci) {
824         Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
825         if (nodeEdges == null) {
826             return;
827         }
828         Set<Node> nodeSet = nodeEdges.keySet();
829         if (nodeSet == null) {
830             return;
831         }
832         ci.println("        Node                                         Edge");
833         for (Node node : nodeSet) {
834             Set<Edge> edgeSet = nodeEdges.get(node);
835             if (edgeSet == null) {
836                 continue;
837             }
838             for (Edge edge : edgeSet) {
839                 ci.println(node + "             " + edge);
840             }
841         }
842     }
843
844     @Override
845     public Object readObject(ObjectInputStream ois)
846             throws FileNotFoundException, IOException, ClassNotFoundException {
847         // TODO Auto-generated method stub
848         return ois.readObject();
849     }
850
851     @Override
852     public Status saveConfiguration() {
853         return saveConfig();
854     }
855
856     @Override
857     public void edgeOverUtilized(Edge edge) {
858         log.warn("Link Utilization above normal: {}", edge);
859     }
860
861     @Override
862     public void edgeUtilBackToNormal(Edge edge) {
863         log.warn("Link Utilization back to normal: {}", edge);
864     }
865
866 }