Merge "Enabling Remote RPC Router module in ODL distribution."
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / TopologyServiceShim.java
index 5d0ee5dccb51afbf6358e0b340eebf1a0e343208..dacc130831ff18332adff071d400621096e8b48b 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -10,7 +9,9 @@
 package org.opendaylight.controller.protocol_plugin.openflow.internal;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -26,37 +27,42 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.eclipse.osgi.framework.console.CommandInterpreter;
 import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.protocol_plugin.openflow.IDiscoveryListener;
+import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsManager;
 import org.opendaylight.controller.protocol_plugin.openflow.IRefreshInternalProvider;
 import org.opendaylight.controller.protocol_plugin.openflow.ITopologyServiceShimListener;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.Config;
 import org.opendaylight.controller.sal.core.ContainerFlow;
 import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.IContainerAware;
 import org.opendaylight.controller.sal.core.IContainerListener;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
 import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.State;
 import org.opendaylight.controller.sal.core.UpdateType;
-import org.opendaylight.controller.sal.discovery.IDiscoveryService;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * The class describes a shim layer that relays the topology events from OpenFlow
- * core to various listeners. The notifications are filtered based on container
- * configurations.
+ * The class describes a shim layer that relays the topology events from
+ * OpenFlow core to various listeners. The notifications are filtered based on
+ * container configurations.
  */
-public class TopologyServiceShim implements IDiscoveryService,
-        IContainerListener, CommandProvider, IRefreshInternalProvider {
+public class TopologyServiceShim implements IDiscoveryListener,
+        IContainerListener, CommandProvider, IRefreshInternalProvider,
+        IInventoryShimExternalListener, IContainerAware {
     protected static final Logger logger = LoggerFactory
             .getLogger(TopologyServiceShim.class);
     private ConcurrentMap<String, ITopologyServiceShimListener> topologyServiceShimListeners = new ConcurrentHashMap<String, ITopologyServiceShimListener>();
     private ConcurrentMap<NodeConnector, List<String>> containerMap = new ConcurrentHashMap<NodeConnector, List<String>>();
-    private ConcurrentMap<String, Map<NodeConnector, Pair<Edge, Set<Property>>>> edgeMap = new ConcurrentHashMap<String, Map<NodeConnector, Pair<Edge, Set<Property>>>>();
+    private ConcurrentMap<String, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>> edgeMap = new ConcurrentHashMap<String, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>>();
 
     private BlockingQueue<NotifyEntry> notifyQ;
     private Thread notifyThread;
@@ -69,46 +75,79 @@ public class TopologyServiceShim implements IDiscoveryService,
     private Thread bwUtilNotifyThread;
     private BlockingQueue<UtilizationUpdate> bwUtilNotifyQ;
     private List<NodeConnector> connectorsOverUtilized;
-    private float bwThresholdFactor = (float) 0.8; // Threshold = 80% of link bandwidth
+    private float bwThresholdFactor = (float) 0.8; // Threshold = 80% of link
+                                                   // bandwidth
 
     class NotifyEntry {
         String container;
-        Pair<Edge, Set<Property>> edgeProps;
-        UpdateType type;
+        List<TopoEdgeUpdate> teuList;
 
-        NotifyEntry(String container, Pair<Edge, Set<Property>> edgeProps,
-                UpdateType type) {
+        public NotifyEntry(String container, TopoEdgeUpdate teu) {
             this.container = container;
-            this.edgeProps = edgeProps;
-            this.type = type;
+            this.teuList = new ArrayList<TopoEdgeUpdate>();
+            if (teu != null) {
+                this.teuList.add(teu);
+            }
+        }
+
+        public NotifyEntry(String container, List<TopoEdgeUpdate> teuList) {
+            this.container = container;
+            this.teuList = new ArrayList<TopoEdgeUpdate>();
+            if (teuList != null) {
+                this.teuList.addAll(teuList);
+            }
         }
     }
 
     class TopologyNotify implements Runnable {
         private final BlockingQueue<NotifyEntry> notifyQ;
+        private NotifyEntry entry;
+        private Map<String, List<TopoEdgeUpdate>> teuMap = new HashMap<String, List<TopoEdgeUpdate>>();
+        private List<TopoEdgeUpdate> teuList;
+        private boolean notifyListeners;
 
         TopologyNotify(BlockingQueue<NotifyEntry> notifyQ) {
             this.notifyQ = notifyQ;
         }
 
+        @Override
         public void run() {
             while (true) {
                 try {
-                    NotifyEntry entry = notifyQ.take();
+                    teuMap.clear();
+                    notifyListeners = false;
+                    while (!notifyQ.isEmpty()) {
+                        entry = notifyQ.take();
+                        teuList = teuMap.get(entry.container);
+                        if (teuList == null) {
+                            teuList = new ArrayList<TopoEdgeUpdate>();
+                        }
+                        // group all the updates together
+                        teuList.addAll(entry.teuList);
+                        teuMap.put(entry.container, teuList);
+                        notifyListeners = true;
+                    }
 
-                    ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners
-                            .get(entry.container);
-                    topologServiceShimListener.edgeUpdate(entry.edgeProps
-                            .getLeft(), entry.type, entry.edgeProps.getRight());
+                    if (notifyListeners) {
+                        for (String container : teuMap.keySet()) {
+                            // notify the listener
+                            ITopologyServiceShimListener l = topologyServiceShimListeners.get(container);
+                            // container topology service may not have come up yet
+                            if (l != null) {
+                                l.edgeUpdate(teuMap.get(container));
+                            }
+                        }
+                    }
 
-                    entry = null;
+                    Thread.sleep(100);
                 } catch (InterruptedException e1) {
-                    logger.warn("TopologyNotify interrupted", e1.getMessage());
+                    logger.warn("TopologyNotify interrupted {}",
+                            e1.getMessage());
                     if (shuttingDown) {
                         return;
                     }
                 } catch (Exception e2) {
-                    e2.printStackTrace();
+                    logger.error("", e2);
                 }
             }
         }
@@ -131,38 +170,44 @@ public class TopologyServiceShim implements IDiscoveryService,
             this.notifyQ = notifyQ;
         }
 
+        @Override
         public void run() {
             while (true) {
                 try {
                     UtilizationUpdate update = notifyQ.take();
                     NodeConnector connector = update.connector;
-                    Set<String> containerList = edgeMap.keySet();//.get(connector);
+                    Set<String> containerList = edgeMap.keySet();
                     for (String container : containerList) {
                         Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
                                 .get(container);
-                        Edge edge = edgePropsMap.get(connector).getLeft();
-                        if (edge.getTailNodeConnector().equals(connector)) {
-                            ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners
-                                    .get(container);
-                            if (update.type == UpdateType.ADDED) {
-                                topologServiceShimListener
-                                        .edgeOverUtilized(edge);
-                            } else {
-                                topologServiceShimListener
-                                        .edgeUtilBackToNormal(edge);
+                        // the edgePropsMap for a particular container may not have
+                        // the connector.
+                        // so check for null
+                        Pair<Edge, Set<Property>> edgeProp = edgePropsMap.get(connector);
+                        if(edgeProp != null) {
+                            Edge edge = edgeProp.getLeft();
+                            if (edge.getTailNodeConnector().equals(connector)) {
+                                ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners
+                                        .get(container);
+                                if (update.type == UpdateType.ADDED) {
+                                    topologServiceShimListener
+                                    .edgeOverUtilized(edge);
+                                } else {
+                                    topologServiceShimListener
+                                    .edgeUtilBackToNormal(edge);
+                                }
                             }
                         }
                     }
                 } catch (InterruptedException e1) {
-                    logger
-                            .warn(
-                                    "Edge Bandwidth Utilization Notify Thread interrupted",
-                                    e1.getMessage());
+                    logger.warn(
+                            "Edge Bandwidth Utilization Notify Thread interrupted {}",
+                            e1.getMessage());
                     if (shuttingDown) {
                         return;
                     }
                 } catch (Exception e2) {
-                    e2.printStackTrace();
+                    logger.error("", e2);
                 }
             }
         }
@@ -212,10 +257,10 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * Continuously polls the transmit bit rate for all the node connectors
-     * from statistics manager and trigger the warning notification upward
-     * when the transmit rate is above a threshold which is a percentage of
-     * the edge bandwidth
+     * Continuously polls the transmit bit rate for all the node connectors from
+     * statistics manager and trigger the warning notification upward when the
+     * transmit rate is above a threshold which is a percentage of the edge
+     * bandwidth
      */
     protected void pollTxBitRates() {
         Map<NodeConnector, Pair<Edge, Set<Property>>> globalContainerEdges = edgeMap
@@ -226,7 +271,8 @@ public class TopologyServiceShim implements IDiscoveryService,
 
         for (NodeConnector connector : globalContainerEdges.keySet()) {
             // Skip if node connector belongs to production switch
-            if (connector.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+            if (connector.getType().equals(
+                    NodeConnector.NodeConnectorIDType.PRODUCTION)) {
                 continue;
             }
 
@@ -258,18 +304,18 @@ public class TopologyServiceShim implements IDiscoveryService,
             // Compare bandwidth usage
             Long switchId = (Long) connector.getNode().getID();
             Short port = (Short) connector.getID();
-            float rate = statsMgr.getTransmitRate(switchId, port);
-            if (rate > bwThresholdFactor * bw) {
-                if (!connectorsOverUtilized.contains(connector)) {
-                    connectorsOverUtilized.add(connector);
-                    this.bwUtilNotifyQ.add(new UtilizationUpdate(connector,
-                            UpdateType.ADDED));
-                }
-            } else {
-                if (connectorsOverUtilized.contains(connector)) {
-                    connectorsOverUtilized.remove(connector);
-                    this.bwUtilNotifyQ.add(new UtilizationUpdate(connector,
-                            UpdateType.REMOVED));
+            if (statsMgr != null) {
+                float rate = statsMgr.getTransmitRate(switchId, port);
+                if (rate > bwThresholdFactor * bw) {
+                    if (!connectorsOverUtilized.contains(connector)) {
+                        connectorsOverUtilized.add(connector);
+                        this.bwUtilNotifyQ.add(new UtilizationUpdate(connector, UpdateType.ADDED));
+                    }
+                } else {
+                    if (connectorsOverUtilized.contains(connector)) {
+                        connectorsOverUtilized.remove(connector);
+                        this.bwUtilNotifyQ.add(new UtilizationUpdate(connector, UpdateType.REMOVED));
+                    }
                 }
             }
         }
@@ -277,9 +323,9 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * 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() {
@@ -289,9 +335,8 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * Function called by dependency manager after "init ()" is called
-     * and after the services provided by the class are registered in
-     * the service registry
+     * Function called by dependency manager after "init ()" is called and after
+     * the services provided by the class are registered in the service registry
      *
      */
     void start() {
@@ -303,9 +348,9 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * Function called by the dependency manager before the services
-     * exported by the component are unregistered, this will be
-     * followed by a "destroy ()" calls
+     * Function called by the dependency manager before the services exported by
+     * the component are unregistered, this will be followed by a "destroy ()"
+     * calls
      *
      */
     void stop() {
@@ -327,10 +372,10 @@ public class TopologyServiceShim implements IDiscoveryService,
         }
         if ((this.topologyServiceShimListeners != null)
                 && !this.topologyServiceShimListeners
-                       .containsKey(containerName)) {
+                        .containsKey(containerName)) {
             this.topologyServiceShimListeners.put(containerName, s);
-            logger.trace("Added topologyServiceShimListener for container:"
-                    containerName);
+            logger.trace("Added topologyServiceShimListener for container: {}",
+                    containerName);
         }
     }
 
@@ -346,14 +391,13 @@ public class TopologyServiceShim implements IDiscoveryService,
             return;
         }
         if ((this.topologyServiceShimListeners != null)
-                && this.topologyServiceShimListeners
-                .containsKey(containerName)
-                && this.topologyServiceShimListeners
-                .get(containerName).equals(s)
-                ) {
+                && this.topologyServiceShimListeners.containsKey(containerName)
+                && this.topologyServiceShimListeners.get(containerName).equals(
+                        s)) {
             this.topologyServiceShimListeners.remove(containerName);
-            logger.trace("Removed topologyServiceShimListener for container: "
-                    + containerName);
+            logger.trace(
+                    "Removed topologyServiceShimListener for container: {}",
+                    containerName);
         }
     }
 
@@ -367,8 +411,95 @@ public class TopologyServiceShim implements IDiscoveryService,
         }
     }
 
+    private void updateContainerMap(List<String> containers, NodeConnector p) {
+        if (containers.isEmpty()) {
+            // Do cleanup to reduce memory footprint if no
+            // elements to be tracked
+            this.containerMap.remove(p);
+        } else {
+            this.containerMap.put(p, containers);
+        }
+    }
+
+    /**
+     * From a given edge map, retrieve the edge sourced by the port and update
+     * the local cache in the container
+     *
+     * @param container
+     *            the container name
+     * @param nodeConnector
+     *            the node connector
+     * @param edges
+     *            the given edge map
+     * @return the found edge
+     */
+    private Edge addEdge(String container, NodeConnector nodeConnector,
+            Map<NodeConnector, Pair<Edge, Set<Property>>> edges) {
+        logger.debug("Search edge sourced by port {} in container {}", nodeConnector, container);
+
+        // Retrieve the associated edge
+        Pair<Edge, Set<Property>> edgeProps = edges.get(nodeConnector);
+        if (edgeProps == null) {
+            logger.debug("edgePros is null for port {} in container {}", nodeConnector, container);
+            return null;
+        }
+
+        Edge edge = edgeProps.getLeft();
+        if (edge == null) {
+            logger.debug("edge is null for port {} in container {}", nodeConnector, container);
+            return null;
+        }
+
+        // Make sure the peer port is in the same container
+        NodeConnector peerConnector = edge.getHeadNodeConnector();
+        List<String> containers = this.containerMap.get(peerConnector);
+        if ((containers == null) || !containers.contains(container)) {
+            logger.debug("peer port {} of edge {} is not part of the container {}", new Object[] { peerConnector, edge,
+                    container });
+            return null;
+        }
+
+        // Update the local cache
+        updateLocalEdgeMap(container, edge, UpdateType.ADDED, edgeProps.getRight());
+        logger.debug("Added edge {} to local cache in container {}", edge, container);
+
+        return edge;
+    }
+
+    private void addNodeConnector(String container,
+            NodeConnector nodeConnector) {
+        // Use the global edge map for the newly added port in a container
+        Map<NodeConnector, Pair<Edge, Set<Property>>> globalEdgeMap = edgeMap.get(GlobalConstants.DEFAULT
+                .toString());
+        if (globalEdgeMap == null) {
+            return;
+        }
+
+        // Get the edge and update local cache in the container
+        Edge edge1, edge2;
+        edge1 = addEdge(container, nodeConnector, globalEdgeMap);
+        if (edge1 == null) {
+            return;
+        }
+
+        // Get the edge in reverse direction and update local cache in the container
+        NodeConnector peerConnector = edge1.getHeadNodeConnector();
+        edge2 = addEdge(container, peerConnector, globalEdgeMap);
+
+        // Send notification upwards in one shot
+        List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
+        teuList.add(new TopoEdgeUpdate(edge1, null, UpdateType.ADDED));
+        logger.debug("Notify edge1: {} in container {}", edge1, container);
+        if (edge2 != null) {
+            teuList.add(new TopoEdgeUpdate(edge2, null, UpdateType.ADDED));
+            logger.debug("Notify edge2: {} in container {}", edge2, container);
+        }
+        notifyEdge(container, teuList);
+    }
+
     private void removeNodeConnector(String container,
             NodeConnector nodeConnector) {
+        List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
         Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
                 .get(container);
         if (edgePropsMap == null) {
@@ -380,7 +511,8 @@ public class TopologyServiceShim implements IDiscoveryService,
         if (edgeProps == null) {
             return;
         }
-        notifyEdge(container, edgeProps.getLeft(), UpdateType.REMOVED, null);
+        teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null,
+                UpdateType.REMOVED));
 
         // Remove edge in another direction
         edgeProps = edgePropsMap
@@ -388,51 +520,130 @@ public class TopologyServiceShim implements IDiscoveryService,
         if (edgeProps == null) {
             return;
         }
-        notifyEdge(container, edgeProps.getLeft(), UpdateType.REMOVED, null);
+        teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null,
+                UpdateType.REMOVED));
+
+        // Update in one shot
+        notifyEdge(container, teuList);
     }
 
-    private void notifyEdge(String container, Edge edge, UpdateType type,
-            Set<Property> props) {
-        Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
+    /**
+     * Update local cache and return true if it needs to notify upper layer
+     * Topology listeners.
+     *
+     * @param container
+     *            The network container
+     * @param edge
+     *            The edge
+     * @param type
+     *            The update type
+     * @param props
+     *            The edge properties
+     * @return true if it needs to notify upper layer Topology listeners
+     */
+    private boolean updateLocalEdgeMap(String container, Edge edge,
+            UpdateType type, Set<Property> props) {
+        ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
                 .get(container);
         NodeConnector src = edge.getTailNodeConnector();
         Pair<Edge, Set<Property>> edgeProps = new ImmutablePair<Edge, Set<Property>>(
                 edge, props);
+        boolean rv = false;
 
         switch (type) {
         case ADDED:
         case CHANGED:
             if (edgePropsMap == null) {
-                edgePropsMap = new HashMap<NodeConnector, Pair<Edge, Set<Property>>>();
+                edgePropsMap = new ConcurrentHashMap<NodeConnector, Pair<Edge, Set<Property>>>();
+                rv = true;
             } else {
                 if (edgePropsMap.containsKey(src)
                         && edgePropsMap.get(src).equals(edgeProps)) {
-                    // Entry already exists. Return here.
-                    return;
+                    // Entry already exists. No update.
+                    rv = false;
+                } else {
+                    rv = true;
                 }
             }
-            edgePropsMap.put(src, edgeProps);
-            edgeMap.put(container, edgePropsMap);
+            if (rv) {
+                edgePropsMap.put(src, edgeProps);
+                edgeMap.put(container, edgePropsMap);
+            }
             break;
         case REMOVED:
-            if (edgePropsMap != null) {
+            if ((edgePropsMap != null) && edgePropsMap.containsKey(src)) {
                 edgePropsMap.remove(src);
                 if (edgePropsMap.isEmpty()) {
                     edgeMap.remove(container);
                 } else {
                     edgeMap.put(container, edgePropsMap);
                 }
+                rv = true;
             }
             break;
         default:
-            logger.debug("Invalid " + type + " Edge " + edge
-                    + " in container {}", container);
+            logger.debug(
+                    "notifyLocalEdgeMap: invalid {} for Edge {} in container {}",
+                    new Object[] { type.getName(), edge, container });
+        }
+
+        if (rv) {
+            logger.debug(
+                    "notifyLocalEdgeMap: {} for Edge {} in container {}",
+                    new Object[] { type.getName(), edge, container });
+        }
+
+        return rv;
+    }
+
+    private void notifyEdge(String container, Edge edge, UpdateType type,
+            Set<Property> props) {
+        boolean notifyListeners;
+
+        // Update local cache
+        notifyListeners = updateLocalEdgeMap(container, edge, type, props);
+
+        // Prepare to update TopologyService
+        if (notifyListeners) {
+            notifyQ.add(new NotifyEntry(container, new TopoEdgeUpdate(edge, props,
+                    type)));
+            logger.debug("notifyEdge: {} Edge {} in container {}",
+                    new Object[] { type.getName(), edge, container });
+        }
+    }
+
+    private void notifyEdge(String container, List<TopoEdgeUpdate> etuList) {
+        if (etuList == null) {
             return;
         }
 
-        notifyQ.add(new NotifyEntry(container, edgeProps, type));
+        Edge edge;
+        UpdateType type;
+        List<TopoEdgeUpdate> etuNotifyList = new ArrayList<TopoEdgeUpdate>();
+        boolean notifyListeners = false, rv;
+
+        for (TopoEdgeUpdate etu : etuList) {
+            edge = etu.getEdge();
+            type = etu.getUpdateType();
+
+            // Update local cache
+            rv = updateLocalEdgeMap(container, edge, type, etu.getProperty());
+            if (rv) {
+                if (!notifyListeners) {
+                    notifyListeners = true;
+                }
+                etuNotifyList.add(etu);
+                logger.debug(
+                        "notifyEdge(TopoEdgeUpdate): {} Edge {} in container {}",
+                        new Object[] { type.getName(), edge, container });
+            }
+        }
 
-        logger.trace(type + " Edge " + edge + " in container {}", container);
+        // Prepare to update TopologyService
+        if (notifyListeners) {
+            notifyQ.add(new NotifyEntry(container, etuNotifyList));
+            logger.debug("notifyEdge(TopoEdgeUpdate): add notifyQ");
+        }
     }
 
     @Override
@@ -460,8 +671,7 @@ public class TopologyServiceShim implements IDiscoveryService,
         NodeConnector src = edge.getTailNodeConnector(), dst = edge
                 .getHeadNodeConnector();
 
-        if (!src.getType().equals(
-                NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+        if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
             /* Find the common containers for both ends */
             List<String> srcContainers = this.containerMap.get(src), dstContainers = this.containerMap
                     .get(dst), cmnContainers = null;
@@ -500,33 +710,24 @@ public class TopologyServiceShim implements IDiscoveryService,
         if (containers == null) {
             containers = new CopyOnWriteArrayList<String>();
         }
-        boolean updateMap = false;
         switch (t) {
         case ADDED:
             if (!containers.contains(containerName)) {
                 containers.add(containerName);
-                updateMap = true;
+                updateContainerMap(containers, p);
+                addNodeConnector(containerName, p);
             }
             break;
         case REMOVED:
             if (containers.contains(containerName)) {
                 containers.remove(containerName);
-                updateMap = true;
+                updateContainerMap(containers, p);
                 removeNodeConnector(containerName, p);
             }
             break;
         case CHANGED:
             break;
         }
-        if (updateMap) {
-            if (containers.isEmpty()) {
-                // Do cleanup to reduce memory footprint if no
-                // elements to be tracked
-                this.containerMap.remove(p);
-            } else {
-                this.containerMap.put(p, containers);
-            }
-        }
     }
 
     @Override
@@ -545,8 +746,8 @@ public class TopologyServiceShim implements IDiscoveryService,
     public String getHelp() {
         StringBuffer help = new StringBuffer();
         help.append("---Topology Service Shim---\n");
-        help
-                .append("\t pem [container]               - Print edgeMap entries for a given container\n");
+        help.append("\t pem [container]               - Print edgeMap entries");
+        help.append(" for a given container\n");
         return help.toString();
     }
 
@@ -557,28 +758,32 @@ public class TopologyServiceShim implements IDiscoveryService,
         }
 
         ci.println("Container: " + container);
-        ci
-                .println("                             Edge                                          Bandwidth");
+        ci.println("                             Edge                                          Bandwidth");
 
         Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
                 .get(container);
         if (edgePropsMap == null) {
             return;
         }
+        int count = 0;
         for (Pair<Edge, Set<Property>> edgeProps : edgePropsMap.values()) {
             if (edgeProps == null) {
                 continue;
             }
 
             long bw = 0;
-            for (Property prop : edgeProps.getRight()) {
-                if (prop.getName().equals(Bandwidth.BandwidthPropName)) {
-                    bw = ((Bandwidth) prop).getValue();
+            Set<Property> props = edgeProps.getRight();
+            if (props != null) {
+                for (Property prop : props) {
+                    if (prop.getName().equals(Bandwidth.BandwidthPropName)) {
+                        bw = ((Bandwidth) prop).getValue();
+                    }
                 }
             }
-
+            count++;
             ci.println(edgeProps.getLeft() + "          " + bw);
         }
+        ci.println("Total number of Edges: " + count);
     }
 
     public void _bwfactor(CommandInterpreter ci) {
@@ -593,15 +798,14 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * This method will trigger topology updates to be sent
-     * toward SAL.  SAL then pushes the updates to ALL the applications
-     * that have registered as listeners for this service.  SAL has no
-     * way of knowing which application requested for the refresh.
+     * This method will trigger topology updates to be sent toward SAL. SAL then
+     * pushes the updates to ALL the applications that have registered as
+     * listeners for this service. SAL has no way of knowing which application
+     * requested for the refresh.
      *
-     * As an example of this case, is stopping and starting the
-     * Topology Manager.  When the topology Manager is stopped,
-     * and restarted, it will no longer have the latest topology.
-     * Hence, a request is sent here.
+     * As an example of this case, is stopping and starting the Topology
+     * Manager. When the topology Manager is stopped, and restarted, it will no
+     * longer have the latest topology. Hence, a request is sent here.
      *
      * @param containerName
      * @return void
@@ -614,17 +818,35 @@ public class TopologyServiceShim implements IDiscoveryService,
     }
 
     /**
-     * Reading the current topology database, the method will replay
-     * all the edge updates for the ITopologyServiceShimListener instance
-     * in the given container, which will in turn publish them toward SAL.
+     * Retrieve the edges for a given container
+     *
      * @param containerName
+     *            the container name
+     * @return the edges and their properties
      */
-    private void TopologyBulkUpdate(String containerName) {
+    private Collection<Pair<Edge, Set<Property>>> getEdgeProps(String containerName) {
         Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropMap = null;
-
-        logger.debug("Try bulk update for container:{}", containerName);
         edgePropMap = edgeMap.get(containerName);
         if (edgePropMap == null) {
+            return null;
+        }
+        return edgePropMap.values();
+    }
+
+    /**
+     * Reading the current topology database, the method will replay all the
+     * edge updates for the ITopologyServiceShimListener instance in the given
+     * container, which will in turn publish them toward SAL.
+     *
+     * @param containerName
+     *            the container name
+     */
+    private void TopologyBulkUpdate(String containerName) {
+        Collection<Pair<Edge, Set<Property>>> edgeProps = null;
+
+        logger.debug("Try bulk update for container:{}", containerName);
+        edgeProps = getEdgeProps(containerName);
+        if (edgeProps == null) {
             logger.debug("No edges known for container:{}", containerName);
             return;
         }
@@ -636,15 +858,96 @@ public class TopologyServiceShim implements IDiscoveryService,
             return;
         }
         int i = 0;
-        for (Pair<Edge, Set<Property>> edgeProps : edgePropMap.values()) {
-            if (edgeProps != null) {
+        List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
+        for (Pair<Edge, Set<Property>> edgeProp : edgeProps) {
+            if (edgeProp != null) {
                 i++;
-                logger.trace("Add edge {}", edgeProps.getLeft());
-                topologServiceShimListener.edgeUpdate(edgeProps.getLeft(),
-                        UpdateType.ADDED, edgeProps.getRight());
+                teuList.add(new TopoEdgeUpdate(edgeProp.getLeft(), edgeProp
+                        .getRight(), UpdateType.ADDED));
+                logger.trace("Add edge {}", edgeProp.getLeft());
             }
         }
+        if (i > 0) {
+            topologServiceShimListener.edgeUpdate(teuList);
+        }
         logger.debug("Sent {} updates", i);
     }
 
+    @Override
+    public void updateNode(Node node, UpdateType type, Set<Property> props) {
+    }
+
+    @Override
+    public void updateNodeConnector(NodeConnector nodeConnector,
+            UpdateType type, Set<Property> props) {
+        List<String> containers = new ArrayList<String>();
+        List<String> conList = this.containerMap.get(nodeConnector);
+
+        containers.add(GlobalConstants.DEFAULT.toString());
+        if (conList != null) {
+            containers.addAll(conList);
+        }
+
+        switch (type) {
+        case ADDED:
+            break;
+        case CHANGED:
+            if (props == null) {
+                break;
+            }
+
+            boolean rmEdge = false;
+            for (Property prop : props) {
+                if (((prop instanceof Config) && (((Config) prop).getValue() != Config.ADMIN_UP))
+                        || ((prop instanceof State) && (((State) prop)
+                                .getValue() != State.EDGE_UP))) {
+                    /*
+                     * If port admin down or link down, remove the edges
+                     * associated with the port
+                     */
+                    rmEdge = true;
+                    break;
+                }
+            }
+
+            if (rmEdge) {
+                for (String cName : containers) {
+                    removeNodeConnector(cName, nodeConnector);
+                }
+            }
+            break;
+        case REMOVED:
+            for (String cName : containers) {
+                removeNodeConnector(cName, nodeConnector);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    @Override
+    public void containerCreate(String containerName) {
+        // do nothing
+    }
+
+    @Override
+    public void containerDestroy(String containerName) {
+        Set<NodeConnector> removeNodeConnectorSet = new HashSet<NodeConnector>();
+        for (Map.Entry<NodeConnector, List<String>> entry : containerMap.entrySet()) {
+            List<String> ncContainers = entry.getValue();
+            if (ncContainers.contains(containerName)) {
+                NodeConnector nodeConnector = entry.getKey();
+                removeNodeConnectorSet.add(nodeConnector);
+            }
+        }
+        for (NodeConnector nodeConnector : removeNodeConnectorSet) {
+            List<String> ncContainers = containerMap.get(nodeConnector);
+            ncContainers.remove(containerName);
+            if (ncContainers.isEmpty()) {
+                containerMap.remove(nodeConnector);
+            }
+        }
+        edgeMap.remove(containerName);
+    }
 }