org.opendaylight.snmp4sdn.internal.TopologyServiceShim.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.snmp4sdn.internal.TopologyServiceShim.java

Source

/*
 * Copyright (c) 2013 Industrial Technology Research Institute of Taiwan and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

/*
This code reused the code base of OpenFlow plugin contributed by Cisco Systems, Inc. Their efforts are appreciated.
*/

package org.opendaylight.snmp4sdn.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;

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.snmp4sdn.IDiscoveryListener;
import org.opendaylight.snmp4sdn.IInventoryShimExternalListener;
import org.opendaylight.snmp4sdn.IOFStatisticsManager;
import org.opendaylight.snmp4sdn.IRefreshInternalProvider;
import org.opendaylight.snmp4sdn.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.topology.TopoEdgeUpdate;
import org.opendaylight.controller.sal.utils.GlobalConstants;

/**
 * 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 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, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>> edgeMap = new ConcurrentHashMap<String, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>>();

    private BlockingQueue<NotifyEntry> notifyQ;
    private Thread notifyThread;
    private BlockingQueue<String> bulkNotifyQ;
    private Thread ofPluginTopoBulkUpdate;
    private volatile Boolean shuttingDown = false;
    private IOFStatisticsManager statsMgr;
    private Timer pollTimer;
    private TimerTask txRatePoller;
    private Thread bwUtilNotifyThread;
    private BlockingQueue<UtilizationUpdate> bwUtilNotifyQ;
    private List<NodeConnector> connectorsOverUtilized;
    private float bwThresholdFactor = (float) 0.8; // Threshold = 80% of link
                                                   // bandwidth

    class NotifyEntry {
        String container;
        List<TopoEdgeUpdate> teuList;

        public NotifyEntry(String container, TopoEdgeUpdate teu) {
            this.container = container;
            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;
        }

        public void run() {
            while (true) {
                try {
                    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;
                    }

                    if (notifyListeners) {
                        for (String container : teuMap.keySet()) {
                            // notify the listener
                            topologyServiceShimListeners.get(container).edgeUpdate(teuMap.get(container));
                        }
                    }

                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    logger.warn("TopologyNotify interrupted {}", e1.getMessage());
                    if (shuttingDown) {
                        return;
                    }
                } catch (Exception e2) {
                    logger.error("", e2);
                }
            }
        }
    }

    class UtilizationUpdate {
        NodeConnector connector;
        UpdateType type;

        UtilizationUpdate(NodeConnector connector, UpdateType type) {
            this.connector = connector;
            this.type = type;
        }
    }

    class BwUtilizationNotify implements Runnable {
        private final BlockingQueue<UtilizationUpdate> notifyQ;

        BwUtilizationNotify(BlockingQueue<UtilizationUpdate> notifyQ) {
            this.notifyQ = notifyQ;
        }

        public void run() {
            while (true) {
                try {
                    UtilizationUpdate update = notifyQ.take();
                    NodeConnector connector = update.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);
                            }
                        }
                    }
                } catch (InterruptedException e1) {
                    logger.warn("Edge Bandwidth Utilization Notify Thread interrupted {}", e1.getMessage());
                    if (shuttingDown) {
                        return;
                    }
                } catch (Exception e2) {
                    logger.error("", e2);
                }
            }
        }
    }

    /**
     * Function called by the dependency manager when all the required
     * dependencies are satisfied
     *
     */
    void init() {
        logger.trace("Init called");
        connectorsOverUtilized = new ArrayList<NodeConnector>();
        notifyQ = new LinkedBlockingQueue<NotifyEntry>();
        notifyThread = new Thread(new TopologyNotify(notifyQ));
        bwUtilNotifyQ = new LinkedBlockingQueue<UtilizationUpdate>();
        bwUtilNotifyThread = new Thread(new BwUtilizationNotify(bwUtilNotifyQ));
        bulkNotifyQ = new LinkedBlockingQueue<String>();
        ofPluginTopoBulkUpdate = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        String containerName = bulkNotifyQ.take();
                        logger.debug("Bulk Notify container:{}", containerName);
                        TopologyBulkUpdate(containerName);
                    } catch (InterruptedException e) {
                        logger.warn("Topology Bulk update thread interrupted");
                        if (shuttingDown) {
                            return;
                        }
                    }
                }
            }
        }, "Topology Bulk Update");

        // Initialize node connector tx bit rate poller timer
        pollTimer = new Timer();
        txRatePoller = new TimerTask() {
            @Override
            public void run() {
                pollTxBitRates();
            }
        };

        registerWithOSGIConsole();
    }

    /**
     * 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
                .get(GlobalConstants.DEFAULT.toString());
        if (globalContainerEdges == null) {
            return;
        }

        for (NodeConnector connector : globalContainerEdges.keySet()) {
            // Skip if node connector belongs to production switch
            if (connector.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
                continue;
            }

            // Get edge for which this node connector is head
            Pair<Edge, Set<Property>> props = this.edgeMap.get(GlobalConstants.DEFAULT.toString()).get(connector);
            // On switch mgr restart the props get reset
            if (props == null) {
                continue;
            }
            Set<Property> propSet = props.getRight();
            if (propSet == null) {
                continue;
            }

            float bw = 0;
            for (Property prop : propSet) {
                if (prop instanceof Bandwidth) {
                    bw = ((Bandwidth) prop).getValue();
                    break;
                }
            }

            // Skip if agent did not provide a bandwidth info for the edge
            if (bw == 0) {
                continue;
            }

            // 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));
                }
            }
        }

    }

    /**
     * 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() {
        logger.trace("DESTROY called!");
        notifyQ = null;
        notifyThread = null;
    }

    /**
     * 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() {
        logger.trace("START called!");
        notifyThread.start();
        bwUtilNotifyThread.start();
        ofPluginTopoBulkUpdate.start();
        pollTimer.scheduleAtFixedRate(txRatePoller, 10000, 5000);
    }

    /**
     * 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() {
        logger.trace("STOP called!");
        shuttingDown = true;
        notifyThread.interrupt();
    }

    void setTopologyServiceShimListener(Map<?, ?> props, ITopologyServiceShimListener s) {
        if (props == null) {
            logger.error("Didn't receive the service properties");
            return;
        }
        String containerName = (String) props.get("containerName");
        if (containerName == null) {
            logger.error("containerName not supplied");
            return;
        }
        if ((this.topologyServiceShimListeners != null)
                && !this.topologyServiceShimListeners.containsKey(containerName)) {
            this.topologyServiceShimListeners.put(containerName, s);
            logger.trace("Added topologyServiceShimListener for container: {}", containerName);
        }
    }

    void unsetTopologyServiceShimListener(Map<?, ?> props, ITopologyServiceShimListener s) {
        if (props == null) {
            logger.error("Didn't receive the service properties");
            return;
        }
        String containerName = (String) props.get("containerName");
        if (containerName == null) {
            logger.error("containerName not supplied");
            return;
        }
        if ((this.topologyServiceShimListeners != null)
                && this.topologyServiceShimListeners.containsKey(containerName)
                && this.topologyServiceShimListeners.get(containerName).equals(s)) {
            this.topologyServiceShimListeners.remove(containerName);
            logger.trace("Removed topologyServiceShimListener for container: {}", containerName);
        }
    }

    void setStatisticsManager(IOFStatisticsManager s) {
        this.statsMgr = s;
    }

    void unsetStatisticsManager(IOFStatisticsManager s) {
        if (this.statsMgr == s) {
            this.statsMgr = null;
        }
    }

    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) {
            return;
        }

        // Remove edge in one direction
        Pair<Edge, Set<Property>> edgeProps = edgePropsMap.get(nodeConnector);
        if (edgeProps == null) {
            return;
        }
        teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null, UpdateType.REMOVED));

        // Remove edge in another direction
        edgeProps = edgePropsMap.get(edgeProps.getLeft().getHeadNodeConnector());
        if (edgeProps == null) {
            return;
        }
        teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null, UpdateType.REMOVED));

        // Update in one shot
        notifyEdge(container, teuList);
    }

    /**
     * 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 ConcurrentHashMap<NodeConnector, Pair<Edge, Set<Property>>>();
                rv = true;
            } else {
                if (edgePropsMap.containsKey(src) && edgePropsMap.get(src).equals(edgeProps)) {
                    // Entry already exists. No update.
                    rv = false;
                } else {
                    rv = true;
                }
            }
            if (rv) {
                edgePropsMap.put(src, edgeProps);
                edgeMap.put(container, edgePropsMap);
            }
            break;
        case REMOVED:
            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("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;
        }

        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 });
            }
        }

        // Prepare to update TopologyService
        if (notifyListeners) {
            notifyQ.add(new NotifyEntry(container, etuNotifyList));
            logger.debug("notifyEdge(TopoEdgeUpdate): add notifyQ");
        }
    }

    @Override
    public void notifyEdge(Edge edge, UpdateType type, Set<Property> props) {
        if ((edge == null) || (type == null)) {
            return;
        }

        // Notify default container
        notifyEdge(GlobalConstants.DEFAULT.toString(), edge, type, props);

        // Notify the corresponding containers
        List<String> containers = getEdgeContainers(edge);
        if (containers != null) {
            for (String container : containers) {
                notifyEdge(container, edge, type, props);
            }
        }
    }

    /*
     * Return a list of containers the edge associated with
     */
    private List<String> getEdgeContainers(Edge edge) {
        NodeConnector src = edge.getTailNodeConnector(), dst = edge.getHeadNodeConnector();

        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;
            if ((srcContainers != null) && (dstContainers != null)) {
                cmnContainers = new ArrayList<String>(srcContainers);
                cmnContainers.retainAll(dstContainers);
            }
            return cmnContainers;
        } else {
            /*
             * If the neighbor is part of a monitored production network, get
             * the containers that the edge port belongs to
             */
            return this.containerMap.get(dst);
        }
    }

    @Override
    public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
    }

    @Override
    public void containerFlowUpdated(String containerName, ContainerFlow previousFlow, ContainerFlow currentFlow,
            UpdateType t) {
    }

    @Override
    public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType t) {
        if (this.containerMap == null) {
            logger.error("containerMap is NULL");
            return;
        }
        List<String> containers = this.containerMap.get(p);
        if (containers == null) {
            containers = new CopyOnWriteArrayList<String>();
        }
        boolean updateMap = false;
        switch (t) {
        case ADDED:
            if (!containers.contains(containerName)) {
                containers.add(containerName);
                updateMap = true;
            }
            break;
        case REMOVED:
            if (containers.contains(containerName)) {
                containers.remove(containerName);
                updateMap = true;
                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
    public void containerModeUpdated(UpdateType t) {
        // do nothing
    }

    private void registerWithOSGIConsole() {
        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
        bundleContext.registerService(CommandProvider.class.getName(), this, null);
    }

    @Override
    public String getHelp() {
        StringBuffer help = new StringBuffer();
        help.append("---Topology Service Shim---\n");
        help.append("\t pem [container]               - Print edgeMap entries");
        help.append(" for a given container\n");
        return help.toString();
    }

    public void _pem(CommandInterpreter ci) {
        String container = ci.nextArgument();
        if (container == null) {
            container = GlobalConstants.DEFAULT.toString();
        }

        ci.println("Container: " + container);
        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;
            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) {
        String factorString = ci.nextArgument();
        if (factorString == null) {
            ci.println("Bw threshold: " + this.bwThresholdFactor);
            ci.println("Insert a non null bw threshold");
            return;
        }
        bwThresholdFactor = Float.parseFloat(factorString);
        ci.println("New Bw threshold: " + this.bwThresholdFactor);
    }

    /**
     * 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.
     *
     * @param containerName
     * @return void
     */
    @Override
    public void requestRefresh(String containerName) {
        // wake up a bulk update thread and exit
        // the thread will execute the bulkUpdate()
        bulkNotifyQ.add(containerName);
    }

    /**
     * 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
     */
    private void TopologyBulkUpdate(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) {
            logger.debug("No edges known for container:{}", containerName);
            return;
        }
        ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners.get(containerName);
        if (topologServiceShimListener == null) {
            logger.debug("No topology service shim listener for container:{}", containerName);
            return;
        }
        int i = 0;
        List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
        for (Pair<Edge, Set<Property>> edgeProps : edgePropMap.values()) {
            if (edgeProps != null) {
                i++;
                teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), edgeProps.getRight(), UpdateType.ADDED));
                logger.trace("Add edge {}", edgeProps.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);
    }

}