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