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