f9574b993ca709d769a48562f5f03cd5ea5e72aa
[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.getDstNodeConnector(), link.getSrcNodeConnector());
600         return getLinkTuple(rLink);
601     }
602
603
604     private Edge getLinkTuple(TopologyUserLinkConfig link) {
605         Edge linkTuple = null;
606         NodeConnector srcNodeConnector = NodeConnector.fromString(link.getSrcNodeConnector());
607         NodeConnector dstNodeConnector = NodeConnector.fromString(link.getDstNodeConnector());
608         if (srcNodeConnector == null || dstNodeConnector == null) return null;
609         try {
610             linkTuple = new Edge(srcNodeConnector, dstNodeConnector);
611         } catch (Exception e) {
612         }
613         return linkTuple;
614     }
615
616     @Override
617     public ConcurrentMap<String, TopologyUserLinkConfig> getUserLinks() {
618         return userLinks;
619     }
620
621     @Override
622     public Status addUserLink(TopologyUserLinkConfig link) {
623         if (!link.isValid()) {
624             return new Status(StatusCode.BADREQUEST,
625                     "Configuration Invalid. Please check the parameters");
626         }
627         if (userLinks.get(link.getName()) != null) {
628             return new Status(StatusCode.CONFLICT, "Link with name : "
629                     + link.getName()
630                     + " already exists. Please use another name");
631         }
632         if (userLinks.containsValue(link)) {
633             return new Status(StatusCode.CONFLICT, "Link configuration exists");
634         }
635
636         link.setStatus(TopologyUserLinkConfig.STATUS.LINKDOWN);
637         userLinks.put(link.getName(), link);
638
639         Edge linkTuple = getLinkTuple(link);
640         if (linkTuple != null) {
641             if (!isProductionLink(linkTuple)) {
642                 edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
643             }
644
645             linkTuple = getReverseLinkTuple(link);
646             if (linkTuple != null) {
647                 link.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
648                 if (!isProductionLink(linkTuple)) {
649                     edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
650                 }
651             }
652         }
653         return new Status(StatusCode.SUCCESS, null);
654     }
655
656     @Override
657     public Status deleteUserLink(String linkName) {
658         if (linkName == null) {
659             return new Status(StatusCode.BADREQUEST,
660                     "A valid linkName is required to Delete a link");
661         }
662
663         TopologyUserLinkConfig link = userLinks.get(linkName);
664
665         Edge linkTuple = getLinkTuple(link);
666         userLinks.remove(linkName);
667         if (linkTuple != null) {
668             if (!isProductionLink(linkTuple)) {
669                 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
670             }
671
672             linkTuple = getReverseLinkTuple(link);
673             if ((linkTuple != null) && !isProductionLink(linkTuple)) {
674                 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
675             }
676         }
677         return new Status(StatusCode.SUCCESS, null);
678     }
679
680     private void registerWithOSGIConsole() {
681         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
682                 .getBundleContext();
683         bundleContext.registerService(CommandProvider.class.getName(), this,
684                 null);
685     }
686
687     @Override
688     public String getHelp() {
689         StringBuffer help = new StringBuffer();
690         help.append("---Topology Manager---\n");
691         help.append("\t addUserLink <name> <node connector string> <node connector string>\n");
692         help.append("\t deleteUserLink <name>\n");
693         help.append("\t printUserLink\n");
694         help.append("\t printNodeEdges\n");
695         return help.toString();
696     }
697
698     public void _printUserLink(CommandInterpreter ci) {
699         for (String name : this.userLinks.keySet()) {
700             TopologyUserLinkConfig linkConfig = userLinks.get(name);
701             ci.println("Name : " + name);
702             ci.println(linkConfig);
703             ci.println("Edge " + getLinkTuple(linkConfig));
704             ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
705         }
706     }
707
708     public void _addUserLink(CommandInterpreter ci) {
709         String name = ci.nextArgument();
710         if ((name == null)) {
711             ci.println("Please enter a valid Name");
712             return;
713         }
714
715         String ncStr1 = ci.nextArgument();
716         if (ncStr1 == null) {
717             ci.println("Please enter two node connector strings");
718             return;
719         }
720         String ncStr2 = ci.nextArgument();
721         if (ncStr2 == null) {
722             ci.println("Please enter second node connector string");
723             return;
724         }
725
726         NodeConnector nc1 = NodeConnector.fromString(ncStr1);
727         if (nc1 == null) {
728             ci.println("Invalid input node connector 1 string: " + ncStr1);
729             return;
730         }
731         NodeConnector nc2 = NodeConnector.fromString(ncStr2);
732         if (nc2 == null) {
733             ci.println("Invalid input node connector 2 string: " + ncStr2);
734             return;
735         }
736
737         TopologyUserLinkConfig config = new TopologyUserLinkConfig(name, ncStr1, ncStr2);
738         ci.println(this.addUserLink(config));
739     }
740
741     public void _deleteUserLink(CommandInterpreter ci) {
742         String name = ci.nextArgument();
743         if ((name == null)) {
744             ci.println("Please enter a valid Name");
745             return;
746         }
747         this.deleteUserLink(name);
748     }
749
750     public void _printNodeEdges(CommandInterpreter ci) {
751         Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
752         if (nodeEdges == null) {
753             return;
754         }
755         Set<Node> nodeSet = nodeEdges.keySet();
756         if (nodeSet == null) {
757             return;
758         }
759         ci.println("        Node                                         Edge");
760         for (Node node : nodeSet) {
761             Set<Edge> edgeSet = nodeEdges.get(node);
762             if (edgeSet == null) {
763                 continue;
764             }
765             for (Edge edge : edgeSet) {
766                 ci.println(node + "             " + edge);
767             }
768         }
769     }
770
771     @Override
772     public Object readObject(ObjectInputStream ois)
773             throws FileNotFoundException, IOException, ClassNotFoundException {
774         // TODO Auto-generated method stub
775         return ois.readObject();
776     }
777
778     @Override
779     public Status saveConfiguration() {
780         return saveConfig();
781     }
782
783     @Override
784     public void edgeOverUtilized(Edge edge) {
785         log.warn("Link Utilization above normal: {}", edge);
786     }
787
788     @Override
789     public void edgeUtilBackToNormal(Edge edge) {
790         log.warn("Link Utilization back to normal: {}", edge);
791     }
792
793 }