From 68e74782556acef71d069a6cd5f9c6d1c2585e34 Mon Sep 17 00:00:00 2001
From: Rob Adams <rob.adams@bigswitch.com>
Date: Fri, 17 Feb 2012 15:25:38 -0800
Subject: [PATCH] Unit tests for the parts of device manager that should be
 working, and some effort at fixing compile errors in other modules

---
 .../floodlightcontroller/core/IListener.java  |   8 +-
 .../devicemanager/IDevice.java                |  44 +-
 .../devicemanager/IDeviceManager.java         |  61 ++
 .../devicemanager/IEntityClass.java           |  12 +-
 .../devicemanager/IEntityClassifier.java      |  28 +-
 .../devicemanager/SwitchPort.java             |  74 +++
 .../internal/DefaultEntityClassifier.java     |  29 +-
 .../devicemanager/internal/Device.java        | 176 ++++-
 .../internal/DeviceManagerImpl.java           | 626 ++++++++++++------
 .../devicemanager/internal/Entity.java        |  63 +-
 .../devicemanager/internal/IndexedEntity.java |  20 +-
 .../topology/ITopology.java                   |  16 +-
 .../topology/internal/TopologyImpl.java       |  14 +-
 .../floodlightcontroller/util/MACAddress.java |   2 +-
 .../java/org/openflow/util/HexString.java     |  10 +-
 .../internal/DeviceManagerImplTest.java       | 288 ++++----
 16 files changed, 1066 insertions(+), 405 deletions(-)
 create mode 100644 src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java

diff --git a/src/main/java/net/floodlightcontroller/core/IListener.java b/src/main/java/net/floodlightcontroller/core/IListener.java
index cb168608f..58b922e4c 100644
--- a/src/main/java/net/floodlightcontroller/core/IListener.java
+++ b/src/main/java/net/floodlightcontroller/core/IListener.java
@@ -17,7 +17,13 @@
 
 package net.floodlightcontroller.core;
 
-
+/**
+ * Interface for implementing an event listener with support for ordering 
+ * based on prerequisites and postrequisites.
+ * @author readams
+ *
+ * @param <T> The event type
+ */
 public interface IListener<T> {
     /**
      * The name assigned to this listener
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
index db0441a16..fa8c694c4 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
@@ -17,13 +17,55 @@
 
 package net.floodlightcontroller.devicemanager;
 
+import java.util.Date;
+
 /**
  * Represents an independent device on the network.  A device consists of a 
  * set of entities, and all the information known about a given device comes
  * only from merging all associated entities for that device.
  * @author readams
- *
  */
 public interface IDevice {
+    /**
+     * Get the primary key for this device.
+     * @return the primary key
+     */
+    public Long getDeviceKey();
+    
+    /**
+     * Get the MAC address of the device as a Long value.
+     * @return the MAC address for the device
+     */
+    public long getMACAddress();
 
+    /**
+     * Get the MAC address of the device as a String value.
+     * @return the MAC address for the device
+     */
+    public String getMACAddressString();
+    
+    /**
+     * Get all VLAN IDs for the device.  The array returned can have
+     * null entries representing untagged packets.
+     * @return an array containing all unique VLAN IDs for the device.
+     */
+    public Short[] getVlanId();
+    
+    /**
+     * Get all IPv4 addresses associated with the device.
+     * @return an array containing the unique IPv4 addresses for the device.
+     */
+    public Integer[] getIPv4Addresses();
+    
+    /**
+     * Get all attachment points associated with the device.
+     * @return an array containing all unique attachment points for the device
+     */
+    public SwitchPort[] getAttachmentPoints();
+    
+    /**
+     * Get the most recent timestamp for this device
+     * @return the last seen timestamp
+     */
+    public Date getLastSeen();
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManager.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManager.java
index 508707a6e..14bed96ad 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManager.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManager.java
@@ -17,11 +17,72 @@
 
 package net.floodlightcontroller.devicemanager;
 
+import java.util.Collection;
+
+import net.floodlightcontroller.core.FloodlightContextStore;
+
 /**
  * Device manager allows interacting with devices on the network.  Note
  * that under normal circumstances, {@link Device} objects should be retrieved
  * from the {@link FloodlightContext} rather than from {@link IDeviceManager}.
  */
 public interface IDeviceManager {
+    /**
+     * The source device for the current packet-in, if applicable.
+     */
+    public static final String CONTEXT_SRC_DEVICE = 
+            "com.bigswitch.floodlight.devicemanager.srcDevice"; 
+
+    /**
+     * The destination device for the current packet-in, if applicable.
+     */
+    public static final String CONTEXT_DST_DEVICE = 
+            "com.bigswitch.floodlight.devicemanager.dstDevice"; 
 
+    /**
+     * A FloodlightContextStore object that can be used to interact with the 
+     * FloodlightContext information created by BVS manager.
+     */
+    public static final FloodlightContextStore<IDevice> fcStore = 
+        new FloodlightContextStore<IDevice>();
+    
+    /**
+     * Set the entity classifier for the device manager to use to
+     * differentiate devices on the network.  If no classifier is set,
+     * the {@link DefaultEntityClassifer} will be used.  This should be 
+     * registered in the application initialization phase before startup.
+     * @param classifier the classifier to set.
+     */
+    public void setEntityClassifier(IEntityClassifier classifier);
+    
+    /**
+     * Flush and/or reclassify all entities in a class
+     *
+     * @param entityClass the class to flush.  If null, flush all classes
+     * @param reclassify if true, begin an asynchronous task to reclassify the
+     * flushed entities
+     */
+    public void flushEntityCache(IEntityClass entityClass, boolean reclassify);
+    
+    /**
+     * Search for a device using entity fields.  Only the key fields as
+     * defined by the {@link IEntityClassifier} will be important in this
+     * search.
+     * @param macAddress The MAC address
+     * @param ipv4Address the ipv4 address
+     * @param vlan the VLAN
+     * @param switchDPID the switch DPID
+     * @param switchPort the switch port
+     * @return an {@link IDevice} or null if no device is found.
+     * @see IDeviceManager#setEntityClassifier(IEntityClassifier)
+     */
+    public IDevice findDevice(long macAddress, Integer ipv4Address,
+                              Short vlan, Long switchDPID,
+                              Integer switchPort);
+    
+    /**
+     * Get an unmodifiable collection view over all devices currently known.
+     * @return the collection of all devices
+     */
+    public Collection<? extends IDevice> getAllDevices();
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
index 3af548dd0..d5412f154 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
@@ -17,7 +17,7 @@
 
 package net.floodlightcontroller.devicemanager;
 
-import java.util.Collection;
+import java.util.Set;
 
 import net.floodlightcontroller.devicemanager.IEntityClassifier.EntityField;
 import net.floodlightcontroller.devicemanager.internal.Device;
@@ -30,20 +30,24 @@ import net.floodlightcontroller.devicemanager.internal.Device;
  * for that entity class. A field is effectively wildcarded by not including 
  * it in the list of key fields returned by {@link IEntityClassifier} and/or 
  * {@link IEntityClass}.
+ * 
+ * Note that if you're not using static objects, you'll need to override
+ * {@link Object#equals(Object)} and {@link Object#hashCode()}.
+ * 
  * @author readams
  *
  */
 public interface IEntityClass {
     /**
-     * Return the list of key fields for this entity class.  Entities 
+     * Return the set of key fields for this entity class.  Entities 
      * belonging to this class that differ in fields not included in 
      * this collection will be considered the same device.  The key 
      * fields for an entity class must not change unless associated 
      * with a flush of that entity class.
      * 
-     * @return a collection containing the fields that should not
+     * @return a set containing the fields that should not
      * be wildcarded.  May be null to indicate that all fields are key fields.
      */
-    Collection<EntityField> getKeyFields();
+    Set<EntityField> getKeyFields();
 }
 
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java
index 7b448aa8e..8d4994cba 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java
@@ -18,6 +18,7 @@
 package net.floodlightcontroller.devicemanager;
 
 import java.util.Collection;
+import java.util.Set;
 
 import net.floodlightcontroller.devicemanager.internal.Device;
 import net.floodlightcontroller.devicemanager.internal.Entity;
@@ -36,10 +37,20 @@ public interface IEntityClassifier {
    }
 
    /**
-    * Classify the given entity into a class.
+    * Classify the given entity into a set of classes.  It is important
+    * that the key fields returned by {@link IEntityClassifier#getKeyFields()}
+    * be sufficient for classifying entities.  That is, if two entities are
+    * identical except for a field that is not a key field, they must be
+    * assigned the same class.
+    * 
+    * <p>Note further that you must take steps to ensure you always return
+    * classes in some consistent ordering.  This could be achieved by sorting
+    * the returned collection before returning it.
     * 
     * @param entity the entity to classify
-    * @return the IEntityClass resulting from the classification
+    * @return the IEntityClass resulting from the classification.  When
+    * iterating, must return results in a sorted order.
+    * @see IEntityClassifier#getKeyFields()
     */
    Collection<IEntityClass> classifyEntity(Entity entity);
 
@@ -51,6 +62,9 @@ public interface IEntityClassifier {
     * object if the entity class returned is different from the entity class for
     * curDevice.
     * 
+    * <p>Note that you must take steps to ensure you always return classes
+    * in some consistent ordering.
+
     * @param curDevice the device currently associated with the entity
     * @param entity the entity to reclassify
     * @return the IEntityClass resulting from the classification
@@ -78,11 +92,15 @@ public interface IEntityClassifier {
     * never be considered a different device by any {@link IEntityClass} 
     * returned by {@link IEntityClassifier#classifyEntity}.  The key fields
     * for an entity classifier must not change unless associated with a 
-    * flush of all entity state.
+    * flush of all entity state.  The list of key fields must be the union
+    * of all key fields that could be returned by
+    * {@link IEntityClass#getKeyFields()}.
     * 
-    * @return a collection containing the fields that should not be
+    * @return a set containing the fields that should not be
     * wildcarded.  May be null to indicate that all fields are key fields.
+    * @see {@link IEntityClass#getKeyFields()}
+    * @see {@link IEntityClassifier#classifyEntity}
     */
-   Collection<EntityField> getKeyFields();
+   Set<EntityField> getKeyFields();
 }
 
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java
new file mode 100644
index 000000000..2fc3c703b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java
@@ -0,0 +1,74 @@
+/**
+*    Copyright 2012 Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.floodlightcontroller.devicemanager;
+
+/**
+ * A simple switch DPID/port pair
+ * @author readams
+ *
+ */
+public class SwitchPort {
+    protected long switchDPID;
+    protected int port;
+
+    /**
+     * Simple constructor
+     * @param switchDPID the dpid
+     * @param port the port
+     */
+    public SwitchPort(long switchDPID, int port) {
+        super();
+        this.switchDPID = switchDPID;
+        this.port = port;
+    }
+    
+    // ***************
+    // Getters/Setters
+    // ***************
+
+    public long getSwitchDPID() {
+        return switchDPID;
+    }
+    public int getPort() {
+        return port;
+    }
+
+    // ******
+    // Object
+    // ******
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + port;
+        result = prime * result + (int) (switchDPID ^ (switchDPID >>> 32));
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        SwitchPort other = (SwitchPort) obj;
+        if (port != other.port) return false;
+        if (switchDPID != other.switchDPID) return false;
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
index 8111aad34..8de1d2f5b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
@@ -17,8 +17,9 @@
 
 package net.floodlightcontroller.devicemanager.internal;
 
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 
 import net.floodlightcontroller.devicemanager.IEntityClass;
 import net.floodlightcontroller.devicemanager.IEntityClassifier;
@@ -29,21 +30,27 @@ import net.floodlightcontroller.devicemanager.IEntityClassifier;
  * @author readams
  */
 public class DefaultEntityClassifier implements IEntityClassifier {
-    protected static Collection<EntityField> keyFields;
+    /**
+     * A default fixed entity class
+     */
+    protected static class DefaultEntityClass implements IEntityClass {
+        @Override
+        public Set<EntityField> getKeyFields() {
+            return keyFields;
+        }
+    }
+    
+    protected static Set<EntityField> keyFields;
     static {
-        keyFields = new ArrayList<EntityField>(2);
+        keyFields = new HashSet<EntityField>(2);
         keyFields.add(EntityField.MAC);
         keyFields.add(EntityField.VLAN);
     }
-    protected static IEntityClass entityClass = new IEntityClass() {
-        @Override
-        public Collection<EntityField> getKeyFields() {
-            return keyFields;
-        }
-    };
+    protected static IEntityClass entityClass = new DefaultEntityClass();
+    
     protected static Collection<IEntityClass> entityClasses;
     static {
-        entityClasses = new ArrayList<IEntityClass>(1);
+        entityClasses = new HashSet<IEntityClass>(1);
         entityClasses.add(entityClass);
     }
 
@@ -65,7 +72,7 @@ public class DefaultEntityClassifier implements IEntityClassifier {
     }
 
     @Override
-    public Collection<EntityField> getKeyFields() {
+    public Set<EntityField> getKeyFields() {
         return keyFields;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index 453a827c8..96adb23fa 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -18,40 +18,194 @@
 package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.TreeSet;
+
+import org.openflow.util.HexString;
+
 import net.floodlightcontroller.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.IEntityClass;
+import net.floodlightcontroller.devicemanager.SwitchPort;
 
 /**
  * Concrete implementation of {@link IDevice}
  * @author readams
  */
-public class Device {
+public class Device implements IDevice {
+    protected Long deviceKey;
+    
     protected Entity[] entities;
+    protected IEntityClass[] entityClasses;
+    
+    protected String macAddressString;
+    
+    // ************
+    // Constructors
+    // ************
     
     /**
      * Create a device from a set of entities
-     * @param entities the entities that make up the device
+     * @param the unique identifier for this device object
+     * @param entity the initial entity for the device
+     * @param entityClasses the entity classes associated with the entity
      */
-    public Device(Entity... entities) {
-        this.entities = entities;
+    public Device(Long deviceKey,
+                  Entity entity, 
+                  Collection<IEntityClass> entityClasses) {
+        this.deviceKey = deviceKey;
+        this.entities = new Entity[] {entity};
+        this.macAddressString = 
+                HexString.toHexString(entity.getMacAddress(), 6);
+        this.entityClasses = 
+                entityClasses.toArray(new IEntityClass[entityClasses.size()]);
+        Arrays.sort(this.entities);
     }
 
     /**
      * Create a device consisting of all entities from another device plus
      * the additional entities specified.
      * @param device the old device 
-     * @param entities the new entities to add
+     * @param entity the new entity to add to the device
+     * @param entityClasses the entity classes associated with the entity
      */
-    public Device(Device device, Entity... entities) {
-        this.entities = new Entity[device.entities.length + entities.length];
-        int i = 0;
-        for (; i < device.entities.length; i++) {
+    public Device(Device device,
+                  Entity entity,
+                  Collection<IEntityClass> entityClasses) {
+        this.deviceKey = device.deviceKey;
+        this.macAddressString = device.macAddressString;
+
+        this.entities = new Entity[device.entities.length + 1];
+        for (int i = 0; i < device.entities.length; i++) {
             this.entities[i] = device.entities[i];
         }
-        for (int j = 0; j < entities.length; j++, i++) {
-            this.entities[i] = entities[j];
+        this.entities[this.entities.length-1] = entity;
+        Arrays.sort(this.entities);
+        
+        if (entityClasses != null &&
+            entityClasses.size() > this.entityClasses.length) {
+            IEntityClass[] classes = new IEntityClass[entityClasses.size()];
+            this.entityClasses = 
+                    entityClasses.toArray(classes);
+        } else {
+            // same actual array, not a copy
+            this.entityClasses = device.entityClasses;
+        }
+    }
+
+    // *******
+    // IDevice
+    // *******
+    
+    @Override
+    public Long getDeviceKey() {
+        return deviceKey;
+    }
+    
+    @Override
+    public long getMACAddress() {
+        // we assume only one MAC per device for now.
+        return entities[0].getMacAddress();
+    }
+
+    @Override
+    public String getMACAddressString() {
+        return macAddressString;
+    }
+
+    @Override
+    public Short[] getVlanId() {
+        if (entities.length == 1)
+            return new Short[]{ entities[0].getVlan() };
+
+        TreeSet<Short> vals = new TreeSet<Short>();
+        for (Entity e : entities) {
+            vals.add(e.getVlan());
+        }
+        return vals.toArray(new Short[vals.size()]);
+    }
+
+    @Override
+    public Integer[] getIPv4Addresses() {
+        Integer addr;
+        if (entities.length == 1 &&
+            (addr = entities[0].getIpv4Address()) != null)
+            return new Integer[]{ addr };
+
+        TreeSet<Integer> vals = new TreeSet<Integer>();
+        for (Entity e : entities) {
+            if (e.getIpv4Address() != null)
+                vals.add(e.getIpv4Address());
+        }
+        return vals.toArray(new Integer[vals.size()]);
+    }
+
+    @Override
+    public SwitchPort[] getAttachmentPoints() {
+        if (entities.length == 1)
+            return new SwitchPort[]{new SwitchPort(entities[0].
+                                                   getSwitchDPID(), 
+                                                   entities[0].
+                                                   getSwitchPort()) };
+
+        HashSet<SwitchPort> vals = new HashSet<SwitchPort>();
+        for (Entity e : entities) {
+            SwitchPort sp = new SwitchPort(e.getSwitchDPID(), 
+                                           e.getSwitchPort());
+            vals.add(sp);
         }
+        return vals.toArray(new SwitchPort[vals.size()]);
     }
 
+    @Override
+    public Date getLastSeen() {
+        Date d = entities[0].getLastSeenTimestamp();
+        for (int i = 1; i < entities.length; i++) {
+            if (entities[i].getLastSeenTimestamp().compareTo(d) < 0)
+                d = entities[i].getLastSeenTimestamp();
+        }
+        return d;
+    }
+    
+    // ***************
+    // Getters/Setters
+    // ***************
+
+
+    public IEntityClass[] getEntityClasses() {
+        return entityClasses;
+    }
+
+    public void setEntityClasses(IEntityClass[] entityClasses) {
+        this.entityClasses = entityClasses;
+    }
+
+    public Entity[] getEntities() {
+        return entities;
+    }
+
+    // ***************
+    // Utility Methods
+    // ***************
+    
+    /**
+     * Check whether the device contains the specified entity
+     * @param entity the entity to search for
+     * @return true if the entity is in the device, or false otherwise
+     */
+    public boolean containsEntity(Entity entity) {
+        for (Entity e : entities) {
+            if (e.equals(entity))
+                return true;
+        }
+        return false;
+    }
+    
+    // ******
+    // Object
+    // ******
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index 3ef2adb16..dbc4760c1 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -1,5 +1,5 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011,2012 Big Switch Networks, Inc. 
 *    Originally created by David Erickson, Stanford University
 * 
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -15,21 +15,34 @@
 *    under the License.
 **/
 
-/**
- *
- */
 package net.floodlightcontroller.devicemanager.internal;
 
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Date;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProvider;
 import net.floodlightcontroller.core.IOFMessageListener;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.IOFSwitchListener;
+import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceManager;
 import net.floodlightcontroller.devicemanager.IEntityClass;
+import net.floodlightcontroller.devicemanager.IEntityClassifier;
+import net.floodlightcontroller.devicemanager.IEntityClassifier.EntityField;
+import net.floodlightcontroller.packet.ARP;
+import net.floodlightcontroller.packet.DHCP;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.UDP;
+import net.floodlightcontroller.storage.IStorageSource;
 import net.floodlightcontroller.storage.IStorageSourceListener;
+import net.floodlightcontroller.topology.ITopology;
 import net.floodlightcontroller.topology.ITopologyAware;
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
@@ -50,44 +63,91 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
         LoggerFactory.getLogger(DeviceManagerImpl.class);
 
     protected IFloodlightProvider floodlightProvider;
+    protected ITopology topology;
+    protected IStorageSource storageSource;
     
     /**
      * This is the master device map that maps device IDs to {@link Device}
      * objects.
      */
-    protected Map<Long, Device> deviceMap;
+    protected ConcurrentHashMap<Long, Device> deviceMap;
+    
+    /**
+     * Counter used to generate device keys
+     */
+    protected long deviceKeyCounter = 0;
+    
+    /**
+     * Lock for incrementing the device key counter
+     */
+    protected Object deviceKeyLock = new Object();
     
     /**
      * This is the primary entity index that maps entities to device IDs.
      */
-    protected Map<IndexedEntity, Long> primaryIndex;
+    protected ConcurrentHashMap<IndexedEntity, Long> primaryIndex;
+    
+    /**
+     * The primary key fields used in the primary index
+     */
+    protected Set<EntityField> primaryKeyFields;
     
     /**
      * This map contains secondary indices for each of the configured {@ref IEntityClass} 
      * that exist
      */
-    protected Map<IEntityClass, Map<IndexedEntity, Long>> secondaryIndexMap;
+    protected ConcurrentHashMap<IEntityClass, 
+                                ConcurrentHashMap<IndexedEntity, 
+                                                  Long>> classIndexMap;
 
+    /**
+     * The entity classifier currently in use
+     */
+    IEntityClassifier entityClassifier;
+    
     // **************
     // IDeviceManager
     // **************
+
+    @Override
+    public void setEntityClassifier(IEntityClassifier classifier) {
+        entityClassifier = classifier;
+        primaryKeyFields = classifier.getKeyFields();
+    }
     
-    // None for now
+    @Override
+    public void flushEntityCache(IEntityClass entityClass, 
+                                 boolean reclassify) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public IDevice findDevice(long macAddress, Integer ipv4Address, 
+                              Short vlan, Long switchDPID, 
+                              Integer switchPort) {
+        return findDeviceByEntity(new Entity(macAddress, vlan, 
+                                             ipv4Address, switchDPID, 
+                                             switchPort, null));
+    }
+
+    @Override
+    public Collection<? extends IDevice> getAllDevices() {
+        return Collections.unmodifiableCollection(deviceMap.values());
+    }
     
     // ******************
     // IOFMessageListener
     // ******************
-    
+
+
     @Override
     public String getName() {
-        // TODO Auto-generated method stub
-        return null;
+        return "devicemanager";
     }
 
     @Override
     public int getId() {
-        // TODO Auto-generated method stub
-        return 0;
+        return IOFMessageListener.FlListenerID.DEVICEMANAGERIMPL;
     }
 
     @Override
@@ -101,12 +161,22 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
         // TODO Auto-generated method stub
         return false;
     }
-
+    
     @Override
-    public Command receive(IOFSwitch sw, OFMessage msg,
+    public Command receive(IOFSwitch sw, OFMessage msg, 
                            FloodlightContext cntx) {
-        // TODO Auto-generated method stub
-        return null;
+        switch (msg.getType()) {
+            case PACKET_IN:
+                return this.processPacketInMessage(sw, 
+                                                   (OFPacketIn) msg, cntx);
+            case PORT_STATUS:
+                return this.processPortStatusMessage(sw, 
+                                                     (OFPortStatus) msg);
+        }
+
+        logger.error("received an unexpected message {} from switch {}", 
+                     msg, sw);
+        return Command.CONTINUE;
     }
     
     // **********************
@@ -132,8 +202,7 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
     @Override
     public void addedLink(IOFSwitch srcSw, short srcPort, int srcPortState,
                           IOFSwitch dstSw, short dstPort, int dstPortState) {
-        // TODO Auto-generated method stub
-        
+        // Nothing to do
     }
 
     @Override
@@ -141,7 +210,6 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
                             int srcPortState, IOFSwitch dstSw,
                             short dstPort, int dstPortState) {
         // TODO Auto-generated method stub
-        
     }
 
     @Override
@@ -176,16 +244,52 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
         // TODO Auto-generated method stub
     }
 
+    // **************
+    // Initialization
+    // **************
+
+    public void setFloodlightProvider(IFloodlightProvider floodlightProvider) {
+        this.floodlightProvider = floodlightProvider;
+    }
+
+    public void setStorageSource(IStorageSource storageSource) {
+        this.storageSource = storageSource;
+    }
+
+    public void setTopology(ITopology topology) {
+        this.topology = topology;
+    }
+    
+    public void init() {
+        
+    }
+
+    public void startUp() {
+        if (entityClassifier == null)
+            setEntityClassifier(new DefaultEntityClassifier());
+        
+        deviceMap = new ConcurrentHashMap<Long, Device>();
+        primaryIndex = new ConcurrentHashMap<IndexedEntity, Long>();
+        classIndexMap = 
+                new ConcurrentHashMap<IEntityClass, 
+                                      ConcurrentHashMap<IndexedEntity, 
+                                                        Long>>();
+        
+        floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+        floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
+        
+        // XXX - TODO entity aging timer
+    }
+    
     // ****************
     // Internal methods
     // ****************
     
-    public Command processPortStatusMessage(IOFSwitch sw, OFPortStatus ps) {
-        return null;
-        
+    protected Command processPortStatusMessage(IOFSwitch sw, OFPortStatus ps) {
+        // XXX - TODO
+        return null;        
     }
 
-
     /**
      * This method is called for every packet-in and should be optimized for
      * performance.
@@ -194,194 +298,326 @@ public class DeviceManagerImpl implements IDeviceManager, IOFMessageListener,
      * @param cntx
      * @return
      */
-    public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
-                                          FloodlightContext cntx) {
-        return null;
-        /*
-        Command ret = Command.CONTINUE;
-        OFMatch match = new OFMatch();
-        match.loadFromPacket(pi.getPacketData(), pi.getInPort(), sw.getId());
-        // Add this packet-in to event history
-        evHistPktIn(match);
-
-        // Create attachment point/update network address if required
-        SwitchPortTuple switchPort = new SwitchPortTuple(sw, pi.getInPort());
-        // Don't learn from internal port or invalid port
-        if (topology.isInternal(switchPort) || 
-                !isValidInputPort(switchPort.getPort())) {
-            processUpdates();
+    protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
+                                             FloodlightContext cntx) {
+        try {
+
+            if (topology.isInternal(sw, pi.getInPort()) ||
+                !isValidInputPort(pi.getInPort()))
+                return Command.CONTINUE;
+
+            Ethernet eth = 
+                    IFloodlightProvider.bcStore.
+                    get(cntx,IFloodlightProvider.CONTEXT_PI_PAYLOAD);
+
+            Entity entity = getEntityFromPacket(eth, sw, pi.getInPort());
+            if (entity == null)
+                return Command.CONTINUE;
+            
+            if (isGratArp(eth)) {
+                // XXX - TODO - Clear attachment points from other clusters
+            }
+            
+            Device d = learnDeviceByEntity(entity);
+            
+            fcStore.put(cntx, CONTEXT_SRC_DEVICE, d);
+            
             return Command.CONTINUE;
-        }
 
-        // Packet arrive at a port from where we can learn the device
-        // if the source is multicast/broadcast ignore it
-        if ((match.getDataLayerSource()[0] & 0x1) != 0) {
-            return Command.CONTINUE;
+        } finally {
+            processUpdates();
         }
-
-        Long dlAddr = Ethernet.toLong(match.getDataLayerSource());
-        Short vlan = match.getDataLayerVirtualLan();
-        if (vlan < 0) vlan = null;
-        Ethernet eth = IFloodlightProvider.bcStore.get(
-                                cntx, IFloodlightProvider.CONTEXT_PI_PAYLOAD);
-        int nwSrc = getSrcNwAddr(eth, dlAddr);
-        Device device = devMgrMaps.getDeviceByDataLayerAddr(dlAddr);
-        Date currentDate = new Date(); // TODO,
-        if (device != null) { 
-            // Write lock is expensive, check if we have an update first
-            boolean newAttachmentPoint = false;
-            boolean newNetworkAddress = false;
-            boolean updateAttachmentPointLastSeen = false;
-            boolean updateNetworkAddressLastSeen = false;
-            boolean updateNeworkAddressMap = false;
-            boolean updateDeviceVlan = false;
-            boolean clearAttachmentPoints = false;
-            boolean updateDevice = false;
-
-            DeviceAttachmentPoint attachmentPoint = null;
-            DeviceNetworkAddress networkAddress = null;
-
-            // Copy-replace of device would be too expensive here
-            device.setLastSeen(currentDate);
-            updateDevice = device.shouldWriteLastSeenToStorage();
-            attachmentPoint = device.getAttachmentPoint(switchPort);
-
-            if (isGratArp(eth)) {
-                clearAttachmentPoints = true;
+        
+    }
+    
+    private void processUpdates() {
+        // XXX - TODO
+    }
+    
+    /**
+     * Check whether the port is a physical port. We should not learn 
+     * attachment points on "special" ports.
+     * @param port the port to check
+     * @return
+     */
+    private boolean isValidInputPort(short port) {
+        return ((int)port & 0xff00) != 0xff00 ||
+                     port == (short)0xfffe;
+    }
+    
+    private boolean isGratArp(Ethernet eth) {
+        if (eth.getPayload() instanceof ARP) {
+            ARP arp = (ARP) eth.getPayload();
+            if (arp.isGratuitous()) {
+                return true;
             }
+        }
+        return false;
+    }
 
-            if (attachmentPoint != null) {
-                updateAttachmentPointLastSeen = true;
-            } else {
-                newAttachmentPoint = true;
+    private int getSrcNwAddr(Ethernet eth, long dlAddr) {
+        if (eth.getPayload() instanceof ARP) {
+            ARP arp = (ARP) eth.getPayload();
+            if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
+                (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
+                return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
             }
-
-            if (nwSrc != 0) {
-                networkAddress = device.getNetworkAddress(nwSrc);
-                if (networkAddress != null) {
-                    updateNetworkAddressLastSeen = true;
-                } else if (eth != null && (eth.getPayload() instanceof ARP)) {
-                    networkAddress = new DeviceNetworkAddress(nwSrc, 
-                                                                currentDate);
-                    newNetworkAddress = true;
-                }
-
-                // Also, if this address is currently mapped to a different 
-                // device, fix it. This should be rare, so it is OK to do update
-                // the storage from here.
-                //
-                // NOTE: the mapping is observed, and the decision is made based
-                // on that, outside a lock. So the state may change by the time 
-                // we get to do the map update. But that is OK since the mapping
-                // will eventually get consistent.
-                Device deviceByNwaddr = this.getDeviceByIPv4Address(nwSrc);
-                if ((deviceByNwaddr != null) &&
-                    (deviceByNwaddr.getDataLayerAddressAsLong() != 
-                                    device.getDataLayerAddressAsLong())) {
-                    updateNeworkAddressMap = true;
-                    Device dCopy = new Device(deviceByNwaddr);
-                    DeviceNetworkAddress naOld = dCopy.getNetworkAddress(nwSrc);
-                    Map<Integer, DeviceNetworkAddress> namap = 
-                                                dCopy.getNetworkAddressesMap();
-                    if (namap.containsKey(nwSrc)) namap.remove(nwSrc);
-                    dCopy.setNetworkAddresses(namap.values());
-                    this.devMgrMaps.updateMaps(dCopy);
-                    if (naOld !=null) 
-                                removeNetworkAddressFromStorage(dCopy, naOld);
-                    log.info(
-                     "Network address {} moved from {} to {} due to packet {}",
-                            new Object[] {networkAddress,
-                                          deviceByNwaddr.getDataLayerAddress(),
-                                          device.getDataLayerAddress(),
-                                          eth});
+        } else if (eth.getPayload() instanceof IPv4) {
+            IPv4 ipv4 = (IPv4) eth.getPayload();
+            if (ipv4.getPayload() instanceof UDP) {
+                UDP udp = (UDP)ipv4.getPayload();
+                if (udp.getPayload() instanceof DHCP) {
+                    DHCP dhcp = (DHCP)udp.getPayload();
+                    if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
+                        return ipv4.getSourceAddress();
+                    }
                 }
-
             }
-            if ((vlan == null && device.getVlanId() != null) ||
-                    (vlan != null && !vlan.equals(device.getVlanId()))) {
-                updateDeviceVlan = true;
-            }
-
-            if (newAttachmentPoint || newNetworkAddress || updateDeviceVlan || 
-                                updateNeworkAddressMap) {
-
-                Device nd = new Device(device);
-
-                try {
-                    // Check if we have seen this attachmentPoint recently,
-                    // An exception is thrown if the attachmentPoint is blocked.
-                    if (newAttachmentPoint) {
-                        attachmentPoint = getNewAttachmentPoint(nd, switchPort);
-                        nd.addAttachmentPoint(attachmentPoint);
-                        evHistAttachmtPt(nd, attachmentPoint.getSwitchPort(),
-                                         EvAction.ADDED, "New AP from pkt-in");
-                    }
-
-                    if (clearAttachmentPoints) {
-                        nd.clearAttachmentPoints();
-                        evHistAttachmtPt(nd, 0L, (short)(-1),
-                                EvAction.CLEARED, "Grat. ARP from pkt-in");
-                    }
+        }
+        return 0;
+    }
+    
+    /**
+     * Parse an entity from an {@link Ethernet} packet.
+     * @param eth the packet to parse
+     * @param sw the switch on which the packet arrived
+     * @param pi the original packetin
+     * @return the entity from the packet
+     */
+    private Entity getEntityFromPacket(Ethernet eth, 
+                                       IOFSwitch sw, 
+                                       int port) {
+        byte[] dlAddrArr = eth.getSourceMACAddress();
+        long dlAddr = Ethernet.toLong(dlAddrArr);
+        
+        // Ignore broadcast/multicast source
+        if ((dlAddrArr[0] & 0x1) != 0)
+            return null;
+        
+        short vlan = eth.getVlanID();
+        int nwSrc = getSrcNwAddr(eth, dlAddr);
+        return new Entity(dlAddr,
+                          ((vlan >= 0) ? vlan : null),
+                          ((nwSrc != 0) ? nwSrc : null),
+                          sw.getId(),
+                          port,
+                          new Date());
+    }
 
-                    if (newNetworkAddress) {
-                        // add the address
-                        nd.addNetworkAddress(networkAddress);
-                        log.debug("Device {} added IP {}", 
-                                            nd, IPv4.fromIPv4Address(nwSrc));
+    /**
+     * Look up a {@link Device} based on the provided {@link Entity}.
+     * @param entity the entity to search for
+     * @return The {@link Device} object if found
+     */
+    protected Device findDeviceByEntity(Entity entity) {
+        IndexedEntity ie = new IndexedEntity(primaryKeyFields, entity);
+        Long deviceKey = primaryIndex.get(ie);
+        if (deviceKey == null)
+            return null;
+        return deviceMap.get(deviceKey);
+    }
+    
+    /**
+     * Look up a {@link Device} based on the provided {@link Entity}.  Also learns
+     * based on the new entity, and will update existing devices as required. 
+     * 
+     * @param entity the {@link Entity}
+     * @return The {@link Device} object if found
+     */
+    protected Device learnDeviceByEntity(Entity entity) {
+        IndexedEntity ie = new IndexedEntity(primaryKeyFields, entity);
+        ArrayList<Long> deleteQueue = null;
+        Device device = null;
+        
+        // we may need to restart the learning process if we detect
+        // concurrent modification.  Note that we ensure that at least
+        // one thread should always succeed so we don't get into infinite
+        // starvation loops
+        while (true) {
+            // Look up the fully-qualified entity to see if it already
+            // exists in the primary entity index.
+            Long deviceKey = primaryIndex.get(ie);
+            Collection<IEntityClass> classes = null;
+            if (deviceKey == null) {
+                // If the entity does not exist in the primary entity index, 
+                // use the entity classifier for find the classes for the 
+                // entity. Look up the entity in each of the returned classes'
+                // class entity indexes.
+                classes = entityClassifier.classifyEntity(entity);
+                for (IEntityClass clazz : classes) {
+                    // ensure that a class index exists for the class if
+                    // needed
+                    ConcurrentHashMap<IndexedEntity, Long> classIndex;
+                    try {
+                        classIndex = getClassIndex(clazz);
+                    } catch (ConcurrentModificationException e) {
+                        continue;
                     }
-
-                    if (updateDeviceVlan) {
-                        nd.setVlanId(vlan);
-                        writeDeviceToStorage(nd, currentDate);
+                        
+                    if (classIndex != null) {
+                        IndexedEntity sie = 
+                                new IndexedEntity(clazz.getKeyFields(), 
+                                                  entity);
+                        deviceKey = classIndex.get(sie);
                     }
-
-                } catch (APBlockedException e) {
-                    assert(attachmentPoint == null);
-                    ret = Command.STOP;
-                    // install drop flow to avoid overloading the controller
-                    // set hard timeout to 5 seconds to avoid blocking host 
-                    // forever
-                    ForwardingBase.blockHost(floodlightProvider, switchPort,
-                        dlAddr, ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT);
                 }
-                // Update the maps
-                devMgrMaps.updateMaps(nd);
-                // publish the update after devMgrMaps is updated.
-                if (newNetworkAddress) {
-                    updateAddress(nd, networkAddress, true);
+            }
+            if (deviceKey != null) {
+                // If the primary or secondary index contains the entity, 
+                // update the entity timestamp, then use resulting device 
+                // key to look up the device in the device map, and
+                // use the referenced Device below.
+                entity.setLastSeenTimestamp(new Date());
+                device = deviceMap.get(deviceKey);
+                if (device == null)
+                    continue;
+            } else {
+                // If the secondary index does not contain the entity, 
+                // create a new Device object containing the entity, and 
+                // generate a new device ID
+                synchronized (deviceKeyLock) {
+                    deviceKey = Long.valueOf(deviceKeyCounter++);
                 }
-                if (updateDeviceVlan) {
-                    updateVlan(nd);
+                device = new Device(deviceKey, entity, classes);
+                
+                // Add the new device to the primary map with a simple put
+                deviceMap.put(deviceKey, device);
+                
+                if (!updateIndices(device, deviceKey)) {
+                    if (deleteQueue == null)
+                        deleteQueue = new ArrayList<Long>();
+                    deleteQueue.add(deviceKey);
+                    continue;
                 }
-                device = nd;
             }
-
-            if (updateAttachmentPointLastSeen) {
-                attachmentPoint.setLastSeen(currentDate);
-                if (attachmentPoint.shouldWriteLastSeenToStorage())
-                    writeAttachmentPointToStorage(
-                            device, attachmentPoint, currentDate);
+            
+            if (device.containsEntity(entity)) {
+                break;
+            } else {
+                Device newDevice = new Device(device, entity, classes);
+                // XXX - TODO When adding an entity, any existing entities on the
+                // same OpenFlow switch cluster but a different attachment point
+                // should be removed. If an entity being removed contains an 
+                // IP address but the new entity does not contain that IP, 
+                // then a new entity should be added containing the IP 
+                // (including updating the entity caches), preserving the old 
+                // timestamp of the entity.
+                
+                // XXX - TODO Handle port channels
+                
+                // XXX - TODO Handle broadcast domains
+                
+                // XXX - TODO Prevent flapping of entities
+                
+                boolean res = deviceMap.replace(deviceKey, device, newDevice);
+                // If replace returns false, restart the process from the 
+                // beginning (this implies another thread concurrently 
+                // modified this Device).
+                if (!res)
+                    continue;
+                
+                device = newDevice;
+                
+                if (!updateIndices(device, deviceKey)) {
+                    continue;
+                }
             }
-
-            if (updateNetworkAddressLastSeen || newNetworkAddress) {
-                if (updateNetworkAddressLastSeen)
-                    networkAddress.setLastSeen(currentDate);
-                if (newNetworkAddress || 
-                        networkAddress.shouldWriteLastSeenToStorage())
-                    writeNetworkAddressToStorage(
-                            device, networkAddress, currentDate);
+        }
+        
+        if (deleteQueue != null) {
+            for (Long l : deleteQueue) {
+                deviceMap.remove(l);
             }
-
-            if (updateDevice) {
-                writeDeviceToStorage(device, currentDate);
+        }
+        return device;
+    }
+    
+    /**
+     * Get the secondary index for a class.  Will return null if the 
+     * secondary index was created concurrently in another thread. 
+     * @param clazz the class for the index
+     * @return
+     */
+    private ConcurrentHashMap<IndexedEntity, 
+                              Long> getClassIndex(IEntityClass clazz) 
+                                      throws ConcurrentModificationException {
+        ConcurrentHashMap<IndexedEntity, Long> classIndex =
+                classIndexMap.get(clazz);
+        if (classIndex != null) return classIndex;
+        
+        if (primaryKeyFields.equals(clazz.getKeyFields())) {
+            return null;
+        }
+        
+        classIndex = 
+                new ConcurrentHashMap<IndexedEntity, Long>();
+        ConcurrentHashMap<IndexedEntity, Long> r = 
+                classIndexMap.putIfAbsent(clazz, 
+                                          classIndex);
+        if (r != null) {
+            // concurrent add; restart
+            throw new ConcurrentModificationException();
+        }
+        return classIndex;
+    }
+    
+    /**
+     * Update both the primary and class indices for the provided device.
+     * If the update fails because of a concurrent update, will return false.
+     * @param device the device to update
+     * @param deviceKey the device key for the device
+     * @return true if the update succeeded, false otherwise.
+     */
+    private boolean updateIndices(Device device, Long deviceKey) {
+        if (!updateIndex(device, deviceKey, 
+                         primaryIndex, primaryKeyFields)) {
+            return false;
+        }
+        for (IEntityClass clazz : device.getEntityClasses()) {
+            Set<EntityField> ef = clazz.getKeyFields();
+            if (primaryKeyFields.equals(ef))
+                continue;
+            ConcurrentHashMap<IndexedEntity, Long> classIndex;
+            try {
+                classIndex = getClassIndex(clazz); 
+            } catch (ConcurrentModificationException e) {
+                return false;
             }
-
-        } else { // device is null 
-            handleNewDevice(match.getDataLayerSource(), currentDate,
-                    switchPort, nwSrc, vlan);
+            if (classIndex != null &&
+                !updateIndex(device, deviceKey, classIndex, ef))
+                return false;
         }
-        processUpdates();
-        return ret;
-        */
+        // XXX - TODO handle indexed views into data
+        return true;
+    }                     
+    
+    /**
+     * Attempt to update an index with the entities in the provided
+     * {@link Device}.  If the update fails because of a concurrent update,
+     * will return false.
+     * @param device the device to update
+     * @param deviceKey the device key for the device
+     * @param index the index to update
+     * @param keyFields the key fields to use for the index
+     * @return true if the update succeeded, false otherwise.
+     */
+    private boolean updateIndex(Device device, 
+                                Long deviceKey,
+                                ConcurrentHashMap<IndexedEntity, 
+                                                  Long> index, 
+                                Set<EntityField> keyFields) {
+        for (Entity e : device.entities) {
+            IndexedEntity ie = new IndexedEntity(keyFields, e);
+            Long ret = index.putIfAbsent(ie, deviceKey);
+            if (ret != null) {
+                // If the return value is non-null, then fail the insert 
+                // (this implies that a device using this entity has 
+                // already been created in another thread).
+                return false;
+            }
+        }
+        
+        return true;
     }
+    
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
index cf18ec579..136faba86 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
@@ -19,7 +19,7 @@ package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.Date;
 
-import net.floodlightcontroller.util.MACAddress;
+import org.openflow.util.HexString;
 
 /**
  * An entity on the network is a visible trace of a device that corresponds
@@ -35,11 +35,11 @@ import net.floodlightcontroller.util.MACAddress;
  * @author readams
  *
  */
-public class Entity {
+public class Entity implements Comparable<Entity> {
      /**
      * The MAC address associated with this entity
      */
-    protected MACAddress macAddress;
+    protected long macAddress;
     
     /**
      * The IP address associated with this entity, or null if no IP learned
@@ -77,14 +77,14 @@ public class Entity {
      * Create a new entity
      * 
      * @param macAddress
-     * @param ipv4Address
      * @param vlan
+     * @param ipv4Address
      * @param switchDPID
      * @param switchPort
      * @param lastSeenTimestamp
      */
-    public Entity(MACAddress macAddress, Integer ipv4Address, 
-                  Short vlan, Long switchDPID, Integer switchPort, 
+    public Entity(long macAddress, Short vlan, 
+                  Integer ipv4Address, Long switchDPID, Integer switchPort, 
                   Date lastSeenTimestamp) {
         super();
         this.macAddress = macAddress;
@@ -99,7 +99,7 @@ public class Entity {
     // Getters/Setters
     // ***************
 
-    public MACAddress getMacAddress() {
+    public long getMacAddress() {
         return macAddress;
     }
 
@@ -133,8 +133,7 @@ public class Entity {
         int result = 1;
         result = prime * result
                  + ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
-        result = prime * result
-                 + ((macAddress == null) ? 0 : macAddress.hashCode());
+        result = prime * result + (int) (macAddress ^ (macAddress >>> 32));
         result = prime * result
                  + ((switchDPID == null) ? 0 : switchDPID.hashCode());
         result = prime * result
@@ -152,9 +151,7 @@ public class Entity {
         if (ipv4Address == null) {
             if (other.ipv4Address != null) return false;
         } else if (!ipv4Address.equals(other.ipv4Address)) return false;
-        if (macAddress == null) {
-            if (other.macAddress != null) return false;
-        } else if (!macAddress.equals(other.macAddress)) return false;
+        if (macAddress != other.macAddress) return false;
         if (switchDPID == null) {
             if (other.switchDPID != null) return false;
         } else if (!switchDPID.equals(other.switchDPID)) return false;
@@ -169,9 +166,49 @@ public class Entity {
 
     @Override
     public String toString() {
-        return "Entity [macAddress=" + macAddress + ", ipv4Address="
+        return "Entity [macAddress=" + HexString.toHexString(macAddress)
+               + ", ipv4Address="
                + ipv4Address + ", vlan=" + vlan + ", switchDPID="
                + switchDPID + ", switchPort=" + switchPort + "]";
     }
+
+    @Override
+    public int compareTo(Entity o) {
+        if (macAddress < o.macAddress) return -1;
+        if (macAddress > o.macAddress) return 1;
+
+        int r;
+        if (ipv4Address == null)
+            r = o.ipv4Address == null ? -1 : 0;
+        else if (o.ipv4Address == null)
+            r = 1;
+        else
+            r = ipv4Address.compareTo(o.ipv4Address);
+        if (r != 0) return r;
+
+        if (vlan == null)
+            r = o.vlan == null ? -1 : 0;
+        else if (o.vlan == null)
+            r = 1;
+        else
+            r = vlan.compareTo(o.vlan);
+        if (r != 0) return r;
+
+        if (switchDPID == null)
+            r = o.switchDPID == null ? -1 : 0;
+        else if (o.switchDPID == null)
+            r = 1;
+        else
+            r = switchDPID.compareTo(o.switchDPID);
+        if (r != 0) return r;
+
+        if (switchPort == null)
+            r = o.switchPort == null ? -1 : 0;
+        else if (o.switchPort == null)
+            r = 1;
+        else
+            r = switchPort.compareTo(o.switchPort);
+        return r;
+    }
     
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
index 4b301c447..d9b2fa29e 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
@@ -37,29 +37,32 @@ public class IndexedEntity {
             switch (f) {
                 case MAC:
                     hashCode = prime * hashCode
-                        + ((entity.macAddress == null) 
-                            ? 0 
-                            : entity.macAddress.hashCode());
+                        + (int)(entity.macAddress);
+                    break;
                 case IP:
                     hashCode = prime * hashCode
                         + ((entity.ipv4Address == null) 
                             ? 0 
                             : entity.ipv4Address.hashCode());
+                    break;
                 case SWITCH:
                     hashCode = prime * hashCode
                         + ((entity.switchDPID == null) 
                             ? 0 
                             : entity.switchDPID.hashCode());
+                    break;
                 case PORT:
                     hashCode = prime * hashCode
                         + ((entity.switchPort == null) 
                             ? 0 
                             : entity.switchPort.hashCode());
+                    break;
                 case VLAN:
                     hashCode = prime * hashCode 
                         + ((entity.vlan == null) 
                             ? 0 
                             : entity.vlan.hashCode());
+                    break;
             }
         }
         return hashCode;
@@ -75,30 +78,33 @@ public class IndexedEntity {
         for (EntityField f : keyFields) {
             switch (f) {
                 case MAC:
-                    if (entity.macAddress == null) {
-                        if (other.entity.macAddress != null) return false;
-                    } else if (!entity.macAddress.
-                            equals(other.entity.macAddress)) return false;
+                    if (entity.macAddress != other.entity.macAddress)
+                        return false;
+                    break;
                 case IP:
                     if (entity.ipv4Address == null) {
                         if (other.entity.ipv4Address != null) return false;
                     } else if (!entity.ipv4Address.
                             equals(other.entity.ipv4Address)) return false;
+                    break;
                 case SWITCH:
                     if (entity.switchDPID == null) {
                         if (other.entity.switchDPID != null) return false;
                     } else if (!entity.switchDPID.
                             equals(other.entity.switchDPID)) return false;
+                    break;
                 case PORT:
                     if (entity.switchPort == null) {
                         if (other.entity.switchPort != null) return false;
                     } else if (!entity.switchPort.
                             equals(other.entity.switchPort)) return false;
+                    break;
                 case VLAN:
                     if (entity.vlan == null) {
                         if (other.entity.vlan != null) return false;
                     } else if (!entity.vlan.
                             equals(other.entity.vlan)) return false;
+                    break;
             }
         }
         
diff --git a/src/main/java/net/floodlightcontroller/topology/ITopology.java b/src/main/java/net/floodlightcontroller/topology/ITopology.java
index fcd99d0ca..0816124a0 100644
--- a/src/main/java/net/floodlightcontroller/topology/ITopology.java
+++ b/src/main/java/net/floodlightcontroller/topology/ITopology.java
@@ -34,13 +34,23 @@ import net.floodlightcontroller.core.IOFSwitch;
  */
 public interface ITopology {
     /**
-     * Query to determine if the specified switch id and port tuple are
+     * Query to determine if the specified switch id and port are
      * connected to another switch or not.  If so, this means the link
      * is passing LLDPs properly between two OpenFlow switches.
-     * @param idPort
-     * @return
+     * @param idPort the {@link SwitchPortTuple} to check
+     * @return true if the link is internal
      */
     public boolean isInternal(SwitchPortTuple idPort);
+    
+    /**
+     * Query to determine if the specified switch id and port are
+     * connected to another switch or not.  If so, this means the link
+     * is passing LLDPs properly between two OpenFlow switches.
+     * @param sw the switch to check
+     * @param port the port to check
+     * @return true if the link is internal
+     */
+    public boolean isInternal(IOFSwitch sw, short port);
 
     /**
      * Get the link that either sources from the port or terminates
diff --git a/src/main/java/net/floodlightcontroller/topology/internal/TopologyImpl.java b/src/main/java/net/floodlightcontroller/topology/internal/TopologyImpl.java
index e079b1e94..b3ef04319 100644
--- a/src/main/java/net/floodlightcontroller/topology/internal/TopologyImpl.java
+++ b/src/main/java/net/floodlightcontroller/topology/internal/TopologyImpl.java
@@ -1137,14 +1137,12 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
     public void setFloodlightProvider(IFloodlightProvider floodlightProvider) {
         this.floodlightProvider = floodlightProvider;
     }
-
-    /**
-     * Checks to see if a SwitchPortTuple is internal. A SwitchPortTuple is
-     * defined as internal if the switch is a core switch if only switches that
-     * are in the same SwitchCluster are connected to it.
-     * @param idPort The SwitchPortTuple to check
-     * @return True if it is internal, false otherwise
-     */
+    
+    @Override
+    public boolean isInternal(IOFSwitch sw, short port) {
+        return isInternal(new SwitchPortTuple(sw, port));
+    }
+    
     @Override
     public boolean isInternal(SwitchPortTuple idPort) {
         lock.readLock().lock();
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index f80f110d5..4ba9dade8 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -11,7 +11,7 @@ public class MACAddress {
     public static final int MAC_ADDRESS_LENGTH = 6;
     private byte[] address = new byte[MAC_ADDRESS_LENGTH];
 
-    private MACAddress(byte[] address) {
+    public MACAddress(byte[] address) {
         this.address = Arrays.copyOf(address, MAC_ADDRESS_LENGTH);
     }
 
diff --git a/src/main/java/org/openflow/util/HexString.java b/src/main/java/org/openflow/util/HexString.java
index c39ed5f12..3a2f30fa9 100644
--- a/src/main/java/org/openflow/util/HexString.java
+++ b/src/main/java/org/openflow/util/HexString.java
@@ -38,12 +38,12 @@ public class HexString {
         return ret;
     }
     
-    public static String toHexString(long val) {
+    public static String toHexString(long val, int padTo) {
         char arr[] = Long.toHexString(val).toCharArray();
         String ret = "";
         // prepend the right number of leading zeros
         int i = 0;
-        for (; i < (16 - arr.length); i++) {
+        for (; i < (padTo * 2 - arr.length); i++) {
             ret += "0";
             if ((i % 2) == 1)
                 ret += ":";
@@ -53,7 +53,11 @@ public class HexString {
             if ((((i + j) % 2) == 1) && (j < (arr.length - 1)))
                 ret += ":";
         }
-        return ret;
+        return ret;        
+    }
+   
+    public static String toHexString(long val) {
+        return toHexString(val, 8);
     }
     
     
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index c9274ae08..4e559b775 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -17,9 +17,13 @@
 
 package net.floodlightcontroller.devicemanager.internal;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
@@ -31,6 +35,10 @@ import static org.easymock.EasyMock.verify;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.test.MockFloodlightProvider;
 import net.floodlightcontroller.devicemanager.DeviceAttachmentPoint;
+import net.floodlightcontroller.devicemanager.IEntityClass;
+import net.floodlightcontroller.devicemanager.IEntityClassifier;
+import net.floodlightcontroller.devicemanager.IEntityClassifier.EntityField;
+import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
@@ -41,6 +49,7 @@ import net.floodlightcontroller.test.FloodlightTestCase;
 import net.floodlightcontroller.topology.ITopology;
 import net.floodlightcontroller.topology.SwitchPortTuple;
 
+import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.openflow.protocol.OFPacketIn;
@@ -53,12 +62,9 @@ import org.openflow.protocol.OFPhysicalPort;
  * @author David Erickson (daviderickson@cs.stanford.edu)
  */
 public class DeviceManagerImplTest extends FloodlightTestCase {
-    protected OFPacketIn packetIn, anotherPacketIn;
-    protected IPacket testPacket, anotherTestPacket;
-    protected byte[] testPacketSerialized, anotherTestPacketSerialized;
-    private IPacket thirdTestPacket;
-    private byte[] thirdTestPacketSerialized;
-    private OFPacketIn thirdPacketIn;
+    private OFPacketIn packetIn;
+    private IPacket testPacket;
+    private byte[] testPacketSerialized;
     MockFloodlightProvider mockFloodlightProvider;
     DeviceManagerImpl deviceManager;
     IStorageSource storageSource;
@@ -93,41 +99,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                     .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
         this.testPacketSerialized = testPacket.serialize();
         
-        // Another test packet with a different source IP
-        this.anotherTestPacket = new Ethernet()
-            .setSourceMACAddress("00:44:33:22:11:01")
-            .setDestinationMACAddress("00:11:22:33:44:55")
-            .setEtherType(Ethernet.TYPE_ARP)
-            .setPayload(
-                    new ARP()
-                    .setHardwareType(ARP.HW_TYPE_ETHERNET)
-                    .setProtocolType(ARP.PROTO_TYPE_IP)
-                    .setHardwareAddressLength((byte) 6)
-                    .setProtocolAddressLength((byte) 4)
-                    .setOpCode(ARP.OP_REPLY)
-                    .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-                    .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-                    .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                    .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
-        this.anotherTestPacketSerialized = anotherTestPacket.serialize();
-        
-        this.thirdTestPacket = new Ethernet()
-        .setSourceMACAddress("00:44:33:22:11:01")
-        .setDestinationMACAddress("00:11:22:33:44:55")
-        .setEtherType(Ethernet.TYPE_ARP)
-        .setPayload(
-                new ARP()
-                .setHardwareType(ARP.HW_TYPE_ETHERNET)
-                .setProtocolType(ARP.PROTO_TYPE_IP)
-                .setHardwareAddressLength((byte) 6)
-                .setProtocolAddressLength((byte) 4)
-                .setOpCode(ARP.OP_REPLY)
-                .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-                .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.3"))
-                .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
-        this.thirdTestPacketSerialized = thirdTestPacket.serialize();
-        
         // Build the PacketIn
         this.packetIn = ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
             .setBufferId(-1)
@@ -135,27 +106,116 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
             .setPacketData(this.testPacketSerialized)
             .setReason(OFPacketInReason.NO_MATCH)
             .setTotalLength((short) this.testPacketSerialized.length);
-        
-        // Build the PacketIn
-        this.anotherPacketIn = ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
-            .setBufferId(-1)
-            .setInPort((short) 1)
-            .setPacketData(this.anotherTestPacketSerialized)
-            .setReason(OFPacketInReason.NO_MATCH)
-            .setTotalLength((short) this.anotherTestPacketSerialized.length);
-        
-        // Build the PacketIn
-        this.thirdPacketIn = ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_IN))
-            .setBufferId(-1)
-            .setInPort((short) 1)
-            .setPacketData(this.thirdTestPacketSerialized)
-            .setReason(OFPacketInReason.NO_MATCH)
-            .setTotalLength((short) this.thirdTestPacketSerialized.length);
     }
+    
+    static HashSet<EntityField> testKeyFields;
+    static {
+        testKeyFields = new HashSet<EntityField>();
+        testKeyFields.add(EntityField.MAC);
+        testKeyFields.add(EntityField.VLAN);
+        testKeyFields.add(EntityField.SWITCH);
+        testKeyFields.add(EntityField.PORT);
+    }
+    
+    public static class TestEntityClass implements IEntityClass {
+        @Override
+        public Set<EntityField> getKeyFields() {
+            return testKeyFields;
+        }
+    }
+
+    protected static IEntityClass testEC = new TestEntityClass();
+    
+    public static class TestEntityClassifier extends DefaultEntityClassifier {
+        
+        @Override
+        public Collection<IEntityClass> classifyEntity(Entity entity) {
+            if (entity.switchDPID >= 10L) {
+                ArrayList<IEntityClass> l = new ArrayList<IEntityClass>();
+                l.add(testEC);
+                return l;
+            }
+            return DefaultEntityClassifier.entityClasses;
+        }
 
+        @Override
+        public Set<EntityField> getKeyFields() {
+            return testKeyFields;
+        }
+        
+    }
+    
+    @Test
+    public void testEntityLearning() throws Exception {
+        deviceManager.setEntityClassifier(new TestEntityClassifier());
+        
+        Entity entity1 = new Entity(1L, null, null, 1L, 1, new Date());
+        Entity entity2 = new Entity(1L, null, null, 10L, 1, new Date());
+        Entity entity3 = new Entity(1L, null, 1, 10L, 1, new Date());
+        Entity entity4 = new Entity(1L, null, 1, 1L, 1, new Date());
+        Entity entity5 = new Entity(2L, (short)4, 1, 5L, 2, new Date());
+        Entity entity6 = new Entity(2L, (short)4, 1, 50L, 3, new Date());
+
+        Device d1 = deviceManager.learnDeviceByEntity(entity1);        
+        assertSame(d1, deviceManager.learnDeviceByEntity(entity1)); 
+        assertSame(d1, deviceManager.findDeviceByEntity(entity1));
+        assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass }, 
+                          d1.entityClasses);
+
+        assertEquals(1, deviceManager.getAllDevices().size());
+        
+        Device d2 = deviceManager.learnDeviceByEntity(entity2);
+        assertFalse(d1.equals(d2));
+        assertNotSame(d1, d2);
+        assertArrayEquals(new IEntityClass[]{ testEC }, 
+                          d2.entityClasses);
+
+        assertEquals(2, deviceManager.getAllDevices().size());
+        
+        Device d3 = deviceManager.learnDeviceByEntity(entity3);
+        assertNotSame(d2, d3);
+        assertArrayEquals(new IEntityClass[]{ testEC }, 
+                          d3.entityClasses);
+        assertArrayEquals(new Integer[] { 1 },
+                          d3.getIPv4Addresses());
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) },
+                          d3.getAttachmentPoints());
+
+        assertEquals(2, deviceManager.getAllDevices().size());
+        
+
+        Device d4 = deviceManager.learnDeviceByEntity(entity4);
+        assertNotSame(d1, d4);
+        assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass }, 
+                          d4.entityClasses);
+        assertArrayEquals(new Integer[] { 1 },
+                          d4.getIPv4Addresses());
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
+                          d4.getAttachmentPoints());
+        
+        assertEquals(2, deviceManager.getAllDevices().size());
+
+        Device d5 = deviceManager.learnDeviceByEntity(entity5);
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 2) },
+                          d5.getAttachmentPoints());
+        assertArrayEquals(new Short[] { (short) 4 },
+                          d5.getVlanId());
+        assertEquals(2L, d5.getMACAddress());
+        assertEquals("00:00:00:00:00:02", d5.getMACAddressString());
+        
+        Device d6 = deviceManager.learnDeviceByEntity(entity6);
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(50L, 3) },
+                          d6.getAttachmentPoints());
+        assertArrayEquals(new Short[] { (short) 4 },
+                          d6.getVlanId());
+
+        assertEquals(4, deviceManager.getAllDevices().size());
+    }
     
     @Test
     public void testAddHostAttachmentPoint() throws Exception {
+        fail();
+        /*
         IOFSwitch mockSwitch = createMock(IOFSwitch.class);
         Device d = new Device(((Ethernet)this.testPacket).getSourceMACAddress());
         Date cDate = new Date();
@@ -169,6 +229,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertEquals(dap1, d.getAttachmentPoint(spt1));
         assertEquals(dap2, d.getAttachmentPoint(spt2));
         assertEquals((int)2, d.getAttachmentPoints().size());
+        */
     }
     
     @Test
@@ -181,17 +242,21 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         expect(mockSwitch.getId()).andReturn(1L).anyTimes();
         expect(mockSwitch.getStringId()).andReturn("00:00:00:00:00:00:00:01").anyTimes();
         ITopology mockTopology = createMock(ITopology.class);
-        expect(mockTopology.isInternal(new SwitchPortTuple(mockSwitch, 1))).andReturn(false);
+        expect(mockTopology.isInternal(mockSwitch, (short)1)).andReturn(false).anyTimes();
         deviceManager.setTopology(mockTopology);
 
         Date currentDate = new Date();
         
         // build our expected Device
-        Device device = new Device();
-        device.setDataLayerAddress(dataLayerSource);
-        device.addAttachmentPoint(new SwitchPortTuple(mockSwitch, (short)1), currentDate);
-        Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
-        device.addNetworkAddress(ipaddr, currentDate);
+        Device device = 
+                new Device(new Long(deviceManager.deviceKeyCounter),
+                           new Entity(Ethernet.toLong(dataLayerSource),
+                                      (short)5,
+                                      IPv4.toIPv4Address("192.168.1.1"),
+                                      1L,
+                                      1,
+                                      currentDate),
+                           DefaultEntityClassifier.entityClasses);
 
         // Start recording the replay on the mocks
         replay(mockSwitch, mockTopology);
@@ -202,9 +267,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         verify(mockSwitch, mockTopology);
 
         // Verify the device
-        Device rdevice = deviceManager.getDeviceByDataLayerAddress(dataLayerSource);
+        Device rdevice = (Device)
+                deviceManager.findDevice(Ethernet.toLong(dataLayerSource), null,
+                                         (short)5, null, null);
         assertEquals(device, rdevice);
-        assertEquals(new Short((short)5), rdevice.getVlanId());
+        assertEquals(new Short((short)5), rdevice.getVlanId()[0]);
+        /*
         assertEquals(device, deviceManager.getDeviceByIPv4Address(ipaddr));
 
         // move the port on this device
@@ -232,86 +300,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         
         // Reset the device cache
         deviceManager.clearAllDeviceStateFromMemory();
+        */
+        fail();
     }
     
-    @Test
-    public void testDeviceRecoverFromStorage() throws Exception {
-        byte[] dataLayerSource = ((Ethernet)this.anotherTestPacket).getSourceMACAddress();
-
-        // Mock up our expected behavior
-        IOFSwitch mockSwitch = createMock(IOFSwitch.class);
-        ITopology mockTopology = createNiceMock(ITopology.class);
-        
-        expect(mockSwitch.getId()).andReturn(1L).anyTimes();
-        expect(mockSwitch.getStringId()).andReturn("00:00:00:00:00:00:00:01").anyTimes();
-        expect(mockTopology.isInternal(new SwitchPortTuple(mockSwitch, 1))).andReturn(false);
-        deviceManager.setTopology(mockTopology);
-
-        // Start recording the replay on the mocks
-        replay(mockSwitch, mockTopology);
-        
-        // Add the switch so the list isn't empty
-        mockFloodlightProvider.getSwitches().put(mockSwitch.getId(), mockSwitch);
-
-        // build our expected Device
-        Device device = new Device();
-        Date currentDate = new Date();
-        Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
-        Integer ipaddr2 = IPv4.toIPv4Address("192.168.1.3");
-        device.setDataLayerAddress(dataLayerSource);
-        SwitchPortTuple spt = new SwitchPortTuple(mockSwitch, (short)1);
-        DeviceAttachmentPoint dap = new DeviceAttachmentPoint(spt, currentDate);
-        device.addAttachmentPoint(dap);
-        device.addNetworkAddress(ipaddr, currentDate);
-        device.addNetworkAddress(ipaddr2, currentDate);
-        
-        // Get the listener and trigger the packet ins
-        mockFloodlightProvider.dispatchMessage(mockSwitch, this.anotherPacketIn);
-        mockFloodlightProvider.dispatchMessage(mockSwitch, this.thirdPacketIn);
-
-        // Verify the device
-        assertEquals(device, deviceManager.getDeviceByDataLayerAddress(dataLayerSource));
-        assertEquals(device, deviceManager.getDeviceByIPv4Address(ipaddr));
-        assertEquals(device, deviceManager.getDeviceByIPv4Address(ipaddr2));
-        assertEquals(dap, device.getAttachmentPoint(spt));
-        
-        // Reset the device cache
-        deviceManager.clearAllDeviceStateFromMemory();
-        
-        // Verify the device
-        assertNull(deviceManager.getDeviceByDataLayerAddress(dataLayerSource));
-        assertNull(deviceManager.getDeviceByIPv4Address(ipaddr));
-        assertNull(deviceManager.getDeviceByIPv4Address(ipaddr2));
-        
-        // Load the device cache from storage
-        deviceManager.readAllDeviceStateFromStorage();
-        
-        // Verify the device
-        Device device2 = deviceManager.getDeviceByDataLayerAddress(dataLayerSource);
-        assertEquals(device, device2);
-        assertEquals(dap, device2.getAttachmentPoint(spt));
-        
-        deviceManager.clearAllDeviceStateFromMemory();
-        mockFloodlightProvider.setSwitches(new HashMap<Long,IOFSwitch>());
-        deviceManager.removedSwitch(mockSwitch);
-        deviceManager.readAllDeviceStateFromStorage();
-        
-        device2 = deviceManager.getDeviceByDataLayerAddress(dataLayerSource);
-        assertEquals(device, device2);
-
-        assertNull(device2.getAttachmentPoint(spt));
-        // The following two asserts seems to be incorrect, need to
-        // replace NULL check with the correct value TODO
-        //assertNull(deviceManager.getDeviceByIPv4Address(ipaddr));
-        //assertNull(deviceManager.getDeviceByIPv4Address(ipaddr2));
-        deviceManager.addedSwitch(mockSwitch);
-        assertEquals(dap, device.getAttachmentPoint(spt));
-        assertEquals(device, deviceManager.getDeviceByIPv4Address(ipaddr));
-        assertEquals(device, deviceManager.getDeviceByIPv4Address(ipaddr2));
-    }
     
     @Test
     public void testDeviceUpdateLastSeenToStorage() throws Exception {
+        fail();
+        /*
         deviceManager.clearAllDeviceStateFromMemory();
         
         MockFloodlightProvider mockFloodlightProvider = getMockFloodlightProvider();
@@ -352,10 +349,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         // Make sure the last seen is after our date
         device = deviceManager.getDeviceByDataLayerAddress(dataLayerSource);
         assertTrue(device.getLastSeen().after(currentDate));
+        */
     }
     
     @Test
     public void testAttachmentPointFlapping() throws Exception {
+        fail();
+        /*
     	OFPhysicalPort port1 = new OFPhysicalPort();
     	OFPhysicalPort port2 = new OFPhysicalPort();
         port1.setName("port1");
@@ -401,8 +401,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         
         // Reset the device cache
         deviceManager.clearAllDeviceStateFromMemory();
+        */
     }
-    
+    /*
     private static final Map<String, Object> pcPort1;
     static {
     	pcPort1 = new HashMap<String, Object>();
@@ -435,13 +436,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 pcPort2.get(DeviceManagerImpl.PC_ID_COLUMN_NAME));
         deviceManager.readPortChannelConfigFromStorage();
     }
-    
+    */
     /**
      * The same test as testAttachmentPointFlapping except for port-channel
      * @throws Exception
      */
     @Test
     public void testPortChannel() throws Exception {
+        fail();
+        /*
     	OFPhysicalPort port1 = new OFPhysicalPort();
     	OFPhysicalPort port2 = new OFPhysicalPort();
         port1.setName("port1");
@@ -490,5 +493,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         deviceManager.clearAllDeviceStateFromMemory();
         
         teardownPortChannel();
+        */
     }
 }
-- 
GitLab