From 8dc731c29a80828f7872d955c03fb51e1a79b2a2 Mon Sep 17 00:00:00 2001
From: kwanggithub <kwang@clemson.edu>
Date: Sat, 2 Feb 2013 23:23:10 -0800
Subject: [PATCH] Squashed commit of the following:

commit 8f1917bd34be3c81f4fc049b4736e4fbe3f1cd0e
Author: Shudong Zhou <shudongzhou@gmail.com>
Date:   Thu Jan 31 00:59:16 2013 -0800

    Fix singleton task initialization

commit dd361d41dafca32cae1f2e8f6be0725633082dc5
Author: Shudong Zhou <shudongzhou@gmail.com>
Date:   Wed Jan 30 21:58:22 2013 -0800

    Send HA role request to switch when master controller gets permission error

commit c18d87699bda09c07707a36e026fb5a4e6301e0d
Author: Shudong Zhou <shudongzhou@gmail.com>
Date:   Wed Jan 30 20:33:51 2013 -0800

    Dampen frequency of HA role transitions

commit ed7c1bd030ce6826510c52427ccea1f8631e09fa
Author: Shudong Zhou <shudongzhou@gmail.com>
Date:   Wed Jan 30 20:04:43 2013 -0800

    Update unit test for HA role change

commit 32dc0b95b34a82902f1309aebbe469b4b713b356
Merge: 4963170 63a85a2
Author: abat <abat@bigswitch.com>
Date:   Wed Jan 30 16:27:28 2013 -0800

    Merge into master from pull request #2414: lower a message level to trace

commit 63a85a28be92ca5c14d826198a572c9e408b0343
Author: Kanzhe Jiang <kanzhe.jiang@bigswitch.com>
Date:   Wed Jan 30 14:55:52 2013 -0800

    lower log level to info

commit 49631705e7393452cd6825ae126e053c6ff3f3a1
Author: Gregor Maier <gregor.maier@bigswitch.com>
Date:   Wed Jan 30 10:10:07 2013 -0800

    Adding debug logging to SwitchPortMatcher. Tweak log levels.

    We'll now log when SwitchPortMatcher can't find the switch or port for a
    packet its trying to match.

commit ca75639f53067087be0b308e3aa72e07bf75d924
Author: Srinivasan Ramasubramanian <srini@bigswitch.com>
Date:   Tue Jan 29 12:20:13 2013 -0800

    Bug fix: When tunnel links are identified, they should not be added to topology switchportlinks.  Update unit tests.

commit 3ee7da2f062834929212a568bf07cb462eb41979
Author: kwanggithub <kwang@clemson.edu>
Date:   Mon Jan 28 11:51:52 2013 -0800

    refactor loadbalancer to not use StaticFlowEntries

commit a0cac00f334970199ca2af9679d927d69fa539ab
Author: Kanzhe Jiang <kanzhe.jiang@bigswitch.com>
Date:   Sun Jan 27 18:05:23 2013 -0800

    notify listeners for device classification first, then listeners for reconcile.

commit 5925b52aa2f0d7aae290dd22d30626bf81e879c3
Author: Srinivasan Ramasubramanian <srini@bigswitch.com>
Date:   Wed Jan 23 01:24:29 2013 -0800

    Refactoring some methods to better support extensibility.

commit ae577282bdc906262cf3fed44eabf685090ca60e
Merge: 9f1613e 1d88cbf
Author: abat <abat@bigswitch.com>
Date:   Mon Jan 21 17:22:21 2013 -0800

    Merge into master from pull request #2255

commit 9f1613e936c5b9485a798486e19670b8dc2dd8eb
Author: Gregor Maier <gregor.maier@bigswitch.com>
Date:   Sat Jan 19 23:25:48 2013 -0800

    Don't use vlans from devices. Use explicitly specified VLAN.

commit 1d88cbff4bc53f8936145abc8ca653fa0d58c9ce
Author: Ronald Li <ronald.li@bigswitch.com>
Date:   Thu Jan 10 18:08:39 2013 -0800
---
 .../core/internal/Controller.java             |  50 +-
 .../devicemanager/IDeviceService.java         |  24 +-
 .../devicemanager/internal/Device.java        |   9 +
 .../internal/DeviceManagerImpl.java           | 129 +++--
 .../devicemanager/web/DeviceSerializer.java   |   2 +
 .../flowcache/FCQueryObj.java                 |   6 +-
 .../linkdiscovery/ILinkDiscoveryService.java  |  14 +-
 .../internal/LinkDiscoveryManager.java        | 119 +++--
 .../loadbalancer/LoadBalancer.java            | 504 +++++++++++++++++-
 .../net/floodlightcontroller/packet/DHCP.java |   1 +
 .../net/floodlightcontroller/packet/LLDP.java |   5 +-
 .../routing/ForwardingBase.java               |  29 +-
 .../topology/TopologyManager.java             |  55 +-
 .../virtualnetwork/VirtualNetworkFilter.java  |   2 +-
 .../core/internal/ControllerTest.java         |   3 +
 .../internal/DeviceManagerImplTest.java       |  15 +-
 .../internal/DeviceUniqueIndexTest.java       |   6 +-
 .../devicemanager/test/MockDevice.java        |   3 +-
 .../devicemanager/test/MockDeviceManager.java |  10 +-
 .../internal/LinkDiscoveryManagerTest.java    |   2 +-
 20 files changed, 840 insertions(+), 148 deletions(-)

diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 5f9cbd81d..73f3bed66 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -41,6 +41,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
@@ -57,6 +59,7 @@ import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
 import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
 import net.floodlightcontroller.core.util.ListenerDispatcher;
+import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.core.web.CoreWebRoutable;
 import net.floodlightcontroller.counter.ICounterStoreService;
 import net.floodlightcontroller.flowcache.IFlowCacheService;
@@ -186,9 +189,12 @@ public class Controller implements IFloodlightProviderService,
     // this role and then notify, on a transition to master we first notify
     // and then set the role. We then use it to make sure we don't forward
     // OF messages while the modules are in slave role. 
-    protected volatile Role notifiedRole; 
+    // The pendingRole is a role change just received, but not sent out
+    // notifications yet.
+    protected Role pendingRole;protected volatile Role notifiedRole;
     // A helper that handles sending and timeout handling for role requests
     protected RoleChanger roleChanger;
+    protected SingletonTask roleChangeDamper;
 
     // Start time of the controller
     protected long systemStartTime;
@@ -289,8 +295,8 @@ public class Controller implements IFloodlightProviderService,
                 }
                 return;
             }
-            if (log.isTraceEnabled()) {
-                log.trace("Dispatching HA Role update newRole = {}, oldRole = {}",
+            if (log.isDebugEnabled()) {
+                log.debug("Dispatching HA Role update newRole = {}, oldRole = {}",
                           newRole, oldRole);
             }
             // Set notified role to slave before notifying listeners. This
@@ -382,19 +388,26 @@ public class Controller implements IFloodlightProviderService,
     public void setRole(Role role) {
         if (role == null) throw new NullPointerException("Role can not be null.");
 
+        // If role is changed in quick succession for some reason,
+        // the 2 second delay will dampen the frequency.
+        this.pendingRole = role;
+        roleChangeDamper.reschedule(2000, TimeUnit.MILLISECONDS);
+    }
+
+    protected void doSetRole() {
         // Need to synchronize to ensure a reliable ordering on role request
         // messages send and to ensure the list of connected switches is stable
         // RoleChanger will handle the actual sending of the message and
         // timeout handling
         // @see RoleChanger
         synchronized(roleChanger) {
-            if (role.equals(this.role)) {
+            if (pendingRole.equals(this.role)) {
                 log.debug("Ignoring role change: role is already {}", role);
                 return;
             }
 
             Role oldRole = this.role;
-            this.role = role;
+            this.role = pendingRole;
 
             log.debug("Submitting role change request to role {}", role);
             roleChanger.submitRequest(connectedSwitches, role);
@@ -974,8 +987,22 @@ public class Controller implements IFloodlightProviderService,
                     if (roleChanger.checkFirstPendingRoleRequestXid(
                             sw, error.getXid())) {
                         roleChanger.deliverRoleRequestError(sw, error);
-                    }
-                    else {
+                    } else if (error.getErrorCode() ==
+                            OFErrorType.OFPET_BAD_REQUEST.getValue() &&
+                            error.getErrorType() ==
+                            OFBadRequestCode.OFPBRC_EPERM.ordinal() &&
+                            role.equals(Role.MASTER)) {
+                        // We are the master and the switch returned permission
+                        // error. Send a role change request in case switch set
+                        // the master to someone else.
+                        // Only send if there are no pending requests.
+                        synchronized(roleChanger) {
+                            if (roleChanger.pendingRequestMap.get(sw) == null) {
+                                log.info("Tell switch {} who is the master", sw);
+                                roleChanger.submitRequest(Collections.singleton(sw), role);
+                            }
+                        }
+                    } else {
                         logError(sw, error);
 
                         // allow registered listeners to receive error messages
@@ -1809,6 +1836,15 @@ public class Controller implements IFloodlightProviderService,
 
         // Add our REST API
         restApi.addRestletRoutable(new CoreWebRoutable());
+
+        // Start role change task
+        ScheduledExecutorService ses = threadPool.getScheduledExecutor();
+        roleChangeDamper = new SingletonTask(ses, new Runnable() {
+            @Override
+            public void run() {
+                doSetRole();
+            }
+        });
     }
 
     @Override
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
index e161825a6..1164163f5 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
@@ -30,6 +30,23 @@ import net.floodlightcontroller.core.module.IFloodlightService;
  * from the {@link FloodlightContext} rather than from {@link IDeviceManager}.
  */
 public interface IDeviceService extends IFloodlightService {
+
+    /**
+     * IDeviceListeners do one of two actions on devices in response to the device events:
+     * 1. Reclassify device
+     * 2. Reconcile flows for the device.
+     *
+     * We must make sure all device classification are done before flow reconciliation.
+     * So all listeners are grouped into two groups based on their types.
+     * Reclassification group is notified first, then the reconciliation group.
+     *
+     * When a module registers as a listener, it must specify the type.
+     */
+    enum ListenerType {
+        DeviceClassifier, // Listener should only do device classification change.
+        DeviceReconciler, // Listener should do reconciliation only
+    }
+
     /**
      * Fields used in devices for indexes and querying
      * @see IDeviceService#addIndex
@@ -189,8 +206,9 @@ public interface IDeviceService extends IFloodlightService {
      * Adds a listener to listen for IDeviceManagerServices notifications
      * 
      * @param listener The listener that wants the notifications
+     * @param type     The type of the listener
      */
-    public void addListener(IDeviceListener listener);
+    public void addListener(IDeviceListener listener, ListenerType type);
     
     /**
      * Specify points in the network where attachment points are not to
@@ -198,8 +216,8 @@ public interface IDeviceService extends IFloodlightService {
      * @param sw
      * @param port
      */
-	public void addSuppressAPs(long swId, short port);
+    public void addSuppressAPs(long swId, short port);
 
-	public void removeSuppressAPs(long swId, short port);
+    public void removeSuppressAPs(long swId, short port);
 
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index 9d59caac4..d2b712de8 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -62,6 +62,7 @@ public class Device implements IDevice {
     protected String macAddressString;
     // the vlan Ids from the entities of this device
     protected Short[] vlanIds;
+    protected String dhcpClientName;
     
    
 
@@ -97,6 +98,7 @@ public class Device implements IDevice {
         this.entityClass = entityClass;
         Arrays.sort(this.entities);
 
+        this.dhcpClientName = null;
         this.oldAPs = null;
         this.attachmentPoints = null;
 
@@ -126,12 +128,14 @@ entity.getLastSeenTimestamp().getTime());
      */
     public Device(DeviceManagerImpl deviceManager,
                   Long deviceKey,
+                  String dhcpClientName,
                   Collection<AttachmentPoint> oldAPs,
                   Collection<AttachmentPoint> attachmentPoints,
                   Collection<Entity> entities,
                   IEntityClass entityClass) {
         this.deviceManager = deviceManager;
         this.deviceKey = deviceKey;
+        this.dhcpClientName = dhcpClientName;
         this.entities = entities.toArray(new Entity[entities.size()]);
         this.oldAPs = null;
         this.attachmentPoints = null;
@@ -167,6 +171,7 @@ entity.getLastSeenTimestamp().getTime());
                   int insertionpoint) {
         this.deviceManager = device.deviceManager;
         this.deviceKey = device.deviceKey;
+        this.dhcpClientName = device.dhcpClientName;
         
         this.entities = new Entity[device.entities.length + 1];
         if (insertionpoint < 0) {
@@ -708,6 +713,10 @@ entity.getLastSeenTimestamp().getTime());
         return entities;
     }
 
+    public String getDHCPClientName() {
+        return dhcpClientName;
+    }
+
     // ***************
     // Utility Methods
     // ***************
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index 8f9281243..dff8cba7c 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -61,8 +61,12 @@ import net.floodlightcontroller.flowcache.IFlowReconcileService;
 import net.floodlightcontroller.flowcache.OFMatchReconcile;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
 import net.floodlightcontroller.packet.ARP;
+import net.floodlightcontroller.packet.DHCP;
+import net.floodlightcontroller.packet.DHCPOption;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.UDP;
+import net.floodlightcontroller.packet.DHCP.DHCPOptionCode;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
@@ -193,8 +197,11 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     /**
      * Device manager event listeners
+     * reclassifyDeviceListeners are notified first before reconcileDeviceListeners.
+     * This is to make sure devices are correctly reclassified before reconciliation.
      */
-    protected Set<IDeviceListener> deviceListeners;
+    protected Set<IDeviceListener> reclassifyDeviceListeners;
+    protected Set<IDeviceListener> reconcileDeviceListeners;
 
     /**
      * A device update event to be dispatched
@@ -519,8 +526,15 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
     }
 
     @Override
-    public void addListener(IDeviceListener listener) {
-        deviceListeners.add(listener);
+    public void addListener(IDeviceListener listener, ListenerType type) {
+        switch (type) {
+            case DeviceClassifier:
+                reclassifyDeviceListeners.add(listener);
+                break;
+            case DeviceReconciler:
+                reconcileDeviceListeners.add(listener);
+                break;
+        }
     }
 
     // *************
@@ -670,7 +684,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 new HashSet<EnumSet<DeviceField>>();
         addIndex(true, EnumSet.of(DeviceField.IPV4));
 
-        this.deviceListeners = new HashSet<IDeviceListener>();
+        this.reclassifyDeviceListeners = new HashSet<IDeviceListener>();
+        this.reconcileDeviceListeners = new HashSet<IDeviceListener>();
         this.suppressAPs = Collections.newSetFromMap(
                                new ConcurrentHashMap<SwitchPort, Boolean>());
 
@@ -788,9 +803,37 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                         new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
                         srcDevice, dstDevice });
        }
+
+        snoopDHCPClientName(eth, srcDevice);
+
         return Command.CONTINUE;
     }
 
+    /**
+     * Snoop and record client-provided host name from DHCP requests
+     * @param eth
+     * @param srcDevice
+     */
+    private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
+        if (eth.getEtherType() != Ethernet.TYPE_IPv4)
+            return;
+        IPv4 ipv4 = (IPv4) eth.getPayload();
+        if (ipv4.getProtocol() != IPv4.PROTOCOL_UDP)
+            return;
+        UDP udp = (UDP) ipv4.getPayload();
+        if (!(udp.getPayload() instanceof DHCP))
+            return;
+        DHCP dhcp = (DHCP) udp.getPayload();
+        byte opcode = dhcp.getOpCode();
+        if (opcode == DHCP.OPCODE_REQUEST) {
+            DHCPOption dhcpOption = dhcp.getOption(
+                    DHCPOptionCode.OptionCode_Hostname);
+            if (dhcpOption != null) {
+                srcDevice.dhcpClientName = new String(dhcpOption.getData());
+            }
+        }
+    }
+
     /**
      * Check whether the given attachment point is valid given the current
      * topology
@@ -1280,39 +1323,44 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             if (logger.isTraceEnabled()) {
                 logger.trace("Dispatching device update: {}", update);
             }
-            for (IDeviceListener listener : deviceListeners) {
-                switch (update.change) {
-                    case ADD:
-                        listener.deviceAdded(update.device);
-                        break;
-                    case DELETE:
-                        listener.deviceRemoved(update.device);
-                        break;
-                    case CHANGE:
-                        for (DeviceField field : update.fieldsChanged) {
-                            switch (field) {
-                                case IPV4:
-                                    listener.deviceIPV4AddrChanged(update.device);
-                                    break;
-                                case SWITCH:
-                                case PORT:
-                                    //listener.deviceMoved(update.device);
-                                    break;
-                                case VLAN:
-                                    listener.deviceVlanChanged(update.device);
-                                    break;
-                                default:
-                                    logger.debug("Unknown device field changed {}",
-                                                update.fieldsChanged.toString());
-                                    break;
-                            }
+            notifyListeners(reclassifyDeviceListeners, update);
+            notifyListeners(reconcileDeviceListeners, update);
+        }
+    }
+
+    protected void notifyListeners(Set<IDeviceListener> listeners, DeviceUpdate update) {
+        for (IDeviceListener listener : listeners) {
+            switch (update.change) {
+                case ADD:
+                    listener.deviceAdded(update.device);
+                    break;
+                case DELETE:
+                    listener.deviceRemoved(update.device);
+                    break;
+                case CHANGE:
+                    for (DeviceField field : update.fieldsChanged) {
+                        switch (field) {
+                            case IPV4:
+                                listener.deviceIPV4AddrChanged(update.device);
+                                break;
+                            case SWITCH:
+                            case PORT:
+                                //listener.deviceMoved(update.device);
+                                break;
+                            case VLAN:
+                                listener.deviceVlanChanged(update.device);
+                                break;
+                            default:
+                                logger.debug("Unknown device field changed {}",
+                                            update.fieldsChanged.toString());
+                                break;
                         }
-                        break;
-                }
+                    }
+                    break;
             }
         }
     }
-    
+
     /**
      * Check if the entity e has all the keyFields set. Returns false if not
      * @param e entity to check 
@@ -1477,6 +1525,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
                 if (toKeep.size() > 0) {
                     Device newDevice = allocateDevice(d.getDeviceKey(),
+                                                      d.getDHCPClientName(),
                                                       d.oldAPs,
                                                       d.attachmentPoints,
                                                       toKeep,
@@ -1595,11 +1644,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     // TODO: FIX THIS.
     protected Device allocateDevice(Long deviceKey,
+                                    String dhcpClientName,
                                     List<AttachmentPoint> aps,
                                     List<AttachmentPoint> trueAPs,
                                     Collection<Entity> entities,
                                     IEntityClass entityClass) {
-        return new Device(this, deviceKey, aps, trueAPs, entities, entityClass);
+        return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
+                          entities, entityClass);
     }
 
     protected Device allocateDevice(Device device,
@@ -1630,8 +1681,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         }
         if (newAPs.isEmpty())
             newAPs = null;
-        Device d = new Device(this, device.getDeviceKey(),newAPs, null,
-                        entities, device.getEntityClass());
+        Device d = new Device(this, device.getDeviceKey(),
+                              device.getDHCPClientName(), newAPs, null,
+                              entities, device.getEntityClass());
         d.updateAttachmentPoint();
         return d;
     }
@@ -1677,7 +1729,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
      * @param updates the updates to process.
      */
     protected void sendDeviceMovedNotification(Device d) {
-        for (IDeviceListener listener : deviceListeners) {
+        for (IDeviceListener listener : reclassifyDeviceListeners) {
+            listener.deviceMoved(d);
+        }
+        for (IDeviceListener listener : reconcileDeviceListeners) {
             listener.deviceMoved(d);
         }
     }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
index 66bdaef93..4e4e07db9 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
@@ -64,6 +64,8 @@ public class DeviceSerializer extends JsonSerializer<Device> {
 
         jGen.writeNumberField("lastSeen", device.getLastSeen().getTime());
         
+        jGen.writeStringField("dhcpClientName", device.getDHCPClientName());
+
         jGen.writeEndObject();
     }
 
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java b/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java
index fe84079c5..8cad49b31 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java
@@ -68,11 +68,7 @@ public class FCQueryObj {
         if (vlan != null) {
         	this.vlans = new Short[] { vlan };
         } else {
-	        if (srcDevice != null) {
-	        	this.vlans = srcDevice.getVlanId();
-	        } else if (dstDevice != null) {
-	            this.vlans = dstDevice.getVlanId();
-	        }
+            this.vlans = null;
         }
     }
 
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
index 84d1cc958..441347e20 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
@@ -19,6 +19,9 @@ package net.floodlightcontroller.linkdiscovery;
 
 import java.util.Map;
 import java.util.Set;
+
+import org.openflow.protocol.OFPacketOut;
+
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.topology.NodePortTuple;
@@ -44,6 +47,15 @@ public interface ILinkDiscoveryService extends IFloodlightService {
      */
     public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info);
 
+    /**
+     * Returns OFPacketOut which contains the LLDP data corresponding
+     * to switchport (sw, port). PacketOut does not contain actions.
+     * PacketOut length includes the minimum length and data length.
+     */
+    public OFPacketOut generateLLDPMessage(long sw, short port,
+                                           boolean isStandard,
+                                           boolean isReverse);
+
     /**
      * Returns an unmodifiable map from switch id to a set of all links with it 
      * as an endpoint.
@@ -91,5 +103,5 @@ public interface ILinkDiscoveryService extends IFloodlightService {
      * Get the map of node-port tuples from link DB
      */
     public Map<NodePortTuple, Set<Link>> getPortLinks();
-    
+
 }
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 3191ab7a9..107114b4d 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -518,6 +518,20 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         sendDiscoveryMessage(sw, port, true, false);
     }
 
+    /**
+     * This method is used to specifically ignore/consider specific
+     * links.
+     * @param src
+     * @param srcPort
+     * @param dst
+     * @param dstPort
+     * @return
+     */
+    protected boolean isLinkAllowed(long src, short srcPort,
+                                    long dst, short dstPort) {
+        return true;
+    }
+
     /**
      * Check if incoming discovery messages are enabled or not.
      * @param sw
@@ -606,31 +620,8 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return actions;
     }
 
-    /**
-     * Send link discovery message out of a given switch port. The discovery
-     * message may be a standard LLDP or a modified LLDP, where the dst mac
-     * address is set to :ff. TODO: The modified LLDP will updated in the future
-     * and may use a different eth-type.
-     * 
-     * @param sw
-     * @param port
-     * @param isStandard
-     *            indicates standard or modified LLDP
-     * @param isReverse
-     *            indicates whether the LLDP was sent as a response
-     */
-    @LogMessageDoc(level = "ERROR",
-                   message = "Failure sending LLDP out port {port} on switch {switch}",
-                   explanation = "An I/O error occured while sending LLDP message "
-                                 + "to the switch.",
-                   recommendation = LogMessageDoc.CHECK_SWITCH)
-    protected
-            void sendDiscoveryMessage(long sw, short port,
-                                      boolean isStandard, boolean isReverse) {
-
-        // Takes care of all checks including null pointer checks.
-        if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse))
-            return;
+    public OFPacketOut generateLLDPMessage(long sw, short port,
+                                       boolean isStandard, boolean isReverse) {
 
         IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
         OFPhysicalPort ofpPort = iofSwitch.getPort(port);
@@ -638,6 +629,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         if (log.isTraceEnabled()) {
             log.trace("Sending LLDP packet out of swich: {}, port: {}",
                       HexString.toHexString(sw), port);
+            return null;
         }
 
         // using "nearest customer bridge" MAC address for broadest possible
@@ -731,9 +723,52 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
         po.setInPort(OFPort.OFPP_NONE);
 
+        // set data and data length
+        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + data.length);
+        po.setPacketData(data);
+
+        return po;
+    }
+
+    /**
+     * Send link discovery message out of a given switch port. The discovery
+     * message may be a standard LLDP or a modified LLDP, where the dst mac
+     * address is set to :ff. TODO: The modified LLDP will updated in the future
+     * and may use a different eth-type.
+     *
+     * @param sw
+     * @param port
+     * @param isStandard
+     *            indicates standard or modified LLDP
+     * @param isReverse
+     *            indicates whether the LLDP was sent as a response
+     */
+    @LogMessageDoc(level = "ERROR",
+                   message = "Failure sending LLDP out port {port} on switch {switch}",
+                   explanation = "An I/O error occured while sending LLDP message "
+                                 + "to the switch.",
+                   recommendation = LogMessageDoc.CHECK_SWITCH)
+    protected
+            void sendDiscoveryMessage(long sw, short port,
+                                      boolean isStandard, boolean isReverse) {
+
+        // Takes care of all checks including null pointer checks.
+        if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse))
+            return;
+
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+
+        if (log.isTraceEnabled()) {
+            log.trace("Sending LLDP packet out of swich: {}, port: {}",
+                      HexString.toHexString(sw), port);
+            return;
+        }
+        OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse);
+
+        // Add actions
         List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort);
         po.setActions(actions);
-        
         short  actionLength = 0;
         Iterator <OFAction> actionIter = actions.iterator();
         while (actionIter.hasNext()) {
@@ -741,10 +776,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         }
         po.setActionsLength(actionLength);
 
-        // set data
-        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength()
-                      + data.length);
-        po.setPacketData(data);
+        // po already has the minimum length + data length set
+        // simply add the actions length to this.
+        po.setLengthU(po.getLengthU() + po.getActionsLength());
 
         // send
         try {
@@ -754,7 +788,6 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             log.error("Failure sending LLDP out port {} on switch {}",
                       new Object[] { port, iofSwitch.getStringId() }, e);
         }
-
     }
 
     /**
@@ -854,12 +887,12 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return Command.CONTINUE;
     }
 
-    private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi,
+    private Command handleLldp(LLDP lldp, long sw, short inPort,
                                boolean isStandard, FloodlightContext cntx) {
         // If LLDP is suppressed on this port, ignore received packet as well
         IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
 
-        if (!isIncomingDiscoveryAllowed(sw, pi.getInPort(), isStandard))
+        if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard))
             return Command.STOP;
 
         // If this is a malformed LLDP, or not from us, exit
@@ -946,10 +979,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             }
             return Command.STOP;
         }
-        if (!iofSwitch.portEnabled(pi.getInPort())) {
+        if (!iofSwitch.portEnabled(inPort)) {
             if (log.isTraceEnabled()) {
                 log.trace("Ignoring link with disabled dest port: switch {} port {}",
-                          HexString.toHexString(sw), pi.getInPort());
+                          HexString.toHexString(sw), inPort);
             }
             return Command.STOP;
         }
@@ -957,15 +990,20 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
         int srcPortState = (physicalPort != null) ? physicalPort.getState()
                                                  : 0;
-        physicalPort = iofSwitch.getPort(pi.getInPort());
+        physicalPort = iofSwitch.getPort(inPort);
         int dstPortState = (physicalPort != null) ? physicalPort.getState()
                                                  : 0;
 
         // Store the time of update to this link, and push it out to
         // routingEngine
         Link lt = new Link(remoteSwitch.getId(), remotePort,
-                           iofSwitch.getId(), pi.getInPort());
+                           iofSwitch.getId(), inPort);
 
+        if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(),
+                           lt.getDst(), lt.getDstPort()))
+            return Command.STOP;
+
+        // Continue only if link is allowed.
         Long lastLldpTime = null;
         Long lastBddpTime = null;
 
@@ -980,8 +1018,11 @@ public class LinkDiscoveryManager implements IOFMessageListener,
                                             lastBddpTime, srcPortState,
                                             dstPortState);
 
+
         addOrUpdateLink(lt, newLinkInfo);
 
+        // Continue only if addOrUpdateLink was successful.
+
         // Check if reverse link exists.
         // If it doesn't exist and if the forward link was seen
         // first seen within a small interval, send probe on the
@@ -1043,9 +1084,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             // continue with the regular processing.
             if (bsn.getPayload() instanceof LLDP == false)
                                                           return Command.CONTINUE;
-            return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
+            return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx);
         } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
-            return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
+            return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx);
         } else if (eth.getEtherType() < 1500) {
             long destMac = eth.getDestinationMAC().toLong();
             if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
index 90d6829c9..e67a2fb29 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
@@ -24,8 +24,11 @@ import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.openflow.protocol.OFFlowMod;
 import org.openflow.protocol.OFMatch;
@@ -35,7 +38,18 @@ import org.openflow.protocol.OFPacketOut;
 import org.openflow.protocol.OFPort;
 import org.openflow.protocol.OFType;
 import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionDataLayerDestination;
+import org.openflow.protocol.action.OFActionDataLayerSource;
+import org.openflow.protocol.action.OFActionEnqueue;
+import org.openflow.protocol.action.OFActionNetworkLayerDestination;
+import org.openflow.protocol.action.OFActionNetworkLayerSource;
+import org.openflow.protocol.action.OFActionNetworkTypeOfService;
 import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.protocol.action.OFActionStripVirtualLan;
+import org.openflow.protocol.action.OFActionTransportLayerDestination;
+import org.openflow.protocol.action.OFActionTransportLayerSource;
+import org.openflow.protocol.action.OFActionVirtualLanIdentifier;
+import org.openflow.protocol.action.OFActionVirtualLanPriorityCodePoint;
 import org.openflow.util.HexString;
 import org.openflow.util.U16;
 import org.slf4j.Logger;
@@ -63,7 +77,6 @@ import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntries;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.topology.NodePortTuple;
 import net.floodlightcontroller.util.MACAddress;
@@ -556,7 +569,7 @@ public class LoadBalancer implements IFloodlightModule,
                    
                }
                
-               StaticFlowEntries.parseActionString(fm, actionString, log);
+               parseActionString(fm, actionString, log);
 
                fm.setPriority(U16.t(LB_PRIORITY));
 
@@ -824,4 +837,491 @@ public class LoadBalancer implements IFloodlightModule,
         restApi.addRestletRoutable(new LoadBalancerWebRoutable());
     }
 
+    // Utilities borrowed from StaticFlowEntries
+    
+    private static class SubActionStruct {
+        OFAction action;
+        int      len;
+    }
+    
+    /**
+     * Parses OFFlowMod actions from strings.
+     * @param flowMod The OFFlowMod to set the actions for
+     * @param actionstr The string containing all the actions
+     * @param log A logger to log for errors.
+     */
+    public static void parseActionString(OFFlowMod flowMod, String actionstr, Logger log) {
+        List<OFAction> actions = new LinkedList<OFAction>();
+        int actionsLength = 0;
+        if (actionstr != null) {
+            actionstr = actionstr.toLowerCase();
+            for (String subaction : actionstr.split(",")) {
+                String action = subaction.split("[=:]")[0];
+                SubActionStruct subaction_struct = null;
+                
+                if (action.equals("output")) {
+                    subaction_struct = decode_output(subaction, log);
+                }
+                else if (action.equals("enqueue")) {
+                    subaction_struct = decode_enqueue(subaction, log);
+                }
+                else if (action.equals("strip-vlan")) {
+                    subaction_struct = decode_strip_vlan(subaction, log);
+                }
+                else if (action.equals("set-vlan-id")) {
+                    subaction_struct = decode_set_vlan_id(subaction, log);
+                }
+                else if (action.equals("set-vlan-priority")) {
+                    subaction_struct = decode_set_vlan_priority(subaction, log);
+                }
+                else if (action.equals("set-src-mac")) {
+                    subaction_struct = decode_set_src_mac(subaction, log);
+                }
+                else if (action.equals("set-dst-mac")) {
+                    subaction_struct = decode_set_dst_mac(subaction, log);
+                }
+                else if (action.equals("set-tos-bits")) {
+                    subaction_struct = decode_set_tos_bits(subaction, log);
+                }
+                else if (action.equals("set-src-ip")) {
+                    subaction_struct = decode_set_src_ip(subaction, log);
+                }
+                else if (action.equals("set-dst-ip")) {
+                    subaction_struct = decode_set_dst_ip(subaction, log);
+                }
+                else if (action.equals("set-src-port")) {
+                    subaction_struct = decode_set_src_port(subaction, log);
+                }
+                else if (action.equals("set-dst-port")) {
+                    subaction_struct = decode_set_dst_port(subaction, log);
+                }
+                else {
+                    log.error("Unexpected action '{}', '{}'", action, subaction);
+                }
+                
+                if (subaction_struct != null) {
+                    actions.add(subaction_struct.action);
+                    actionsLength += subaction_struct.len;
+                }
+            }
+        }
+        log.debug("action {}", actions);
+        
+        flowMod.setActions(actions);
+        flowMod.setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLength);
+    } 
+    
+    private static SubActionStruct decode_output(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n;
+        
+        n = Pattern.compile("output=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(subaction);
+        if (n.matches()) {
+            OFActionOutput action = new OFActionOutput();
+            action.setMaxLength((short) Short.MAX_VALUE);
+            short port = OFPort.OFPP_NONE.getValue();
+            if (n.group(1) != null) {
+                try {
+                    port = get_short(n.group(1));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid port in: '{}' (error ignored)", subaction);
+                    return null;
+                }
+            }
+            else if (n.group(2) != null)
+                port = OFPort.OFPP_ALL.getValue();
+            else if (n.group(3) != null)
+                port = OFPort.OFPP_CONTROLLER.getValue();
+            else if (n.group(4) != null)
+                port = OFPort.OFPP_LOCAL.getValue();
+            else if (n.group(5) != null)
+                port = OFPort.OFPP_IN_PORT.getValue();
+            else if (n.group(6) != null)
+                port = OFPort.OFPP_NORMAL.getValue();
+            else if (n.group(7) != null)
+                port = OFPort.OFPP_FLOOD.getValue();
+            action.setPort(port);
+            log.debug("action {}", action);
+            
+            sa = new SubActionStruct();
+            sa.action = action;
+            sa.len = OFActionOutput.MINIMUM_LENGTH;
+        }
+        else {
+            log.error("Invalid subaction: '{}'", subaction);
+            return null;
+        }
+        
+        return sa;
+    }
+    
+    private static SubActionStruct decode_enqueue(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n;
+        
+        n = Pattern.compile("enqueue=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(subaction);
+        if (n.matches()) {
+            short portnum = 0;
+            if (n.group(1) != null) {
+                try {
+                    portnum = get_short(n.group(1));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid port-num in: '{}' (error ignored)", subaction);
+                    return null;
+                }
+            }
+
+            int queueid = 0;
+            if (n.group(2) != null) {
+                try {
+                    queueid = get_int(n.group(2));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid queue-id in: '{}' (error ignored)", subaction);
+                    return null;
+               }
+            }
+            
+            OFActionEnqueue action = new OFActionEnqueue();
+            action.setPort(portnum);
+            action.setQueueId(queueid);
+            log.debug("action {}", action);
+            
+            sa = new SubActionStruct();
+            sa.action = action;
+            sa.len = OFActionEnqueue.MINIMUM_LENGTH;
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+        
+        return sa;
+    }
+    
+    private static SubActionStruct decode_strip_vlan(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("strip-vlan").matcher(subaction);
+        
+        if (n.matches()) {
+            OFActionStripVirtualLan action = new OFActionStripVirtualLan();
+            log.debug("action {}", action);
+            
+            sa = new SubActionStruct();
+            sa.action = action;
+            sa.len = OFActionStripVirtualLan.MINIMUM_LENGTH;
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static SubActionStruct decode_set_vlan_id(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction);
+        
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    short vlanid = get_short(n.group(1));
+                    OFActionVirtualLanIdentifier action = new OFActionVirtualLanIdentifier();
+                    action.setVirtualLanIdentifier(vlanid);
+                    log.debug("  action {}", action);
+
+                    sa = new SubActionStruct();
+                    sa.action = action;
+                    sa.len = OFActionVirtualLanIdentifier.MINIMUM_LENGTH;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN in: {} (error ignored)", subaction);
+                    return null;
+                }
+            }          
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static SubActionStruct decode_set_vlan_priority(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); 
+        
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    byte prior = get_byte(n.group(1));
+                    OFActionVirtualLanPriorityCodePoint action = new OFActionVirtualLanPriorityCodePoint();
+                    action.setVirtualLanPriorityCodePoint(prior);
+                    log.debug("  action {}", action);
+                    
+                    sa = new SubActionStruct();
+                    sa.action = action;
+                    sa.len = OFActionVirtualLanPriorityCodePoint.MINIMUM_LENGTH;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN priority in: {} (error ignored)", subaction);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static SubActionStruct decode_set_src_mac(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); 
+
+        if (n.matches()) {
+            byte[] macaddr = get_mac_addr(n, subaction, log);
+            if (macaddr != null) {
+                OFActionDataLayerSource action = new OFActionDataLayerSource();
+                action.setDataLayerAddress(macaddr);
+                log.debug("action {}", action);
+
+                sa = new SubActionStruct();
+                sa.action = action;
+                sa.len = OFActionDataLayerSource.MINIMUM_LENGTH;
+            }            
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+
+    private static SubActionStruct decode_set_dst_mac(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction);
+        
+        if (n.matches()) {
+            byte[] macaddr = get_mac_addr(n, subaction, log);            
+            if (macaddr != null) {
+                OFActionDataLayerDestination action = new OFActionDataLayerDestination();
+                action.setDataLayerAddress(macaddr);
+                log.debug("  action {}", action);
+                
+                sa = new SubActionStruct();
+                sa.action = action;
+                sa.len = OFActionDataLayerDestination.MINIMUM_LENGTH;
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static SubActionStruct decode_set_tos_bits(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); 
+
+        if (n.matches()) {
+            if (n.group(1) != null) {
+                try {
+                    byte tosbits = get_byte(n.group(1));
+                    OFActionNetworkTypeOfService action = new OFActionNetworkTypeOfService();
+                    action.setNetworkTypeOfService(tosbits);
+                    log.debug("  action {}", action);
+                    
+                    sa = new SubActionStruct();
+                    sa.action = action;
+                    sa.len = OFActionNetworkTypeOfService.MINIMUM_LENGTH;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid dst-port in: {} (error ignored)", subaction);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static SubActionStruct decode_set_src_ip(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction);
+
+        if (n.matches()) {
+            int ipaddr = get_ip_addr(n, subaction, log);
+            OFActionNetworkLayerSource action = new OFActionNetworkLayerSource();
+            action.setNetworkAddress(ipaddr);
+            log.debug("  action {}", action);
+
+            sa = new SubActionStruct();
+            sa.action = action;
+            sa.len = OFActionNetworkLayerSource.MINIMUM_LENGTH;
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+
+    private static SubActionStruct decode_set_dst_ip(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction);
+
+        if (n.matches()) {
+            int ipaddr = get_ip_addr(n, subaction, log);
+            OFActionNetworkLayerDestination action = new OFActionNetworkLayerDestination();
+            action.setNetworkAddress(ipaddr);
+            log.debug("action {}", action);
+ 
+            sa = new SubActionStruct();
+            sa.action = action;
+            sa.len = OFActionNetworkLayerDestination.MINIMUM_LENGTH;
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+
+    private static SubActionStruct decode_set_src_port(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); 
+
+        if (n.matches()) {
+            if (n.group(1) != null) {
+                try {
+                    short portnum = get_short(n.group(1));
+                    OFActionTransportLayerSource action = new OFActionTransportLayerSource();
+                    action.setTransportPort(portnum);
+                    log.debug("action {}", action);
+                    
+                    sa = new SubActionStruct();
+                    sa.action = action;
+                    sa.len = OFActionTransportLayerSource.MINIMUM_LENGTH;;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid src-port in: {} (error ignored)", subaction);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+
+    private static SubActionStruct decode_set_dst_port(String subaction, Logger log) {
+        SubActionStruct sa = null;
+        Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction);
+
+        if (n.matches()) {
+            if (n.group(1) != null) {
+                try {
+                    short portnum = get_short(n.group(1));
+                    OFActionTransportLayerDestination action = new OFActionTransportLayerDestination();
+                    action.setTransportPort(portnum);
+                    log.debug("action {}", action);
+                    
+                    sa = new SubActionStruct();
+                    sa.action = action;
+                    sa.len = OFActionTransportLayerDestination.MINIMUM_LENGTH;;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid dst-port in: {} (error ignored)", subaction);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", subaction);
+            return null;
+        }
+
+        return sa;
+    }
+    
+    private static byte[] get_mac_addr(Matcher n, String subaction, Logger log) {
+        byte[] macaddr = new byte[6];
+        
+        for (int i=0; i<6; i++) {
+            if (n.group(i+1) != null) {
+                try {
+                    macaddr[i] = get_byte("0x" + n.group(i+1));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid src-mac in: '{}' (error ignored)", subaction);
+                    return null;
+                }
+            }
+            else { 
+                log.debug("Invalid src-mac in: '{}' (null, error ignored)", subaction);
+                return null;
+            }
+        }
+        
+        return macaddr;
+    }
+    
+    private static int get_ip_addr(Matcher n, String subaction, Logger log) {
+        int ipaddr = 0;
+
+        for (int i=0; i<4; i++) {
+            if (n.group(i+1) != null) {
+                try {
+                    ipaddr = ipaddr<<8;
+                    ipaddr = ipaddr | get_int(n.group(i+1));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid src-ip in: '{}' (error ignored)", subaction);
+                    return 0;
+                }
+            }
+            else {
+                log.debug("Invalid src-ip in: '{}' (null, error ignored)", subaction);
+                return 0;
+            }
+        }
+        
+        return ipaddr;
+    }
+    
+    // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0)
+    private static int get_int(String str) {
+        return (int)Integer.decode(str);
+    }
+   
+    // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0)
+    private static short get_short(String str) {
+        return (short)(int)Integer.decode(str);
+    }
+   
+    // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0)
+    private static byte get_byte(String str) {
+        return Integer.decode(str).byteValue();
+    }
+
+    
 }
diff --git a/src/main/java/net/floodlightcontroller/packet/DHCP.java b/src/main/java/net/floodlightcontroller/packet/DHCP.java
index 5ad215e57..fbc2d4353 100644
--- a/src/main/java/net/floodlightcontroller/packet/DHCP.java
+++ b/src/main/java/net/floodlightcontroller/packet/DHCP.java
@@ -63,6 +63,7 @@ public class DHCP extends BasePacket {
     
     public enum DHCPOptionCode {
         OptionCode_SubnetMask           ((byte)1),
+        OptionCode_Hostname             ((byte)12),
         OptionCode_RequestedIP          ((byte)50),
         OptionCode_LeaseTime            ((byte)51),
         OptionCode_MessageType          ((byte)53),
diff --git a/src/main/java/net/floodlightcontroller/packet/LLDP.java b/src/main/java/net/floodlightcontroller/packet/LLDP.java
index 5a1104104..130f1c731 100644
--- a/src/main/java/net/floodlightcontroller/packet/LLDP.java
+++ b/src/main/java/net/floodlightcontroller/packet/LLDP.java
@@ -105,7 +105,8 @@ public class LLDP extends BasePacket {
         int length = 2+this.chassisId.getLength() + 2+this.portId.getLength() +
             2+this.ttl.getLength() + 2;
         for (LLDPTLV tlv : this.optionalTLVList) {
-            length += 2 + tlv.getLength();
+            if (tlv != null)
+                length += 2 + tlv.getLength();
         }
 
         byte[] data = new byte[length];
@@ -114,7 +115,7 @@ public class LLDP extends BasePacket {
         bb.put(this.portId.serialize());
         bb.put(this.ttl.serialize());
         for (LLDPTLV tlv : this.optionalTLVList) {
-            bb.put(tlv.serialize());
+            if (tlv != null) bb.put(tlv.serialize());
         }
         bb.putShort((short) 0); // End of LLDPDU
 
diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
index cd0cd3692..5a6827f49 100644
--- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
+++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
@@ -34,8 +34,6 @@ import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
 import net.floodlightcontroller.core.util.AppCookie;
 import net.floodlightcontroller.counter.ICounterStoreService;
-import net.floodlightcontroller.devicemanager.IDevice;
-import net.floodlightcontroller.devicemanager.IDeviceListener;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.packet.Ethernet;
@@ -66,7 +64,7 @@ import org.slf4j.LoggerFactory;
  */
 @LogMessageCategory("Flow Programming")
 public abstract class ForwardingBase 
-    implements IOFMessageListener, IDeviceListener {
+    implements IOFMessageListener {
     
     protected static Logger log =
             LoggerFactory.getLogger(ForwardingBase.class);
@@ -124,7 +122,6 @@ public abstract class ForwardingBase
      * Adds a listener for devicemanager and registers for PacketIns.
      */
     protected void startUp() {
-        deviceManager.addListener(this);
         floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
     }
 
@@ -592,30 +589,6 @@ public abstract class ForwardingBase
 
     }
 
-    @Override
-    public void deviceAdded(IDevice device) {
-        // NOOP
-    }
-
-    @Override
-    public void deviceRemoved(IDevice device) {
-        // NOOP
-    }
-
-    @Override
-    public void deviceMoved(IDevice device) {
-    }
-
-    @Override
-    public void deviceIPV4AddrChanged(IDevice device) {
-
-    }
-
-    @Override
-    public void deviceVlanChanged(IDevice device) {
-
-    }
-
     @Override
     public boolean isCallbackOrderingPrereq(OFType type, String name) {
         return (type.equals(OFType.PACKET_IN) && 
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index 347d21893..ed85b57ac 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -117,11 +117,11 @@ public class TopologyManager implements
 
     protected BlockingQueue<LDUpdate> ldUpdates;
     protected List<LDUpdate> appliedUpdates;
-    
+
     // These must be accessed using getCurrentInstance(), not directly
     protected TopologyInstance currentInstance;
     protected TopologyInstance currentInstanceWithoutTunnels;
-    
+
     protected SingletonTask newInstanceTask;
     private Date lastUpdateTime;
 
@@ -140,10 +140,29 @@ public class TopologyManager implements
      */
     protected boolean tunnelPortsUpdated;
 
-    protected final int TOPOLOGY_COMPUTE_INTERVAL_MS = 500;
+    protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500;
+
+   //  Getter/Setter methods
+    /**
+     * Get the time interval for the period topology updates, if any.
+     * The time returned is in milliseconds.
+     * @return
+     */
+    public int getTopologyComputeInterval() {
+        return TOPOLOGY_COMPUTE_INTERVAL_MS;
+    }
+
+    /**
+     * Set the time interval for the period topology updates, if any.
+     * The time is in milliseconds.
+     * @return
+     */
+    public void setTopologyComputeInterval(int time_ms) {
+        TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms;
+    }
 
     /**
-     * Thread for recomputing topology.  The thread is always running, 
+     * Thread for recomputing topology.  The thread is always running,
      * however the function applyUpdates() has a blocking call.
      */
     @LogMessageDoc(level="ERROR",
@@ -157,6 +176,7 @@ public class TopologyManager implements
             try {
                 if (ldUpdates.peek() != null)
                     updateTopology();
+                handleMiscellaneousPeriodicEvents();
             }
             catch (Exception e) {
                 log.error("Error in topology instance task thread", e);
@@ -168,6 +188,11 @@ public class TopologyManager implements
         }
     }
 
+    // To be used for adding any periodic events that's required by topology.
+    protected void handleMiscellaneousPeriodicEvents() {
+        return;
+    }
+
     public boolean updateTopology() {
         boolean newInstanceFlag;
         linksUpdated = false;
@@ -1206,23 +1231,35 @@ public class TopologyManager implements
         return result1 || result2;
     }
 
+    protected void addOrUpdateTunnelLink(long srcId, short srcPort, long dstId,
+                                    short dstPort) {
+        // If you need to handle tunnel links, this is a placeholder.
+    }
+
     public void addOrUpdateLink(long srcId, short srcPort, long dstId, 
                                 short dstPort, LinkType type) {
         Link link = new Link(srcId, srcPort, dstId, dstPort);
-        addPortToSwitch(srcId, srcPort);
-        addPortToSwitch(dstId, dstPort);
-
-        addLinkToStructure(switchPortLinks, link);
 
         if (type.equals(LinkType.MULTIHOP_LINK)) {
+            addPortToSwitch(srcId, srcPort);
+            addPortToSwitch(dstId, dstPort);
+            addLinkToStructure(switchPortLinks, link);
+
             addLinkToStructure(portBroadcastDomainLinks, link);
             dtLinksUpdated = removeLinkFromStructure(directLinks, link);
+            linksUpdated = true;
         } else if (type.equals(LinkType.DIRECT_LINK)) {
+            addPortToSwitch(srcId, srcPort);
+            addPortToSwitch(dstId, dstPort);
+            addLinkToStructure(switchPortLinks, link);
+
             addLinkToStructure(directLinks, link);
             removeLinkFromStructure(portBroadcastDomainLinks, link);
             dtLinksUpdated = true;
+            linksUpdated = true;
+        } else if (type.equals(LinkType.TUNNEL)) {
+            addOrUpdateTunnelLink(srcId, srcPort, dstId, dstPort);
         }
-        linksUpdated = true;
     }
 
     public void removeLink(Link link)  {
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
index 684d0687a..87dfc4a39 100644
--- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
@@ -312,7 +312,7 @@ public class VirtualNetworkFilter
     public void startUp(FloodlightModuleContext context) {
         floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
         restApi.addRestletRoutable(new VirtualNetworkWebRoutable());
-        deviceService.addListener(this);
+        deviceService.addListener(this, IDeviceService.ListenerType.DeviceClassifier);
     }
 
     // IOFMessageListener
diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
index f9169983a..f6f94f880 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
@@ -762,6 +762,7 @@ public class ControllerTest extends FloodlightTestCase
         
         replay(roleChanger);
         controller.setRole(Role.SLAVE);
+        controller.doSetRole(); // avoid wait
         verify(roleChanger);
         
         Controller.IUpdate upd = controller.updates.poll();
@@ -1531,6 +1532,7 @@ public class ControllerTest extends FloodlightTestCase
         expectLastCall().andReturn(Command.STOP).once();
         replay(sw, listener);
         controller.setRole(Role.SLAVE);
+        controller.doSetRole();  // avoid the wait
         chdlr.processOFMessage(pi);
         verify(sw, listener);
         assertEquals(1, controller.updates.size());
@@ -1550,6 +1552,7 @@ public class ControllerTest extends FloodlightTestCase
         resetToDefault(listener);
         replay(listener);
         controller.setRole(Role.MASTER);
+        controller.doSetRole(); // avoid the wait
         chdlr.processOFMessage(pi);
         verify(listener);
         assertEquals(1, controller.updates.size());
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index 2e64e99b3..5e3d764df 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -277,7 +277,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         IDeviceListener mockListener =
                 createStrictMock(IDeviceListener.class);
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
         deviceManager.entityClassifier= new MockEntityClassifier();
         deviceManager.startUp(null);
 
@@ -483,7 +483,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         IDeviceListener mockListener =
                 createStrictMock(IDeviceListener.class);
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
 
         ITopologyService mockTopology = createMock(ITopologyService.class);
         expect(mockTopology.getL2DomainId(1L)).
@@ -591,7 +591,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         IDeviceListener mockListener =
                 createMock(IDeviceListener.class);
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
 
         ITopologyService mockTopology = createMock(ITopologyService.class);
         expect(mockTopology.getL2DomainId(1L)).
@@ -706,7 +706,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         IDeviceListener mockListener =
                 createMock(IDeviceListener.class);
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
 
         ITopologyService mockTopology = createMock(ITopologyService.class);
         expect(mockTopology.getL2DomainId(1L)).
@@ -1097,7 +1097,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
 
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
         replay(mockListener);
         deviceManager.entityCleanupTask.reschedule(0, null);
 
@@ -1160,7 +1160,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         d = deviceManager.learnDeviceByEntity(entity1);
         assertArrayEquals(new Integer[] { 1, 2 }, d.getIPv4Addresses());
 
-        deviceManager.addListener(mockListener);
+        deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier);
         replay(mockListener);
         deviceManager.entityCleanupTask.reschedule(0, null);
 
@@ -1877,7 +1877,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
             Entity[] entities = new Entity[] { entity1, entity2,
                                                entity3, entity4
                                              };
-            Device d = new Device(null,1L, null, null, Arrays.asList(entities), null);
+            Device d = new Device(null,1L, null, null, null,
+                                  Arrays.asList(entities), null);
             SwitchPort swp1x1 = new SwitchPort(1L, 1);
             SwitchPort swp1x2 = new SwitchPort(1L, 2);
             SwitchPort swp2x1 = new SwitchPort(2L, 1);
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
index 2a6ee9d09..1ac1f1c35 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
@@ -51,7 +51,8 @@ public class DeviceUniqueIndexTest extends TestCase {
         List<Entity> d1Entities = new ArrayList<Entity>(2);
         d1Entities.add(e1a);
         d1Entities.add(e1b);
-        d1 = new Device(null, Long.valueOf(1), null, null, d1Entities, null);
+        d1 = new Device(null, Long.valueOf(1), null, null, null,
+                        d1Entities, null);
         
         // e2 and e2 alt match in MAC and VLAN
         e2 = new Entity(2L, (short)2, 2, 2L, 2, new Date());
@@ -145,7 +146,8 @@ public class DeviceUniqueIndexTest extends TestCase {
         // all key fields are null
         idx2.updateIndex(e4, 4L);
         assertEquals(null, idx2.findByEntity(e4));
-        Device d4 = new Device(null, 4L, null, null, Collections.<Entity>singleton(e4), null);
+        Device d4 = new Device(null, 4L, null, null, null,
+                               Collections.<Entity>singleton(e4), null);
         idx2.updateIndex(d4, 4L);
         assertEquals(null, idx2.findByEntity(e4));
         
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
index 1c2f7d1b4..d05197019 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
@@ -51,7 +51,8 @@ public class MockDevice extends Device {
                       List<AttachmentPoint> trueAPs,
                       Collection<Entity> entities,
                       IEntityClass entityClass) {
-        super(deviceManager, deviceKey, aps, trueAPs, entities, entityClass);
+        super(deviceManager, deviceKey, null, aps, trueAPs,
+              entities, entityClass);
     }
 
     @Override
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
index 73023c98d..dd105bba8 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
@@ -63,9 +63,11 @@ public class MockDeviceManager extends DeviceManagerImpl {
                                Integer ipv4Address, Long switchDPID, 
                                Integer switchPort,
                                boolean processUpdates) {
-        Set<IDeviceListener> listeners = deviceListeners;
+        Set<IDeviceListener> reclassifyListeners = reclassifyDeviceListeners;
+        Set<IDeviceListener> reconcileListeners = reconcileDeviceListeners;
         if (!processUpdates) {
-            deviceListeners = Collections.<IDeviceListener>emptySet();
+            reclassifyDeviceListeners = Collections.<IDeviceListener>emptySet();
+            reconcileDeviceListeners = Collections.<IDeviceListener>emptySet();
         }
         
         if (vlan != null && vlan.shortValue() <= 0)
@@ -75,7 +77,8 @@ public class MockDeviceManager extends DeviceManagerImpl {
         IDevice res =  learnDeviceByEntity(new Entity(macAddress, vlan, 
                                                       ipv4Address, switchDPID, 
                                                       switchPort, new Date()));
-        deviceListeners = listeners;
+        reclassifyDeviceListeners = reclassifyListeners;
+        reconcileDeviceListeners = reconcileListeners;
         return res;
     }
     
@@ -109,6 +112,7 @@ public class MockDeviceManager extends DeviceManagerImpl {
     
     @Override
     protected Device allocateDevice(Long deviceKey,
+                                    String dhcpClientName,
                                     List<AttachmentPoint> aps,
                                     List<AttachmentPoint> trueAPs,
                                     Collection<Entity> entities,
diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
index fe9f572e9..487e36ee7 100644
--- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
@@ -95,7 +95,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase {
         return ldm;
     }
 
-    public IOFSwitch createMockSwitch(Long id) {
+    private IOFSwitch createMockSwitch(Long id) {
         IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class);
         expect(mockSwitch.getId()).andReturn(id).anyTimes();
         return mockSwitch;
-- 
GitLab