Merge "Principal to contain all the user roles"
[controller.git] / opendaylight / topologymanager / src / main / java / org / opendaylight / controller / topologymanager / internal / TopologyManagerImpl.java
index 92d0c391d3694deade3dd564a12eee62a22ef9aa..8c39098a5ec56f532ecd29c99045f2e644ca1d61 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -12,6 +11,7 @@ package org.opendaylight.controller.topologymanager.internal;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.EnumSet;
@@ -20,6 +20,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -37,14 +38,16 @@ import org.opendaylight.controller.sal.core.Edge;
 import org.opendaylight.controller.sal.core.Host;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
 import org.opendaylight.controller.sal.core.Property;
 import org.opendaylight.controller.sal.core.TimeStamp;
 import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
 import org.opendaylight.controller.sal.topology.IListenTopoUpdates;
 import org.opendaylight.controller.sal.topology.ITopologyService;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
 import org.opendaylight.controller.sal.utils.StatusCode;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
-import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.IObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectWriter;
@@ -71,8 +74,9 @@ public class TopologyManagerImpl implements ITopologyManager,
     private IClusterContainerServices clusterContainerService = null;
     // DB of all the Edges with properties which constitute our topology
     private ConcurrentMap<Edge, Set<Property>> edgesDB = null;
-    // DB of all NodeConnector which are part of Edges, meaning they
-    // are connected to another NodeConnector on the other side
+    // DB of all NodeConnector which are part of ISL Edges, meaning they
+    // are connected to another NodeConnector on the other side of an ISL link.
+    // NodeConnector of a Production Edge is not part of this DB.
     private ConcurrentMap<NodeConnector, Set<Property>> nodeConnectorsDB = null;
     // DB of all the NodeConnectors with an Host attached to it
     private ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>> hostsDB = null;
@@ -83,33 +87,36 @@ public class TopologyManagerImpl implements ITopologyManager,
     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
     private String userLinksFileName = null;
     private ConcurrentMap<String, TopologyUserLinkConfig> userLinks;
-    
+
     void nonClusterObjectCreate() {
-       edgesDB = new ConcurrentHashMap<Edge, Set<Property>>();
-       hostsDB = new ConcurrentHashMap<NodeConnector, ImmutablePair<Host, Set<Property>>>();
-       userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
-       nodeConnectorsDB = new ConcurrentHashMap<NodeConnector, Set<Property>>();
+        edgesDB = new ConcurrentHashMap<Edge, Set<Property>>();
+        hostsDB = new ConcurrentHashMap<NodeConnector, ImmutablePair<Host, Set<Property>>>();
+        userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
+        nodeConnectorsDB = new ConcurrentHashMap<NodeConnector, Set<Property>>();
     }
-    
 
     void setTopologyManagerAware(ITopologyManagerAware s) {
         if (this.topologyManagerAware != null) {
+            log.debug("Adding ITopologyManagerAware: {}", s);
             this.topologyManagerAware.add(s);
         }
     }
 
     void unsetTopologyManagerAware(ITopologyManagerAware s) {
         if (this.topologyManagerAware != null) {
+            log.debug("Removing ITopologyManagerAware: {}", s);
             this.topologyManagerAware.remove(s);
         }
     }
 
     void setTopoService(ITopologyService s) {
+        log.debug("Adding ITopologyService: {}", s);
         this.topoService = s;
     }
 
     void unsetTopoService(ITopologyService s) {
         if (this.topoService == s) {
+            log.debug("Removing ITopologyService: {}", s);
             this.topoService = null;
         }
     }
@@ -195,8 +202,8 @@ public class TopologyManagerImpl implements ITopologyManager,
     }
 
     /**
-     * Function called after the topology manager has registered the
-     * service in OSGi service registry.
+     * Function called after the topology manager has registered the service in
+     * OSGi service registry.
      *
      */
     void started() {
@@ -207,9 +214,9 @@ public class TopologyManagerImpl implements ITopologyManager,
     }
 
     /**
-     * Function called by the dependency manager when at least one
-     * dependency become unsatisfied or when the component is shutting
-     * down because for example bundle is being stopped.
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
      *
      */
     void destroy() {
@@ -227,7 +234,7 @@ public class TopologyManagerImpl implements ITopologyManager,
         this.clusterContainerService
                 .destroyCache("topologymanager.nodeConnectorDB");
         this.nodeConnectorsDB = null;
-        log.debug("Topology Manager DB DE-allocated");
+        log.debug("Topology Manager DB Deallocated");
     }
 
     @SuppressWarnings("unchecked")
@@ -250,18 +257,18 @@ public class TopologyManagerImpl implements ITopologyManager,
         // Publish the save config event to the cluster nodes
         /**
          * Get the CLUSTERING SERVICES WORKING BEFORE TRYING THIS
-
-        configSaveEvent.put(new Date().getTime(), SAVE);
+         *
+         * configSaveEvent.put(new Date().getTime(), SAVE);
          */
         return saveConfigInternal();
     }
 
     public Status saveConfigInternal() {
-       Status retS;
+        Status retS;
         ObjectWriter objWriter = new ObjectWriter();
 
-        retS = objWriter.write(
-                new ConcurrentHashMap<String, TopologyUserLinkConfig>(
+        retS = objWriter
+                .write(new ConcurrentHashMap<String, TopologyUserLinkConfig>(
                         userLinks), userLinksFileName);
 
         if (retS.isSuccess()) {
@@ -319,11 +326,36 @@ public class TopologyManagerImpl implements ITopologyManager,
     }
 
     /**
-     * The Map returned is a copy of the current topology hence if the
-     * topology changes the copy doesn't
+     * This method returns true if the edge is an ISL link.
+     *
+     * @param e
+     *            The edge
+     * @return true if it is an ISL link
+     */
+    public boolean isISLink(Edge e) {
+        return (!isProductionLink(e));
+    }
+
+    /**
+     * This method returns true if the edge is a production link.
      *
-     * @return A Map representing the current topology expressed as
-     * edges of the network
+     * @param e
+     *            The edge
+     * @return true if it is a production link
+     */
+    public boolean isProductionLink(Edge e) {
+        return (e.getHeadNodeConnector().getType()
+                .equals(NodeConnector.NodeConnectorIDType.PRODUCTION) || e
+                .getTailNodeConnector().getType()
+                .equals(NodeConnector.NodeConnectorIDType.PRODUCTION));
+    }
+
+    /**
+     * The Map returned is a copy of the current topology hence if the topology
+     * changes the copy doesn't
+     *
+     * @return A Map representing the current topology expressed as edges of the
+     *         network
      */
     @Override
     public Map<Edge, Set<Property>> getEdges() {
@@ -335,8 +367,8 @@ public class TopologyManagerImpl implements ITopologyManager,
         for (Edge key : this.edgesDB.keySet()) {
             // Sets of props are copied because the composition of
             // those properties could change with time
-            HashSet<Property> prop = new HashSet<Property>(this.edgesDB
-                    .get(key));
+            HashSet<Property> prop = new HashSet<Property>(
+                    this.edgesDB.get(key));
             // We can simply reuse the key because the object is
             // immutable so doesn't really matter that we are
             // referencing the only owned by a different table, the
@@ -349,7 +381,8 @@ public class TopologyManagerImpl implements ITopologyManager,
 
     // TODO remove with spring-dm removal
     /**
-     * @param set the topologyAware to set
+     * @param set
+     *            the topologyAware to set
      */
     public void setTopologyAware(Set<Object> set) {
         for (Object s : set) {
@@ -427,8 +460,8 @@ public class TopologyManagerImpl implements ITopologyManager,
         }
     }
 
-    @Override
-    public void edgeUpdate(Edge e, UpdateType type, Set<Property> props) {
+    private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type,
+            Set<Property> props) {
         switch (type) {
         case ADDED:
             // Make sure the props are non-null
@@ -439,7 +472,7 @@ public class TopologyManagerImpl implements ITopologyManager,
                 props = (Set<Property>) new HashSet(props);
             }
 
-            // Now make sure thre is the creation timestamp for the
+            // Now make sure there is the creation timestamp for the
             // edge, if not there timestamp with the first update
             boolean found_create = false;
             for (Property prop : props) {
@@ -462,12 +495,16 @@ public class TopologyManagerImpl implements ITopologyManager,
             this.edgesDB.put(e, props);
 
             // Now populate the DB of NodeConnectors
-            // NOTE WELL: properties are empy sets, not really needed
+            // NOTE WELL: properties are empty sets, not really needed
             // for now.
-            this.nodeConnectorsDB.put(e.getHeadNodeConnector(),
-                    new HashSet<Property>());
-            this.nodeConnectorsDB.put(e.getTailNodeConnector(),
-                    new HashSet<Property>());
+            // The DB only contains ISL ports
+            if (isISLink(e)) {
+                this.nodeConnectorsDB.put(e.getHeadNodeConnector(),
+                        new HashSet<Property>());
+                this.nodeConnectorsDB.put(e.getTailNodeConnector(),
+                        new HashSet<Property>());
+            }
+            log.trace("Edge {}  {}", e.toString(), type.name());
             break;
         case REMOVED:
             // Now remove the edge from edgesDB
@@ -482,6 +519,7 @@ public class TopologyManagerImpl implements ITopologyManager,
             // should be safe to assume that won't happen.
             this.nodeConnectorsDB.remove(e.getHeadNodeConnector());
             this.nodeConnectorsDB.remove(e.getTailNodeConnector());
+            log.trace("Edge {}  {}", e.toString(), type.name());
             break;
         case CHANGED:
             Set<Property> old_props = this.edgesDB.get(e);
@@ -500,7 +538,7 @@ public class TopologyManagerImpl implements ITopologyManager,
                 }
             }
 
-            // Now lest make sure new properties are non-null
+            // Now lets make sure new properties are non-null
             // Make sure the props are non-null
             if (props == null) {
                 props = (Set<Property>) new HashSet();
@@ -528,105 +566,104 @@ public class TopologyManagerImpl implements ITopologyManager,
 
             // Finally update
             this.edgesDB.put(e, props);
+            log.trace("Edge {}  {}", e.toString(), type.name());
             break;
         }
+        return new TopoEdgeUpdate(e, props, type);
+    }
+
+    @Override
+    public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
+        List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
+        for (int i = 0; i < topoedgeupdateList.size(); i++) {
+            Edge e = topoedgeupdateList.get(i).getEdge();
+            Set<Property> p = topoedgeupdateList.get(i).getProperty();
+            UpdateType type = topoedgeupdateList.get(i).getUpdateType();
+            TopoEdgeUpdate teu = edgeUpdate(e, type, p);
+            teuList.add(teu);
+        }
 
         // Now update the listeners
         for (ITopologyManagerAware s : this.topologyManagerAware) {
             try {
-                s.edgeUpdate(e, type, props);
+                s.edgeUpdate(teuList);
             } catch (Exception exc) {
                 log.error("Exception on callback", exc);
             }
         }
+
     }
 
     private Edge getReverseLinkTuple(TopologyUserLinkConfig link) {
-        TopologyUserLinkConfig rLink = new TopologyUserLinkConfig(link
-                .getName(), link.getDstSwitchId(), link.getDstPort(), link
-                .getSrcSwitchId(), link.getSrcPort());
+        TopologyUserLinkConfig rLink = new TopologyUserLinkConfig(
+                link.getName(), link.getDstNodeIDType(), link.getDstSwitchId(),
+                link.getDstNodeConnectorIDType(), link.getDstPort(),
+                link.getSrcNodeIDType(), link.getSrcSwitchId(),
+                link.getSrcNodeConnectorIDType(), link.getSrcPort());
         return getLinkTuple(rLink);
     }
 
     private Edge getLinkTuple(TopologyUserLinkConfig link) {
         Edge linkTuple = null;
-        Long sID = link.getSrcSwitchIDLong();
-        Long dID = link.getDstSwitchIDLong();
-        Short srcPort = Short.valueOf((short) 0);
-        Short dstPort = Short.valueOf((short) 0);
-        if (link.isSrcPortByName()) {
-            // TODO find the inventory service to do this, for now 0
-            //srcPort = srcSw.getPortNumber(link.getSrcPort());
-        } else {
-            srcPort = Short.parseShort(link.getSrcPort());
-        }
-
-        if (link.isDstPortByName()) {
-            //dstPort = dstSw.getPortNumber(link.getDstPort());;
-        } else {
-            dstPort = Short.parseShort(link.getDstPort());
-        }
 
-        // if atleast 1 link exists for the srcPort and atleast 1 link exists for the dstPort
+        // if atleast 1 link exists for the srcPort and atleast 1 link exists
+        // for the dstPort
         // that makes it ineligible for the Manual link addition
         // This is just an extra protection to avoid mis-programming.
         boolean srcLinkExists = false;
         boolean dstLinkExists = false;
-        /**
-         * Disabling this optimization for now to understand the real benefit of doing this.
-         * Since this is a Manual Link addition, the user knows what he is doing and it is
-         * not good to restrict such creativity...
-         */
-        /*
-          Set <Edge> links = oneTopology.getLinks().keySet();
-          if (links != null) {
-          for (Edge eLink : links) {
-          if (!eLink.isUserCreated() &&
-          eLink.getSrc().getSid().equals(link.getSrcSwitchIDLong()) &&
-          eLink.getSrc().getPort().equals(srcPort)) {
-          srcLinkExists = true;
-          }
-
-          if (!eLink.isUserCreated() &&
-          eLink.getSrc().getSid().equals(link.getSrcSwitchIDLong()) &&
-          eLink.getSrc().getPort().equals(srcPort)) {
-          dstLinkExists = true;
-          }
-
-          if (!eLink.isUserCreated() &&
-          eLink.getDst().getSid().equals(link.getSrcSwitchIDLong()) &&
-          eLink.getDst().getPort().equals(srcPort)) {
-          srcLinkExists = true;
-          }
-
-          if (!eLink.isUserCreated() &&
-          eLink.getDst().getSid().equals(link.getSrcSwitchIDLong()) &&
-          eLink.getDst().getPort().equals(srcPort)) {
-          dstLinkExists = true;
-          }
-          }
-          }
-         */
-        //TODO check a way to validate the port with inventory services
-        //if (srcSw.getPorts().contains(srcPort) &&
-        //dstSw.getPorts().contains(srcPort) &&
+        // TODO check a way to validate the port with inventory services
+        // if (srcSw.getPorts().contains(srcPort) &&
+        // dstSw.getPorts().contains(srcPort) &&
         if (!srcLinkExists && !dstLinkExists) {
             Node sNode = null;
             Node dNode = null;
             NodeConnector sPort = null;
             NodeConnector dPort = null;
             linkTuple = null;
+            String srcNodeIDType = link.getSrcNodeIDType();
+            String srcNodeConnectorIDType = link.getSrcNodeConnectorIDType();
+            String dstNodeIDType = link.getDstNodeIDType();
+            String dstNodeConnectorIDType = link.getDstNodeConnectorIDType();
             try {
-                sNode = new Node(Node.NodeIDType.OPENFLOW, sID);
-                dNode = new Node(Node.NodeIDType.OPENFLOW, dID);
-                sPort = new NodeConnector(
-                        NodeConnector.NodeConnectorIDType.OPENFLOW, srcPort,
-                        sNode);
-                dPort = new NodeConnector(
-                        NodeConnector.NodeConnectorIDType.OPENFLOW, dstPort,
-                        dNode);
+                if (srcNodeIDType.equals(NodeIDType.OPENFLOW)) {
+                    sNode = new Node(srcNodeIDType, link.getSrcSwitchIDLong());
+                } else {
+                    sNode = new Node(srcNodeIDType, link.getSrcSwitchId());
+                }
+
+                if (dstNodeIDType.equals(NodeIDType.OPENFLOW)) {
+                    dNode = new Node(dstNodeIDType, link.getDstSwitchIDLong());
+                } else {
+                    dNode = new Node(dstNodeIDType, link.getDstSwitchId());
+                }
+
+                if (srcNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
+                    Short srcPort = Short.valueOf((short) 0);
+                    if (!link.isSrcPortByName()) {
+                        srcPort = Short.parseShort(link.getSrcPort());
+                    }
+                    sPort = new NodeConnector(srcNodeConnectorIDType, srcPort,
+                            sNode);
+                } else {
+                    sPort = new NodeConnector(srcNodeConnectorIDType,
+                            link.getSrcPort(), sNode);
+                }
+
+                if (dstNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
+                    Short dstPort = Short.valueOf((short) 0);
+                    if (!link.isDstPortByName()) {
+                        dstPort = Short.parseShort(link.getDstPort());
+                    }
+                    dPort = new NodeConnector(dstNodeConnectorIDType, dstPort,
+                            dNode);
+                } else {
+                    dPort = new NodeConnector(dstNodeConnectorIDType,
+                            link.getDstPort(), dNode);
+                }
                 linkTuple = new Edge(sPort, dPort);
             } catch (ConstructionException cex) {
+                log.warn("Caught exception ", cex);
             }
             return linkTuple;
         }
@@ -645,12 +682,12 @@ public class TopologyManagerImpl implements ITopologyManager,
     @Override
     public Status addUserLink(TopologyUserLinkConfig link) {
         if (!link.isValid()) {
-            return new Status(StatusCode.BADREQUEST, 
-                       "Configuration Invalid. Please check the parameters");
+            return new Status(StatusCode.BADREQUEST,
+                    "Configuration Invalid. Please check the parameters");
         }
         if (userLinks.get(link.getName()) != null) {
-            return new Status(StatusCode.CONFLICT, 
-                       "Link with name : " + link.getName()
+            return new Status(StatusCode.CONFLICT, "Link with name : "
+                    + link.getName()
                     + " already exists. Please use another name");
         }
         if (userLinks.containsValue(link)) {
@@ -662,19 +699,16 @@ public class TopologyManagerImpl implements ITopologyManager,
 
         Edge linkTuple = getLinkTuple(link);
         if (linkTuple != null) {
-            try {
-                // TODO The onetopology will be gone too, topology
-                //manager is the master of the topology at this point
-                //if (oneTopology.addUserConfiguredLink(linkTuple)) {
-                linkTuple = getReverseLinkTuple(link);
-                //if (oneTopology.addUserConfiguredLink(linkTuple)) {
+            if (!isProductionLink(linkTuple)) {
+                edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
+            }
+
+            linkTuple = getReverseLinkTuple(link);
+            if (linkTuple != null) {
                 link.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
-                //}
-                //}
-            } catch (Exception e) {
-                return new Status(StatusCode.INTERNALERROR,
-                               "Exception while adding custom link : " + 
-                                               e.getMessage());
+                if (!isProductionLink(linkTuple)) {
+                    edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
+                }
             }
         }
         return new Status(StatusCode.SUCCESS, null);
@@ -683,8 +717,8 @@ public class TopologyManagerImpl implements ITopologyManager,
     @Override
     public Status deleteUserLink(String linkName) {
         if (linkName == null) {
-            return new Status(StatusCode.BADREQUEST, 
-                       "A valid linkName is required to Delete a link");
+            return new Status(StatusCode.BADREQUEST,
+                    "A valid linkName is required to Delete a link");
         }
 
         TopologyUserLinkConfig link = userLinks.get(linkName);
@@ -692,20 +726,13 @@ public class TopologyManagerImpl implements ITopologyManager,
         Edge linkTuple = getLinkTuple(link);
         userLinks.remove(linkName);
         if (linkTuple != null) {
-            try {
-                //oneTopology.deleteUserConfiguredLink(linkTuple);
-            } catch (Exception e) {
-                log
-                        .warn("Harmless : Exception while Deleting User Configured link "
-                                + link + " " + e.toString());
+            if (!isProductionLink(linkTuple)) {
+                edgeUpdate(linkTuple, UpdateType.REMOVED, null);
             }
+
             linkTuple = getReverseLinkTuple(link);
-            try {
-                //oneTopology.deleteUserConfiguredLink(linkTuple);
-            } catch (Exception e) {
-                log
-                        .error("Harmless : Exception while Deleting User Configured Reverse link "
-                                + link + " " + e.toString());
+            if ((linkTuple != null) && !isProductionLink(linkTuple)) {
+                edgeUpdate(linkTuple, UpdateType.REMOVED, null);
             }
         }
         return new Status(StatusCode.SUCCESS, null);
@@ -722,67 +749,69 @@ public class TopologyManagerImpl implements ITopologyManager,
     public String getHelp() {
         StringBuffer help = new StringBuffer();
         help.append("---Topology Manager---\n");
-        help
-                .append("\t addTopo name <src-sw-id> <port-number> <dst-sw-id> <port-number>\n");
-        help.append("\t delTopo name\n");
-        help.append("\t _printTopo\n");
+        help.append("\t addUserLink <name> <node connector string> <node connector string>\n");
+        help.append("\t deleteUserLink <name>\n");
+        help.append("\t printUserLink\n");
+        help.append("\t printNodeEdges\n");
         return help.toString();
     }
 
-    public void _printTopo(CommandInterpreter ci) {
+    public void _printUserLink(CommandInterpreter ci) {
         for (String name : this.userLinks.keySet()) {
-            ci.println(name + " : " + userLinks.get(name));
+            TopologyUserLinkConfig linkConfig = userLinks.get(name);
+            ci.println("Name : " + name);
+            ci.println(linkConfig);
+            ci.println("Edge " + getLinkTuple(linkConfig));
+            ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
         }
     }
 
-    public void _addTopo(CommandInterpreter ci) {
+    public void _addUserLink(CommandInterpreter ci) {
         String name = ci.nextArgument();
         if ((name == null)) {
             ci.println("Please enter a valid Name");
             return;
         }
 
-        String dpid = ci.nextArgument();
-        if (dpid == null) {
-            ci.println("Invalid Switch ID. Format xx:xx:xx:xx:xx:xx:xx:xx");
-            return;
-        }
-        try {
-            HexEncode.stringToLong(dpid);
-        } catch (Exception e) {
-            ci.println("Invalid Switch ID. Format xx:xx:xx:xx:xx:xx:xx:xx");
+        String ncStr1 = ci.nextArgument();
+        if (ncStr1 == null) {
+            ci.println("Please enter two node connector strings");
             return;
         }
-
-        String port = ci.nextArgument();
-        if (port == null) {
-            ci.println("Invalid port number");
+        String ncStr2 = ci.nextArgument();
+        if (ncStr2 == null) {
+            ci.println("Please enter second node connector string");
             return;
         }
 
-        String ddpid = ci.nextArgument();
-        if (ddpid == null) {
-            ci.println("Invalid Switch ID. Format xx:xx:xx:xx:xx:xx:xx:xx");
+        NodeConnector nc1 = NodeConnector.fromString(ncStr1);
+        if (nc1 == null) {
+            ci.println("Invalid input node connector 1 string: " + ncStr1);
             return;
         }
-        try {
-            HexEncode.stringToLong(ddpid);
-        } catch (Exception e) {
-            ci.println("Invalid Switch ID. Format xx:xx:xx:xx:xx:xx:xx:xx");
+        NodeConnector nc2 = NodeConnector.fromString(ncStr2);
+        if (nc2 == null) {
+            ci.println("Invalid input node connector 2 string: " + ncStr2);
             return;
         }
 
-        String dport = ci.nextArgument();
-        if (dport == null) {
-            ci.println("Invalid port number");
-            return;
-        }
+        String nodeType1 = nc1.getNode().getType().toString();
+        String nid1 = nc1.getNode().getID().toString();
+        String ncType1 = nc1.getType().toString();
+        String ncid1 = nc1.getID().toString();
+
+        String nodeType2 = nc2.getNode().getType().toString();
+        String nid2 = nc2.getNode().getID().toString();
+        String ncType2 = nc2.getType().toString();
+        String ncid2 = nc2.getID().toString();
+
         TopologyUserLinkConfig config = new TopologyUserLinkConfig(name,
-                dpid, port, ddpid, dport);
+                nodeType1, nid1, ncType1, ncid1, nodeType2, nid2, ncType2,
+                ncid2);
         ci.println(this.addUserLink(config));
     }
 
-    public void _delTopo(CommandInterpreter ci) {
+    public void _deleteUserLink(CommandInterpreter ci) {
         String name = ci.nextArgument();
         if ((name == null)) {
             ci.println("Please enter a valid Name");
@@ -791,6 +820,27 @@ public class TopologyManagerImpl implements ITopologyManager,
         this.deleteUserLink(name);
     }
 
+    public void _printNodeEdges(CommandInterpreter ci) {
+        Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
+        if (nodeEdges == null) {
+            return;
+        }
+        Set<Node> nodeSet = nodeEdges.keySet();
+        if (nodeSet == null) {
+            return;
+        }
+        ci.println("        Node                                         Edge");
+        for (Node node : nodeSet) {
+            Set<Edge> edgeSet = nodeEdges.get(node);
+            if (edgeSet == null) {
+                continue;
+            }
+            for (Edge edge : edgeSet) {
+                ci.println(node + "             " + edge);
+            }
+        }
+    }
+
     @Override
     public Object readObject(ObjectInputStream ois)
             throws FileNotFoundException, IOException, ClassNotFoundException {
@@ -805,12 +855,12 @@ public class TopologyManagerImpl implements ITopologyManager,
 
     @Override
     public void edgeOverUtilized(Edge edge) {
-        log.warn("Link Utilization above normal: " + edge);
+        log.warn("Link Utilization above normal: {}", edge);
     }
 
     @Override
     public void edgeUtilBackToNormal(Edge edge) {
-        log.warn("Link Utilization back to normal: " + edge);
+        log.warn("Link Utilization back to normal: {}", edge);
     }
 
 }