diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java index 4deba4b6277c86e0252b6249120df2ba7a31cf1d..22c3c44a53a1b3fcf98c1455110b090246a15ce7 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java @@ -19,6 +19,9 @@ package net.floodlightcontroller.devicemanager; import java.util.Date; +import net.floodlightcontroller.devicemanager.internal.Entity; + + /** * 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 @@ -80,10 +83,15 @@ public interface IDevice { public Date getLastSeen(); /** - * Get the entity classes for the device. - * @return the entity classes - * @see IEntityClassifier - * @see IDeviceService#setEntityClassifier(IEntityClassifier) + * Get the entity class for the device. + * @return the entity class + * @see IEntityClassifierService + */ + public IEntityClass getEntityClass(); + + /** + * Get the list of entities for this device + * @return */ - public IEntityClass[] getEntityClasses(); + public Entity[] getEntities(); } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 79cb2699bab7b8da1d6fc6700a7fbbeb1445442c..5d482c48133e1c09d0945bbc2d2117472523ee54 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -67,27 +67,36 @@ public interface IDeviceService extends IFloodlightService { public IDevice getDevice(Long deviceKey); /** - * Search for a device using exactly matching the provided device fields. - * Only the key fields as defined by the {@link IEntityClassifier} will - * be important in this search. + * Search for a device exactly matching the provided device fields. This + * is the same lookup process that is used for packet_in processing and + * device learning. Thus, findDevice() can be used to match flow entries + * from switches to devices. + * Only the key fields as defined by the {@link IEntityClassifierService} will + * be important in this search. All key fields MUST be supplied. + * + *{@link queryDevices()} might be more appropriate! * * @param macAddress The MAC address - * @param vlan the VLAN + * @param vlan the VLAN. Null means no VLAN and is valid even if VLAN is a + * key field. * @param ipv4Address the ipv4 address * @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) + * @see IDeviceManager#setEntityClassifier(IEntityClassifierService) + * @throws IllegalArgumentException if not all key fields of the + * current {@link IEntityClassifierService} are specified. */ public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address, Long switchDPID, - Integer switchPort); + Integer switchPort) + throws IllegalArgumentException; /** * Get a destination device using entity fields that corresponds with * the given source device. The source device is important since * there could be ambiguity in the destination device without the - * attachment point information. + * attachment point information. * * @param source the source device. The returned destination will be * in the same entity class as the source. @@ -97,10 +106,13 @@ public interface IDeviceService extends IFloodlightService { * @return an {@link IDevice} or null if no device is found. * @see IDeviceService#findDevice(long, Short, Integer, Long, * Integer) + * @throws IllegalArgumentException if not all key fields of the + * source's {@link IEntityClass} are specified. */ public IDevice findDestDevice(IDevice source, long macAddress, Short vlan, - Integer ipv4Address); + Integer ipv4Address) + throws IllegalArgumentException; /** * Get an unmodifiable collection view over all devices currently known. @@ -177,16 +189,7 @@ public interface IDeviceService extends IFloodlightService { */ public void addListener(IDeviceListener listener); - /** - * 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 * diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java index 1053a79a1b9cc54849f917576573e103a841dc50..bb077f116ce3094151fdd543af680aa8c9828c42 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java @@ -28,7 +28,7 @@ import net.floodlightcontroller.devicemanager.internal.Device; * class. A set of entities are considered to be the same device if and only * if they belong to the same entity class and they match on all key fields * 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 + * it in the list of key fields returned by {@link IEntityClassifierService} and/or * {@link IEntityClass}. * * Note that if you're not using static objects, you'll need to override @@ -49,5 +49,11 @@ public interface IEntityClass { * be wildcarded. May be null to indicate that all fields are key fields. */ EnumSet<DeviceField> getKeyFields(); + + /** + * Returns a user-friendly, unique name for this EntityClass + * @return the name of the entity class + */ + String getName(); } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java similarity index 83% rename from src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java rename to src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java index 31cf4a70dfa39690adcb8dcd399a498976841164..8d693ce9220155f92ef2aef5ecfec785eee78c84 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java @@ -19,6 +19,8 @@ package net.floodlightcontroller.devicemanager; import java.util.Collection; import java.util.EnumSet; + +import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; import net.floodlightcontroller.devicemanager.internal.Entity; @@ -30,32 +32,27 @@ import net.floodlightcontroller.devicemanager.internal.Entity; * * @author readams */ -public interface IEntityClassifier { +public interface IEntityClassifierService extends IFloodlightService { /** - * Classify the given entity into a set of classes. It is important - * that the key fields returned by {@link IEntityClassifier#getKeyFields()} + * Classify the given entity into an IEntityClass. It is important + * that the key fields returned by {@link IEntityClassifierService#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. Furthermore, entity classification must be * transitive: For all entities x, y, z, if x and y belong to a class c, and * y and z belong class c, then x and z must belong to class c. * - * <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. When - * iterating, must return results in a sorted order. - * @see IEntityClassifier#getKeyFields() + * @return the IEntityClass resulting from the classification. + * @see IEntityClassifierService#getKeyFields() */ - Collection<IEntityClass> classifyEntity(Entity entity); + IEntityClass classifyEntity(Entity entity); /** * Return the most general list of fields that should be used as key * fields. If devices differ in any fields not listed here, they can * never be considered a different device by any {@link IEntityClass} - * returned by {@link IEntityClassifier#classifyEntity}. The key fields + * returned by {@link IEntityClassifierService#classifyEntity}. The key fields * for an entity classifier must not change unless associated with a * flush of all entity state. The list of key fields must be the union * of all key fields that could be returned by @@ -64,7 +61,7 @@ public interface IEntityClassifier { * @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} + * @see {@link IEntityClassifierService#classifyEntity} */ EnumSet<DeviceField> getKeyFields(); @@ -83,7 +80,7 @@ public interface IEntityClassifier { * @param entity the entity to reclassify * @return the IEntityClass resulting from the classification */ - Collection<IEntityClass> reclassifyEntity(IDevice curDevice, + IEntityClass reclassifyEntity(IDevice curDevice, Entity entity); /** diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java index 32bbeabb63eb317862d16f90f3aac5119e2742b4..353a72d1b5b8a3e382feb895d6ac24e10bc9c8e8 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java @@ -17,22 +17,31 @@ package net.floodlightcontroller.devicemanager.internal; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; import net.floodlightcontroller.devicemanager.IEntityClass; -import net.floodlightcontroller.devicemanager.IEntityClassifier; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; /** * This is a default entity classifier that simply classifies all * entities into a fixed entity class, with key fields of MAC and VLAN. * @author readams */ -public class DefaultEntityClassifier implements IEntityClassifier { +public class DefaultEntityClassifier implements + IEntityClassifierService, + IFloodlightModule +{ /** * A default fixed entity class */ @@ -41,6 +50,11 @@ public class DefaultEntityClassifier implements IEntityClassifier { public EnumSet<IDeviceService.DeviceField> getKeyFields() { return keyFields; } + + @Override + public String getName() { + return "DefaultEntityClass"; + } } protected static EnumSet<DeviceField> keyFields; @@ -49,20 +63,15 @@ public class DefaultEntityClassifier implements IEntityClassifier { } protected static IEntityClass entityClass = new DefaultEntityClass(); - public static Collection<IEntityClass> entityClasses; - static { - entityClasses = Arrays.asList(entityClass); - } - @Override - public Collection<IEntityClass> classifyEntity(Entity entity) { - return entityClasses; + public IEntityClass classifyEntity(Entity entity) { + return entityClass; } @Override - public Collection<IEntityClass> reclassifyEntity(IDevice curDevice, + public IEntityClass reclassifyEntity(IDevice curDevice, Entity entity) { - return entityClasses; + return entityClass; } @Override @@ -75,4 +84,41 @@ public class DefaultEntityClassifier implements IEntityClassifier { public EnumSet<DeviceField> getKeyFields() { return keyFields; } + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IEntityClassifierService.class); + return l; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + // We are the class that implements the service + m.put(IEntityClassifierService.class, this); + return m; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + // No dependencies + return null; + } + + @Override + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { + // no-op + } + + @Override + public void startUp(FloodlightModuleContext context) { + // no-op + } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 447be63f3cf0164c5ab84f62386a11dc7aca1247..b0b33794f2e0dfe30d633609951ccc5a6f6a54c3 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -27,7 +27,6 @@ import java.util.TreeSet; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.openflow.util.HexString; - import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; import net.floodlightcontroller.devicemanager.web.DeviceSerializer; import net.floodlightcontroller.devicemanager.IDevice; @@ -46,7 +45,7 @@ public class Device implements IDevice { protected DeviceManagerImpl deviceManager; protected Entity[] entities; - protected IEntityClass[] entityClasses; + protected IEntityClass entityClass; protected String macAddressString; @@ -59,19 +58,18 @@ public class Device implements IDevice { * @param deviceManager the device manager for this device * @param deviceKey the unique identifier for this device object * @param entity the initial entity for the device - * @param entityClasses the entity classes associated with the entity + * @param entityClass the entity classes associated with the entity */ public Device(DeviceManagerImpl deviceManager, Long deviceKey, Entity entity, - Collection<IEntityClass> entityClasses) { + IEntityClass entityClass) { this.deviceManager = deviceManager; this.deviceKey = deviceKey; this.entities = new Entity[] {entity}; this.macAddressString = HexString.toHexString(entity.getMacAddress(), 6); - this.entityClasses = - entityClasses.toArray(new IEntityClass[entityClasses.size()]); + this.entityClass = entityClass; Arrays.sort(this.entities); } @@ -80,18 +78,18 @@ public class Device implements IDevice { * @param deviceManager the device manager for this device * @param deviceKey the unique identifier for this device object * @param entities the initial entities for the device - * @param entityClasses the entity classes associated with the entity + * @param entityClass the entity class associated with the entities */ public Device(DeviceManagerImpl deviceManager, Long deviceKey, Collection<Entity> entities, - IEntityClass[] entityClasses) { + IEntityClass entityClass) { this.deviceManager = deviceManager; this.deviceKey = deviceKey; this.entities = entities.toArray(new Entity[entities.size()]); this.macAddressString = HexString.toHexString(this.entities[0].getMacAddress(), 6); - this.entityClasses = entityClasses; + this.entityClass = entityClass; Arrays.sort(this.entities); } @@ -99,12 +97,11 @@ public class Device implements IDevice { * Construct a new device consisting of the entities from the old device * plus an additional entity * @param device the old device object - * @param newEntity the entity to add - * @param entityClasses the entity classes associated with the entities + * @param newEntity the entity to add. newEntity must be have the same + * entity class as device */ public Device(Device device, - Entity newEntity, - Collection<IEntityClass> entityClasses) { + Entity newEntity) { this.deviceManager = device.deviceManager; this.deviceKey = device.deviceKey; this.entities = Arrays.<Entity>copyOf(device.entities, @@ -115,15 +112,7 @@ public class Device implements IDevice { this.macAddressString = HexString.toHexString(this.entities[0].getMacAddress(), 6); - if (entityClasses != null && - entityClasses.size() > device.entityClasses.length) { - IEntityClass[] classes = new IEntityClass[entityClasses.size()]; - this.entityClasses = - entityClasses.toArray(classes); - } else { - // same actual array, not a copy - this.entityClasses = device.entityClasses; - } + this.entityClass = device.entityClass; } // ******* @@ -173,43 +162,33 @@ public class Device implements IDevice { // XXX - TODO we can cache this result. Let's find out if this // is really a performance bottleneck first though. - if (entities.length == 1) { - if (entities[0].getIpv4Address() != null) { - return new Integer[]{ entities[0].getIpv4Address() }; - } else { - return new Integer[0]; - } - } - TreeSet<Integer> vals = new TreeSet<Integer>(); for (Entity e : entities) { if (e.getIpv4Address() == null) continue; - + // We have an IP address only if among the devices within the class // we have the most recent entity with that IP. boolean validIP = true; - for (IEntityClass clazz : entityClasses) { - Iterator<Device> devices = - deviceManager.queryClassByEntity(clazz, ipv4Fields, e); - while (devices.hasNext()) { - Device d = devices.next(); - for (Entity se : d.entities) { - if (se.ipv4Address != null && - se.ipv4Address.equals(e.ipv4Address) && - se.lastSeenTimestamp != null && - 0 < se.lastSeenTimestamp. - compareTo(e.lastSeenTimestamp)) { - validIP = false; - break; - } - } - if (!validIP) + Iterator<Device> devices = + deviceManager.queryClassByEntity(entityClass, ipv4Fields, e); + while (devices.hasNext()) { + Device d = devices.next(); + if (deviceKey.equals(d.getDeviceKey())) + continue; + for (Entity se : d.entities) { + if (se.getIpv4Address() != null && + se.getIpv4Address().equals(e.getIpv4Address()) && + se.getLastSeenTimestamp() != null && + 0 < se.getLastSeenTimestamp(). + compareTo(e.getLastSeenTimestamp())) { + validIP = false; break; + } } if (!validIP) break; } - + if (validIP) vals.add(e.getIpv4Address()); } @@ -268,7 +247,7 @@ public class Device implements IDevice { ) continue; long curCluster = - topology.getL2DomainId(cur.switchDPID); + topology.getL2DomainId(cur.getSwitchDPID()); if (prevCluster != curCluster) { prev = null; latestLastSeen = 0; @@ -379,10 +358,12 @@ public class Device implements IDevice { // Getters/Setters // *************** - public IEntityClass[] getEntityClasses() { - return entityClasses; + @Override + public IEntityClass getEntityClass() { + return entityClass; } + @Override public Entity[] getEntities() { return entities; } @@ -425,6 +406,7 @@ public class Device implements IDevice { @Override public String toString() { - return "Device [entities=" + Arrays.toString(entities) + "]"; + return "Device [entityClass=" + entityClass.getName() + + " entities=" + Arrays.toString(entities) + "]"; } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java index 795775c15f1f5d0852aa4fb362b4dd5ced3600ec..2cbea66e5577495461171661f479c4f26335f11f 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java @@ -67,37 +67,34 @@ public class DeviceIterator extends FilterIterator<Device> { protected boolean matches(Device value) { boolean match; if (entityClasses != null) { - IEntityClass[] classes = next.getEntityClasses(); - if (classes == null) return false; + IEntityClass clazz = value.getEntityClass(); + if (clazz == null) return false; match = false; - for (IEntityClass clazz : classes) { - for (IEntityClass entityClass : entityClasses) { - if (clazz.equals(entityClass)) { - match = true; - break; - } + for (IEntityClass entityClass : entityClasses) { + if (clazz.equals(entityClass)) { + match = true; + break; } - if (match == true) break; } if (!match) return false; } if (macAddress != null) { - if (macAddress.longValue() != next.getMACAddress()) + if (macAddress.longValue() != value.getMACAddress()) return false; } if (vlan != null) { - Short[] vlans = next.getVlanId(); + Short[] vlans = value.getVlanId(); if (Arrays.binarySearch(vlans, vlan) < 0) return false; } if (ipv4Address != null) { - Integer[] ipv4Addresses = next.getIPv4Addresses(); + Integer[] ipv4Addresses = value.getIPv4Addresses(); if (Arrays.binarySearch(ipv4Addresses, ipv4Address) < 0) return false; } if (switchDPID != null || switchPort != null) { - SwitchPort[] sps = next.getAttachmentPoints(); + SwitchPort[] sps = value.getAttachmentPoints(); if (sps == null) return false; match = false; diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index ab1d8788dbfd91ff7802b0622bee662e260cc3ee..7592332f1a544344e3d9d24943a8e0e755c8ffec 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -18,7 +18,6 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -50,7 +49,7 @@ import net.floodlightcontroller.core.util.SingletonTask; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClass; -import net.floodlightcontroller.devicemanager.IEntityClassifier; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.devicemanager.IDeviceListener; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.devicemanager.web.DeviceRoutable; @@ -157,7 +156,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { /** * The entity classifier currently in use */ - IEntityClassifier entityClassifier; + protected IEntityClassifierService entityClassifier; /** * Used to cache state about specific entity classes @@ -316,30 +315,38 @@ IFlowReconcileListener, IInfoProvider, IHAListener { @Override public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address, Long switchDPID, - Integer switchPort) { + Integer switchPort) + throws IllegalArgumentException { if (vlan != null && vlan.shortValue() <= 0) vlan = null; if (ipv4Address != null && ipv4Address == 0) ipv4Address = null; - return findDeviceByEntity(new Entity(macAddress, vlan, - ipv4Address, switchDPID, - switchPort, null)); + Entity e = new Entity(macAddress, vlan, ipv4Address, switchDPID, + switchPort, null); + if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) { + throw new IllegalArgumentException("Not all key fields specified." + + " Required fields: " + entityClassifier.getKeyFields()); + } + return findDeviceByEntity(e); } @Override public IDevice findDestDevice(IDevice source, long macAddress, - Short vlan, Integer ipv4Address) { + Short vlan, Integer ipv4Address) + throws IllegalArgumentException { if (vlan != null && vlan.shortValue() <= 0) vlan = null; if (ipv4Address != null && ipv4Address == 0) ipv4Address = null; - return findDestByEntity(source, - new Entity(macAddress, - vlan, - ipv4Address, - null, - null, - null)); + Entity e = new Entity(macAddress, vlan, ipv4Address, + null, null, null); + if (source == null || + !allKeyFieldsPresent(e, source.getEntityClass().getKeyFields())) { + throw new IllegalArgumentException("Not all key fields and/or " + + " no source device specified. Required fields: " + + entityClassifier.getKeyFields()); + } + return findDestByEntity(source, e); } @Override @@ -406,48 +413,46 @@ IFlowReconcileListener, IInfoProvider, IHAListener { Integer ipv4Address, Long switchDPID, Integer switchPort) { - IEntityClass[] entityClasses = reference.getEntityClasses(); + IEntityClass entityClass = reference.getEntityClass(); ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>(); - for (IEntityClass clazz : entityClasses) { - ClassState classState = getClassState(clazz); - - DeviceIndex index = null; - if (classState.secondaryIndexMap.size() > 0) { - EnumSet<DeviceField> keys = - getEntityKeys(macAddress, vlan, ipv4Address, - switchDPID, switchPort); - index = classState.secondaryIndexMap.get(keys); - } - - Iterator<Device> iter; + ClassState classState = getClassState(entityClass); + + DeviceIndex index = null; + if (classState.secondaryIndexMap.size() > 0) { + EnumSet<DeviceField> keys = + getEntityKeys(macAddress, vlan, ipv4Address, + switchDPID, switchPort); + index = classState.secondaryIndexMap.get(keys); + } + + Iterator<Device> iter; + if (index == null) { + index = classState.classIndex; if (index == null) { - index = classState.classIndex; - if (index == null) { - // scan all devices - return new DeviceIterator(deviceMap.values().iterator(), - entityClasses, - macAddress, vlan, ipv4Address, - switchDPID, switchPort); - } else { - // scan the entire class - iter = new DeviceIndexInterator(this, index.getAll()); - } + // scan all devices + return new DeviceIterator(deviceMap.values().iterator(), + new IEntityClass[] { entityClass }, + macAddress, vlan, ipv4Address, + switchDPID, switchPort); } else { - // index lookup - Entity entity = - new Entity((macAddress == null ? 0 : macAddress), - vlan, - ipv4Address, - switchDPID, - switchPort, - null); - iter = new DeviceIndexInterator(this, - index.queryByEntity(entity)); + // scan the entire class + iter = new DeviceIndexInterator(this, index.getAll()); } - iterators.add(iter); + } else { + // index lookup + Entity entity = + new Entity((macAddress == null ? 0 : macAddress), + vlan, + ipv4Address, + switchDPID, + switchPort, + null); + iter = new DeviceIndexInterator(this, + index.queryByEntity(entity)); } - + iterators.add(iter); + return new MultiIterator<Device>(iterators.iterator()); } @@ -456,11 +461,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { deviceListeners.add(listener); } - @Override - public void setEntityClassifier(IEntityClassifier classifier) { - entityClassifier = classifier; - } - @Override public void flushEntityCache(IEntityClass entityClass, boolean reclassify) { @@ -600,6 +600,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { l.add(IRestApiService.class); l.add(IThreadPoolService.class); l.add(IFlowReconcileService.class); + l.add(IEntityClassifierService.class); return l; } @@ -622,13 +623,11 @@ IFlowReconcileListener, IInfoProvider, IHAListener { this.restApi = fmc.getServiceImpl(IRestApiService.class); this.threadPool = fmc.getServiceImpl(IThreadPoolService.class); this.flowReconcileMgr = fmc.getServiceImpl(IFlowReconcileService.class); + this.entityClassifier = fmc.getServiceImpl(IEntityClassifierService.class); } @Override public void startUp(FloodlightModuleContext fmc) { - if (entityClassifier == null) - setEntityClassifier(new DefaultEntityClassifier()); - primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields()); secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>(); @@ -710,13 +709,21 @@ IFlowReconcileListener, IInfoProvider, IHAListener { // Find the device matching the destination from the entity // classes of the source. Entity dstEntity = getDestEntityFromPacket(eth); + Device dstDevice = null; if (dstEntity != null) { - Device dstDevice = + dstDevice = findDestByEntity(srcDevice, dstEntity); if (dstDevice != null) fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); } + if (logger.isTraceEnabled()) { + logger.trace("Received PI: {} on switch {}, port {} *** eth={}" + + " *** srcDev={} *** dstDev={} *** ", + new Object[] { pi, sw.getStringId(), pi.getInPort(), eth, + srcDevice, dstDevice } + ); + } return Command.CONTINUE; } @@ -748,6 +755,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener { return true; } + /** + * Get IP address from packet if the packet is either an ARP + * or a DHCP packet + * @param eth + * @param dlAddr + * @return + */ private int getSrcNwAddr(Ethernet eth, long dlAddr) { if (eth.getPayload() instanceof ARP) { ARP arp = (ARP) eth.getPayload(); @@ -787,24 +801,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if ((dlAddrArr[0] & 0x1) != 0) return null; - boolean learnap = true; - if (!isValidAttachmentPoint(swdpid, (short)port)) { - // If this is an internal port or we otherwise don't want - // to learn on these ports. In the future, we should - // handle this case by labeling flows with something that - // will give us the entity class. For now, we'll do our - // best assuming attachment point information isn't used - // as a key field. - learnap = false; - } - short vlan = eth.getVlanID(); int nwSrc = getSrcNwAddr(eth, dlAddr); return new Entity(dlAddr, ((vlan >= 0) ? vlan : null), ((nwSrc != 0) ? nwSrc : null), - (learnap ? swdpid : null), - (learnap ? port : null), + swdpid, + port, new Date()); } @@ -878,12 +881,33 @@ IFlowReconcileListener, IInfoProvider, IHAListener { new Date()); } /** - * Look up a {@link Device} based on the provided {@link Entity}. + * Look up a {@link Device} based on the provided {@link Entity}. We first + * check the primary index. If we do not find an entry there we classify + * the device into its IEntityClass and query the classIndex. + * This implies that all key field of the current IEntityClassifier must + * be present in the entity for the lookup to succeed! * @param entity the entity to search for * @return The {@link Device} object if found */ protected Device findDeviceByEntity(Entity entity) { - Long deviceKey = primaryIndex.findByEntity(entity); + // Look up the fully-qualified entity to see if it already + // exists in the primary entity index. + Long deviceKey = primaryIndex.findByEntity(entity); + IEntityClass entityClass = 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 the returned class' + // class entity index. + entityClass = entityClassifier.classifyEntity(entity); + ClassState classState = getClassState(entityClass); + + if (classState.classIndex != null) { + deviceKey = + classState.classIndex.findByEntity(entity); + } + } if (deviceKey == null) return null; return deviceMap.get(deviceKey); } @@ -900,36 +924,30 @@ IFlowReconcileListener, IInfoProvider, IHAListener { */ protected Device findDestByEntity(IDevice source, Entity dstEntity) { - Device dstDevice = findDeviceByEntity(dstEntity); - - //if (dstDevice == null) { - // This could happen because: - // 1) no destination known, or a broadcast destination - // 2) if we have attachment point key fields since - // attachment point information isn't available for - // destination devices. - // For the second case, we'll need to match up the - // destination device with the class of the source - // device. - /* - ArrayList<Device> candidates = new ArrayList<Device>(); - for (IEntityClass clazz : srcDevice.getEntityClasses()) { - Device c = findDeviceInClassByEntity(clazz, dstEntity); - if (c != null) - candidates.add(c); - } - if (candidates.size() == 1) { - dstDevice = candidates.get(0); - } else if (candidates.size() > 1) { - // ambiguous device. A higher-order component will - // need to deal with it by assigning priority - // XXX - TODO - } - */ - //} - - return dstDevice; + + // Look up the fully-qualified entity to see if it + // exists in the primary entity index + Long deviceKey = primaryIndex.findByEntity(dstEntity); + + if (deviceKey == null) { + // This could happen because: + // 1) no destination known, or a broadcast destination + // 2) if we have attachment point key fields since + // attachment point information isn't available for + // destination devices. + // For the second case, we'll need to match up the + // destination device with the class of the source + // device. + ClassState classState = getClassState(source.getEntityClass()); + if (classState.classIndex == null) { + return null; + } + deviceKey = classState.classIndex.findByEntity(dstEntity); + } + if (deviceKey == null) return null; + return deviceMap.get(deviceKey); } + /** * Look up a {@link Device} within a particular entity class based on @@ -967,21 +985,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener { // Look up the fully-qualified entity to see if it already // exists in the primary entity index. Long deviceKey = primaryIndex.findByEntity(entity); - Collection<IEntityClass> classes = null; + IEntityClass entityClass = 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) { - ClassState classState = getClassState(clazz); - - if (classState.classIndex != null) { - deviceKey = - classState.classIndex.findByEntity(entity); - } + // entity. Look up the entity in the returned class' + // class entity index. + entityClass = entityClassifier.classifyEntity(entity); + ClassState classState = getClassState(entityClass); + + if (classState.classIndex != null) { + deviceKey = + classState.classIndex.findByEntity(entity); } } if (deviceKey != null) { @@ -998,7 +1014,11 @@ IFlowReconcileListener, IInfoProvider, IHAListener { synchronized (deviceKeyLock) { deviceKey = Long.valueOf(deviceKeyCounter++); } - device = allocateDevice(deviceKey, entity, classes); + device = allocateDevice(deviceKey, entity, entityClass); + if (logger.isDebugEnabled()) { + logger.debug("New device created: {} deviceKey={}", + device, deviceKey); + } // Add the new device to the primary map with a simple put deviceMap.put(deviceKey, device); @@ -1011,7 +1031,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { continue; } - updateSecondaryIndices(entity, classes, deviceKey); + updateSecondaryIndices(entity, entityClass, deviceKey); // generate new device update deviceUpdates = @@ -1029,7 +1049,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { device.entities[entityindex].setLastSeenTimestamp(lastSeen); break; } else { - Device newDevice = allocateDevice(device, entity, classes); + Device newDevice = allocateDevice(device, entity); // generate updates EnumSet<DeviceField> changedFields = @@ -1055,7 +1075,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { continue; } updateSecondaryIndices(entity, - device.getEntityClasses(), + device.getEntityClass(), deviceKey); break; } @@ -1146,6 +1166,41 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } } } + + /** + * Check if the entity e has all the keyFields set. Returns false if not + * @param e entity to check + * @param keyFields the key fields to check e against + * @return + */ + protected boolean allKeyFieldsPresent(Entity e, EnumSet<DeviceField> keyFields) { + for (DeviceField f : keyFields) { + switch (f) { + case MAC: + // MAC address is always present + break; + case IPV4: + if (e.ipv4Address == null) return false; + break; + case SWITCH: + if (e.switchDPID == null) return false; + break; + case PORT: + if (e.switchPort == null) return false; + break; + case VLAN: + // FIXME: vlan==null is ambiguous: it can mean: not present + // or untagged + //if (e.vlan == null) return false; + break; + default: + // we should never get here. unless somebody extended + // DeviceFields + throw new IllegalStateException(); + } + } + return true; + } private LinkedList<DeviceUpdate> updateUpdates(LinkedList<DeviceUpdate> list, DeviceUpdate update) { @@ -1178,7 +1233,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { /** * Update both the primary and class indices for the provided device. - * If the update fails because of aEn concurrent update, will return false. + * If the update fails because of an 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. @@ -1187,52 +1242,36 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (!primaryIndex.updateIndex(device, deviceKey)) { return false; } - for (IEntityClass clazz : device.getEntityClasses()) { - ClassState classState = getClassState(clazz); + IEntityClass entityClass = device.getEntityClass(); + ClassState classState = getClassState(entityClass); - if (classState.classIndex != null) { - if (!classState.classIndex.updateIndex(device, - deviceKey)) - return false; - } + if (classState.classIndex != null) { + if (!classState.classIndex.updateIndex(device, + deviceKey)) + return false; } - return true; + return true; } /** * Update the secondary indices for the given entity and associated * entity classes * @param entity the entity to update - * @param entityClasses the entity classes for the entity + * @param entityClass the entity class for the entity * @param deviceKey the device key to set up */ private void updateSecondaryIndices(Entity entity, - Collection<IEntityClass> entityClasses, + IEntityClass entityClass, Long deviceKey) { for (DeviceIndex index : secondaryIndexMap.values()) { index.updateIndex(entity, deviceKey); } - for (IEntityClass clazz : entityClasses) { - ClassState state = getClassState(clazz); - for (DeviceIndex index : state.secondaryIndexMap.values()) { - index.updateIndex(entity, deviceKey); - } + ClassState state = getClassState(entityClass); + for (DeviceIndex index : state.secondaryIndexMap.values()) { + index.updateIndex(entity, deviceKey); } } - /** - * Update the secondary indices for the given entity and associated - * entity classes - * @param entity the entity to update - * @param entityClasses the entity classes for the entity - * @param deviceKey the device key to set up - */ - private void updateSecondaryIndices(Entity entity, - IEntityClass[] entityClasses, - Long deviceKey) { - updateSecondaryIndices(entity, Arrays.asList(entityClasses), deviceKey); - } - /** * Clean up expired entities/devices */ @@ -1269,13 +1308,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } for (Entity e : toRemove) { - removeEntity(e, d.getEntityClasses(), d.deviceKey, toKeep); + removeEntity(e, d.getEntityClass(), d.deviceKey, toKeep); } if (toKeep.size() > 0) { Device newDevice = allocateDevice(d.getDeviceKey(), toKeep, - d.entityClasses); + d.entityClass); EnumSet<DeviceField> changedFields = EnumSet.noneOf(DeviceField.class); @@ -1305,29 +1344,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } private void removeEntity(Entity removed, - IEntityClass[] classes, + IEntityClass entityClass, Long deviceKey, Collection<Entity> others) { for (DeviceIndex index : secondaryIndexMap.values()) { index.removeEntityIfNeeded(removed, deviceKey, others); } - for (IEntityClass clazz : classes) { - ClassState classState = getClassState(clazz); - for (DeviceIndex index : classState.secondaryIndexMap.values()) { - index.removeEntityIfNeeded(removed, deviceKey, others); - } + ClassState classState = getClassState(entityClass); + for (DeviceIndex index : classState.secondaryIndexMap.values()) { + index.removeEntityIfNeeded(removed, deviceKey, others); } primaryIndex.removeEntityIfNeeded(removed, deviceKey, others); - for (IEntityClass clazz : classes) { - ClassState classState = getClassState(clazz); - - if (classState.classIndex != null) { - classState.classIndex.removeEntityIfNeeded(removed, - deviceKey, - others); - } + if (classState.classIndex != null) { + classState.classIndex.removeEntityIfNeeded(removed, + deviceKey, + others); } } @@ -1336,6 +1369,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener { Integer ipv4Address, Long switchDPID, Integer switchPort) { + // FIXME: vlan==null is a valid search. Need to handle this + // case correctly. Note that the code will still work correctly. + // But we might do a full device search instead of using an index. EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class); if (macAddress != null) keys.add(DeviceField.MAC); if (vlan != null) keys.add(DeviceField.VLAN); @@ -1357,20 +1393,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener { protected Device allocateDevice(Long deviceKey, Entity entity, - Collection<IEntityClass> entityClasses) { - return new Device(this, deviceKey, entity, entityClasses); + IEntityClass entityClass) { + return new Device(this, deviceKey, entity, entityClass); } protected Device allocateDevice(Long deviceKey, Collection<Entity> entities, - IEntityClass[] entityClasses) { - return new Device(this, deviceKey, entities, entityClasses); + IEntityClass entityClass) { + return new Device(this, deviceKey, entities, entityClass); } protected Device allocateDevice(Device device, - Entity entity, - Collection<IEntityClass> entityClasses) { - return new Device(device, entity, entityClasses); + Entity entity) { + return new Device(device, entity); } @Override diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 58dfd424c408607f175332d1dede8e0ff0d73281..4ac12eba8cfeb1c3a4c1d1cf16ccb70b85f01ca0 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -171,7 +171,7 @@ public class Entity implements Comparable<Entity> { public void setActiveSince(Date activeSince) { this.activeSince = activeSince; } - + @Override public int hashCode() { if (hashCode != 0) return hashCode; diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java index ac1981b1abc20d6c4693914f7d37d276c7e975bd..7a128e6259bb07f185a1c631f61288811ec82540 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java @@ -5,6 +5,7 @@ import java.util.EnumSet; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; + /** * This is a thin wrapper around {@link Entity} that allows overriding * the behavior of {@link Object#hashCode()} and {@link Object#equals(Object)} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java index fa9bcc79352539a20abde1d39a891a4c11990d63..66bdaef93255458603491463bc7d2c220cb3162c 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java @@ -40,6 +40,8 @@ public class DeviceSerializer extends JsonSerializer<Device> { JsonProcessingException { jGen.writeStartObject(); + jGen.writeStringField("entityClass", device.getEntityClass().getName()); + jGen.writeArrayFieldStart("mac"); jGen.writeString(HexString.toHexString(device.getMACAddress(), 6)); jGen.writeEndArray(); @@ -50,9 +52,9 @@ public class DeviceSerializer extends JsonSerializer<Device> { jGen.writeEndArray(); jGen.writeArrayFieldStart("vlan"); - for (Short ip : device.getVlanId()) - if (ip >= 0) - jGen.writeNumber(ip); + for (Short vlan : device.getVlanId()) + if (vlan >= 0) + jGen.writeNumber(vlan); jGen.writeEndArray(); jGen.writeArrayFieldStart("attachmentPoint"); for (SwitchPort ap : device.getAttachmentPoints(true)) { diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule index 9339ea3bcd6346a4fd1a942ad6611eff23b4a0d2..af007737c2308aaf1d7616c0cd22651623229a5d 100644 --- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule +++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule @@ -18,3 +18,4 @@ net.floodlightcontroller.counter.NullCounterStore net.floodlightcontroller.threadpool.ThreadPool net.floodlightcontroller.ui.web.StaticWebRoutable net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter +net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 25ea828b06c98b510ba0d40fc36b0822a9108e19..2bafa6d32a5551aa35d9ac4163d13d5d422fe3a4 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -19,7 +19,6 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.Arrays; import java.util.Calendar; -import java.util.Collection; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; @@ -41,13 +40,13 @@ import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.core.test.MockThreadPoolService; -import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.*; import net.floodlightcontroller.devicemanager.IDeviceListener; -import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; import net.floodlightcontroller.devicemanager.IDevice; -import net.floodlightcontroller.devicemanager.IEntityClass; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.test.MockEntityClassifier; +import net.floodlightcontroller.devicemanager.test.MockEntityClassifierMac; import net.floodlightcontroller.flowcache.FlowReconcileManager; import net.floodlightcontroller.flowcache.IFlowReconcileService; import net.floodlightcontroller.packet.ARP; @@ -108,21 +107,25 @@ public class DeviceManagerImplTest extends FloodlightTestCase { mockFloodlightProvider = getMockFloodlightProvider(); deviceManager = new DeviceManagerImpl(); flowReconcileMgr = new FlowReconcileManager(); + DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); fmc.addService(IDeviceService.class, deviceManager); storageSource = new MemoryStorageSource(); fmc.addService(IStorageSourceService.class, storageSource); fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider); fmc.addService(IRestApiService.class, restApi); fmc.addService(IFlowReconcileService.class, flowReconcileMgr); + fmc.addService(IEntityClassifierService.class, entityClassifier); tp.init(fmc); restApi.init(fmc); storageSource.init(fmc); deviceManager.init(fmc); flowReconcileMgr.init(fmc); + entityClassifier.init(fmc); storageSource.startUp(fmc); deviceManager.startUp(fmc); flowReconcileMgr.startUp(fmc); tp.startUp(fmc); + entityClassifier.startUp(fmc); IOFSwitch mockSwitch1 = makeSwitchMock(1L); IOFSwitch mockSwitch10 = makeSwitchMock(10L); @@ -219,37 +222,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setTotalLength((short) this.testARPReplyPacket_3_Serialized.length); } - static EnumSet<DeviceField> testKeyFields; - static { - testKeyFields = EnumSet.of(MAC, VLAN, SWITCH, PORT); - } - - public static class TestEntityClass implements IEntityClass { - @Override - public EnumSet<DeviceField> 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) { - return Arrays.asList(testEC); - } - return DefaultEntityClassifier.entityClasses; - } - - @Override - public EnumSet<IDeviceService.DeviceField> getKeyFields() { - return testKeyFields; - } - - } + @Test public void testLastSeen() throws Exception { Calendar c = Calendar.getInstance(); @@ -276,7 +252,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { createStrictMock(IDeviceListener.class); deviceManager.addListener(mockListener); - deviceManager.setEntityClassifier(new TestEntityClassifier()); + deviceManager.entityClassifier= new MockEntityClassifier(); deviceManager.startUp(null); ITopologyService mockTopology = createMock(ITopologyService.class); @@ -314,8 +290,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d1 = deviceManager.learnDeviceByEntity(entity1); assertSame(d1, deviceManager.learnDeviceByEntity(entity1)); assertSame(d1, deviceManager.findDeviceByEntity(entity1)); - assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass }, - d1.entityClasses); + assertEquals(DefaultEntityClassifier.entityClass , + d1.entityClass); assertArrayEquals(new Short[] { -1 }, d1.getVlanId()); assertArrayEquals(new Integer[] { }, d1.getIPv4Addresses()); @@ -329,8 +305,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d2 = deviceManager.learnDeviceByEntity(entity2); assertFalse(d1.equals(d2)); assertNotSame(d1, d2); - assertArrayEquals(new IEntityClass[]{ testEC }, - d2.entityClasses); + assertNotSame(d1.getDeviceKey(), d2.getDeviceKey()); + assertEquals(MockEntityClassifier.testEC, d2.entityClass); assertArrayEquals(new Short[] { -1 }, d2.getVlanId()); assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses()); @@ -343,8 +319,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d3 = deviceManager.learnDeviceByEntity(entity3); assertNotSame(d2, d3); - assertArrayEquals(new IEntityClass[]{ testEC }, - d3.entityClasses); + assertEquals(d2.getDeviceKey(), d3.getDeviceKey()); + assertEquals(MockEntityClassifier.testEC, d3.entityClass); assertArrayEquals(new Integer[] { 1 }, d3.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) }, @@ -363,8 +339,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d4 = deviceManager.learnDeviceByEntity(entity4); assertNotSame(d1, d4); - assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass }, - d4.entityClasses); + assertEquals(d1.getDeviceKey(), d4.getDeviceKey()); + assertEquals(DefaultEntityClassifier.entityClass, d4.entityClass); assertArrayEquals(new Integer[] { 1 }, d4.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, @@ -406,6 +382,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { replay(mockListener); Device d7 = deviceManager.learnDeviceByEntity(entity7); + assertNotSame(d6, d7); + assertEquals(d6.getDeviceKey(), d7.getDeviceKey()); assertArrayEquals(new SwitchPort[] { new SwitchPort(50L, 3) }, d7.getAttachmentPoints()); assertArrayEquals(new Short[] { (short) 4 }, @@ -699,7 +677,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { 1L, 1, currentDate), - DefaultEntityClassifier.entityClasses); + DefaultEntityClassifier.entityClass); expect(mockTopology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())). @@ -738,8 +716,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { ipaddr, 5L, 2, - currentDate), - DefaultEntityClassifier.entityClasses); + currentDate)); reset(mockTopology); expect(mockTopology.isAttachmentPointPort(anyLong(), @@ -1185,4 +1162,204 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public void testDeviceClassQuery() throws Exception { doTestDeviceClassQuery(); } + + @Test + public void testFindDevice() { + boolean exceptionCaught; + deviceManager.entityClassifier= new MockEntityClassifierMac(); + deviceManager.startUp(null); + + Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date()); + Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date()); + Entity entity2b = new Entity(22L, (short)2, 2, 1L, 2, new Date()); + + Entity entity3 = new Entity(3L, (short)1, 3, 2L, 1, new Date()); + Entity entity4 = new Entity(4L, (short)2, 4, 2L, 2, new Date()); + + Entity entity5 = new Entity(5L, (short)1, 5, 3L, 1, new Date()); + + IDevice d1 = deviceManager.learnDeviceByEntity(entity1); + IDevice d2 = deviceManager.learnDeviceByEntity(entity2); + IDevice d3 = deviceManager.learnDeviceByEntity(entity3); + IDevice d4 = deviceManager.learnDeviceByEntity(entity4); + IDevice d5 = deviceManager.learnDeviceByEntity(entity5); + + // Make sure the entity classifier worked as expected + assertEquals(MockEntityClassifierMac.testECMac1, d1.getEntityClass()); + assertEquals(MockEntityClassifierMac.testECMac1, d2.getEntityClass()); + assertEquals(MockEntityClassifierMac.testECMac2, d3.getEntityClass()); + assertEquals(MockEntityClassifierMac.testECMac2, d4.getEntityClass()); + assertEquals(DefaultEntityClassifier.entityClass, + d5.getEntityClass()); + + // Look up the device using findDevice() which uses only the primary + // index + assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(), + entity1.getVlan(), + entity1.getIpv4Address(), + entity1.getSwitchDPID(), + entity1.getSwitchPort())); + // port changed. Device will be found through class index + assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(), + entity1.getVlan(), + entity1.getIpv4Address(), + entity1.getSwitchDPID(), + entity1.getSwitchPort()+1)); + // VLAN changed. No device matches + assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(), + (short)42, + entity1.getIpv4Address(), + entity1.getSwitchDPID(), + entity1.getSwitchPort())); + assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(), + null, + entity1.getIpv4Address(), + entity1.getSwitchDPID(), + entity1.getSwitchPort())); + assertEquals(d2, deviceManager.findDeviceByEntity(entity2)); + assertEquals(null, deviceManager.findDeviceByEntity(entity2b)); + assertEquals(d3, deviceManager.findDevice(entity3.getMacAddress(), + entity3.getVlan(), + entity3.getIpv4Address(), + entity3.getSwitchDPID(), + entity3.getSwitchPort())); + // switch and port not set. throws exception + exceptionCaught = false; + try { + assertEquals(null, deviceManager.findDevice(entity3.getMacAddress(), + entity3.getVlan(), + entity3.getIpv4Address(), + null, + null)); + } + catch (IllegalArgumentException e) { + exceptionCaught = true; + } + if (!exceptionCaught) + fail("findDevice() did not throw IllegalArgumentException"); + assertEquals(d4, deviceManager.findDeviceByEntity(entity4)); + assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(), + entity5.getVlan(), + entity5.getIpv4Address(), + entity5.getSwitchDPID(), + entity5.getSwitchPort())); + // switch and port not set. throws exception (swith/port are key + // fields of IEntityClassifier but not d5.entityClass + exceptionCaught = false; + try { + assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(), + entity5.getVlan(), + entity5.getIpv4Address(), + null, + null)); + } + catch (IllegalArgumentException e) { + exceptionCaught = true; + } + if (!exceptionCaught) + fail("findDevice() did not throw IllegalArgumentException"); + + // Now look up destination devices + assertEquals(d1, deviceManager.findDestDevice(d2, + entity1.getMacAddress(), + entity1.getVlan(), + entity1.getIpv4Address())); + assertEquals(d1, deviceManager.findDestDevice(d2, + entity1.getMacAddress(), + entity1.getVlan(), + null)); + assertEquals(null, deviceManager.findDestDevice(d2, + entity1.getMacAddress(), + (short) -1, + 0)); + } + + @Test + public void testGetIPv4Addresses() { + // Looks like Date is only 1s granularity + + Entity e1 = new Entity(1L, (short)1, null, null, null, new Date(2000)); + Device d1 = deviceManager.learnDeviceByEntity(e1); + assertArrayEquals(new Integer[0], d1.getIPv4Addresses()); + + + Entity e2 = new Entity(2L, (short)2, 2, null, null, new Date(2000)); + Device d2 = deviceManager.learnDeviceByEntity(e2); + d2 = deviceManager.learnDeviceByEntity(e2); + assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); + // More than one entity + Entity e2b = new Entity(2L, (short)2, null, 2L, 2, new Date(3000)); + d2 = deviceManager.learnDeviceByEntity(e2b); + assertEquals(2, d2.entities.length); + assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); + // and now add an entity with an IP + Entity e2c = new Entity(2L, (short)2, 2, 2L, 3, new Date(3000)); + d2 = deviceManager.learnDeviceByEntity(e2c); + assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); + assertEquals(3, d2.entities.length); + + // Other devices with different IPs shouldn't interfere + Entity e3 = new Entity(3L, (short)3, 3, null, null, new Date(4000)); + Entity e3b = new Entity(3L, (short)3, 3, 3L, 3, new Date(4400)); + Device d3 = deviceManager.learnDeviceByEntity(e3); + d3 = deviceManager.learnDeviceByEntity(e3b); + assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); + assertArrayEquals(new Integer[] { 3 }, d3.getIPv4Addresses()); + + // Add another IP to d3 + Entity e3c = new Entity(3L, (short)3, 33, 3L, 3, new Date(4400)); + d3 = deviceManager.learnDeviceByEntity(e3c); + Integer[] ips = d3.getIPv4Addresses(); + Arrays.sort(ips); + assertArrayEquals(new Integer[] { 3, 33 }, ips); + + // Add another device that also claims IP2 but is older than e2 + Entity e4 = new Entity(4L, (short)4, 2, null, null, new Date(1000)); + Entity e4b = new Entity(4L, (short)4, null, 4L, 4, new Date(1000)); + Device d4 = deviceManager.learnDeviceByEntity(e4); + assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); + assertArrayEquals(new Integer[0], d4.getIPv4Addresses()); + // add another entity to d4 + d4 = deviceManager.learnDeviceByEntity(e4b); + assertArrayEquals(new Integer[0], d4.getIPv4Addresses()); + + // Make e4 and e4a newer + Entity e4c = new Entity(4L, (short)4, 2, null, null, new Date(5000)); + Entity e4d = new Entity(4L, (short)4, null, 4L, 5, new Date(5000)); + d4 = deviceManager.learnDeviceByEntity(e4c); + d4 = deviceManager.learnDeviceByEntity(e4d); + assertArrayEquals(new Integer[0], d2.getIPv4Addresses()); + // FIXME: d4 should not return IP4 + assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses()); + + // Add another newer entity to d2 but with different IP + Entity e2d = new Entity(2L, (short)2, 22, 4L, 6, new Date(6000)); + d2 = deviceManager.learnDeviceByEntity(e2d); + assertArrayEquals(new Integer[] { 22 }, d2.getIPv4Addresses()); + assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses()); + + // new IP for d2,d4 but with same timestamp. Both devices get the IP + Entity e2e = new Entity(2L, (short)2, 42, 2L, 4, new Date(7000)); + d2 = deviceManager.learnDeviceByEntity(e2e); + ips= d2.getIPv4Addresses(); + Arrays.sort(ips); + assertArrayEquals(new Integer[] { 22, 42 }, ips); + Entity e4e = new Entity(4L, (short)4, 42, 4L, 7, new Date(7000)); + d4 = deviceManager.learnDeviceByEntity(e4e); + ips= d4.getIPv4Addresses(); + Arrays.sort(ips); + assertArrayEquals(new Integer[] { 2, 42 }, ips); + + // add a couple more IPs + Entity e2f = new Entity(2L, (short)2, 4242, 2L, 5, new Date(8000)); + d2 = deviceManager.learnDeviceByEntity(e2f); + ips= d2.getIPv4Addresses(); + Arrays.sort(ips); + assertArrayEquals(new Integer[] { 22, 42, 4242 }, ips); + Entity e4f = new Entity(4L, (short)4, 4242, 4L, 8, new Date(9000)); + d4 = deviceManager.learnDeviceByEntity(e4f); + ips= d4.getIPv4Addresses(); + Arrays.sort(ips); + assertArrayEquals(new Integer[] { 2, 42, 4242 }, ips); + } } diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c6c9c7329f3ea95204c744a4bd8ec16a2340ad19 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java @@ -0,0 +1,167 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* +* 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.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Iterator; + +import org.junit.Test; +import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; +import junit.framework.TestCase; + +/** + * + * @author gregor + * + */ +public class DeviceUniqueIndexTest extends TestCase { + protected Entity e1a; + protected Entity e1b; + protected Device d1; + protected Entity e2; + protected Entity e2alt; + protected Entity e3; + protected Entity e4; + + @Override + protected void setUp() throws Exception { + super.setUp(); + e1a = new Entity(1L, (short)1, 1, 1L, 1, new Date()); + e1b = new Entity(1L, (short)2, 1, 1L, 1, new Date()); + List<Entity> d1Entities = new ArrayList<Entity>(2); + d1Entities.add(e1a); + d1Entities.add(e1b); + d1 = new Device(null, Long.valueOf(1), d1Entities, null); + + // e2 and e2 alt match in MAC and VLAN + e2 = new Entity(2L, (short)2, 2, 2L, 2, new Date()); + e2alt = new Entity(2, (short)2, null, null, null, null); + + // IP is null + e3 = new Entity(3L, (short)3, null, 3L, 3, new Date()); + + // IP and switch and port are null + e4 = new Entity(4L, (short)4, null, null, null, new Date()); + } + + /* + * Checks that the iterator it returns the elements in the Set expected + * Doesn't check how often an element is returned as long it's at least + * once + */ + protected void verifyIterator(Set<Long> expected, Iterator<Long> it) { + HashSet<Long> actual = new HashSet<Long>(); + while (it.hasNext()) { + actual.add(it.next()); + } + assertEquals(expected, actual); + } + + @Test + public void testDeviceUniqueIndex() { + DeviceUniqueIndex idx1 = new DeviceUniqueIndex( + EnumSet.of(DeviceField.MAC, + DeviceField.VLAN)); + + idx1.updateIndex(d1, d1.getDeviceKey()); + idx1.updateIndex(e2, 2L); + + //------------- + // Test findByEntity lookups + assertEquals(Long.valueOf(1L), idx1.findByEntity(e1a)); + assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b)); + assertEquals(Long.valueOf(2L), idx1.findByEntity(e2)); + // we didn't add e2alt but since they key fields are the same we + // should find it + assertEquals(Long.valueOf(2L), idx1.findByEntity(e2alt)); + assertEquals(null, idx1.findByEntity(e3)); + assertEquals(null, idx1.findByEntity(e4)); + + //------------- + // Test getAll() + HashSet<Long> expectedKeys = new HashSet<Long>(); + expectedKeys.add(1L); + expectedKeys.add(2L); + verifyIterator(expectedKeys, idx1.getAll()); + + + //------------- + // Test queryByEntity() + verifyIterator(Collections.<Long>singleton(1L), + idx1.queryByEntity(e1a)); + verifyIterator(Collections.<Long>singleton(1L), + idx1.queryByEntity(e1b)); + verifyIterator(Collections.<Long>singleton(2L), + idx1.queryByEntity(e2)); + verifyIterator(Collections.<Long>singleton(2L), + idx1.queryByEntity(e2alt)); + assertEquals(false, idx1.queryByEntity(e3).hasNext()); + assertEquals(false, idx1.queryByEntity(e3).hasNext()); + + + //------------- + // Test removal + idx1.removeEntity(e1a, 42L); // No-op. e1a isn't mapped to this key + assertEquals(Long.valueOf(1L), idx1.findByEntity(e1a)); + idx1.removeEntity(e1a, 1L); + assertEquals(null, idx1.findByEntity(e1a)); + assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b)); + assertEquals(Long.valueOf(2L), idx1.findByEntity(e2)); + idx1.removeEntity(e2); + assertEquals(null, idx1.findByEntity(e2)); + assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b)); + + + //------------- + // Test null keys + DeviceUniqueIndex idx2 = new DeviceUniqueIndex( + EnumSet.of(DeviceField.IPV4, + DeviceField.SWITCH)); + // only one key field is null + idx2.updateIndex(e3, 3L); + assertEquals(Long.valueOf(3L), idx2.findByEntity(e3)); + e3.ipv4Address = 3; + assertEquals(null, idx2.findByEntity(e3)); + // all key fields are null + idx2.updateIndex(e4, 4L); + assertEquals(null, idx2.findByEntity(e4)); + Device d4 = new Device(null, 4L, Collections.<Entity>singleton(e4), null); + idx2.updateIndex(d4, 4L); + assertEquals(null, idx2.findByEntity(e4)); + + + + //------------- + // entity already exists with different deviceKey + DeviceUniqueIndex idx3 = new DeviceUniqueIndex( + EnumSet.of(DeviceField.MAC, + DeviceField.VLAN)); + idx3.updateIndex(e1a, 42L); + assertEquals(false, idx3.updateIndex(d1, 1L)); + // TODO: shouldn't this fail as well so that the behavior + // is consistent? + idx3.updateIndex(e1a, 1L); + // anyways. We can now add d1 ;-) + assertEquals(true, idx3.updateIndex(d1, 1L)); + } +} diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java index 47c39d176e885845b614090a1fb83bc6d0340ebe..6e91f314f5dfac482ad435209b4bea4746b07f33 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java @@ -36,19 +36,18 @@ public class MockDevice extends Device { public MockDevice(DeviceManagerImpl deviceManager, Long deviceKey, Entity entity, - Collection<IEntityClass> entityClasses) { - super(deviceManager, deviceKey, entity, entityClasses); + IEntityClass entityClass) { + super(deviceManager, deviceKey, entity, entityClass); } - public MockDevice(Device device, Entity newEntity, - Collection<IEntityClass> entityClasses) { - super(device, newEntity, entityClasses); + public MockDevice(Device device, Entity newEntity) { + super(device, newEntity); } public MockDevice(DeviceManagerImpl deviceManager, Long deviceKey, Collection<Entity> entities, - IEntityClass[] entityClasses) { - super(deviceManager, deviceKey, entities, entityClasses); + IEntityClass entityClass) { + super(deviceManager, deviceKey, 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 2502f7c04580a97a45172314aa05b7e497d2b82a..cd84a6a3c157ad3403c0d869ae25bac908a13a9f 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java @@ -7,6 +7,7 @@ import java.util.Set; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceListener; import net.floodlightcontroller.devicemanager.IEntityClass; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.devicemanager.internal.Device; import net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl; import net.floodlightcontroller.devicemanager.internal.Entity; @@ -16,6 +17,18 @@ import net.floodlightcontroller.devicemanager.internal.Entity; * @author readams */ public class MockDeviceManager extends DeviceManagerImpl { + /** + * Set a new IEntityClassifier + * Use this as a quick way to use a particular entity classifier in a + * single test without having to setup the full FloodlightModuleContext + * again. + * @param ecs + */ + public void setEntityClassifier(IEntityClassifierService ecs) { + this.entityClassifier = ecs; + this.startUp(null); + } + /** * Learn a device using the given characteristics. * @param macAddress the MAC @@ -66,21 +79,20 @@ public class MockDeviceManager extends DeviceManagerImpl { @Override protected Device allocateDevice(Long deviceKey, Entity entity, - Collection<IEntityClass> entityClasses) { - return new MockDevice(this, deviceKey, entity, entityClasses); + IEntityClass entityClass) { + return new MockDevice(this, deviceKey, entity, entityClass); } @Override protected Device allocateDevice(Long deviceKey, Collection<Entity> entities, - IEntityClass[] entityClasses) { - return new MockDevice(this, deviceKey, entities, entityClasses); + IEntityClass entityClass) { + return new MockDevice(this, deviceKey, entities, entityClass); } @Override protected Device allocateDevice(Device device, - Entity entity, - Collection<IEntityClass> entityClasses) { - return new MockDevice(device, entity, entityClasses); + Entity entity) { + return new MockDevice(device, entity); } } diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java new file mode 100644 index 0000000000000000000000000000000000000000..679dc9a4eb7c922329738479bea251a1bdc873c5 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java @@ -0,0 +1,47 @@ +package net.floodlightcontroller.devicemanager.test; + +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.MAC; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.PORT; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.SWITCH; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.VLAN; + +import java.util.EnumSet; + +import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.IEntityClass; +import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; +import net.floodlightcontroller.devicemanager.internal.Entity; + +/** A simple IEntityClassifier. Useful for tests that need IEntityClassifiers + * and IEntityClass'es with switch and/or port key fields + */ +public class MockEntityClassifier extends DefaultEntityClassifier { + public static class TestEntityClass implements IEntityClass { + @Override + public EnumSet<DeviceField> getKeyFields() { + return EnumSet.of(MAC, VLAN, SWITCH, PORT); + } + + @Override + public String getName() { + return "TestEntityClass"; + } + } + public static IEntityClass testEC = + new MockEntityClassifier.TestEntityClass(); + + @Override + public IEntityClass classifyEntity(Entity entity) { + if (entity.getSwitchDPID() >= 10L) { + return testEC; + } + return DefaultEntityClassifier.entityClass; + } + + @Override + public EnumSet<IDeviceService.DeviceField> getKeyFields() { + return EnumSet.of(MAC, VLAN, SWITCH, PORT); + } + +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java new file mode 100644 index 0000000000000000000000000000000000000000..768cc503e6795095df69923d95b995c2b49fdfd0 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java @@ -0,0 +1,55 @@ +package net.floodlightcontroller.devicemanager.test; + +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.MAC; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.PORT; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.SWITCH; +import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.VLAN; + +import java.util.EnumSet; + +import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.IEntityClass; +import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; +import net.floodlightcontroller.devicemanager.internal.Entity; + +/** A simple IEntityClassifier. Useful for tests that need an IEntityClassifier + * with switch/port as key fields. + */ +public class MockEntityClassifierMac extends DefaultEntityClassifier { + public static class TestEntityClassMac implements IEntityClass { + protected String name; + public TestEntityClassMac(String name) { + this.name = name; + } + + @Override + public EnumSet<DeviceField> getKeyFields() { + return EnumSet.of(MAC, VLAN); + } + + @Override + public String getName() { + return name; + } + } + public static IEntityClass testECMac1 = + new MockEntityClassifierMac.TestEntityClassMac("testECMac1"); + public static IEntityClass testECMac2 = + new MockEntityClassifierMac.TestEntityClassMac("testECMac2"); + + @Override + public IEntityClass classifyEntity(Entity entity) { + if (entity.getSwitchDPID() == 1L) { + return testECMac1; + } else if (entity.getSwitchDPID() == 2L) { + return testECMac2; + } + return DefaultEntityClassifier.entityClass; + } + + @Override + public EnumSet<IDeviceService.DeviceField> getKeyFields() { + return EnumSet.of(MAC, VLAN, SWITCH, PORT); + } +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 96f1a2bc3b45ae0c348762e0da30a68bdd4daf41..0d850a789fccbb818ffbe65c9a587e163c321198 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -38,11 +38,13 @@ import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.core.test.MockThreadPoolService; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; import net.floodlightcontroller.devicemanager.test.MockDeviceManager; import net.floodlightcontroller.counter.CounterStore; import net.floodlightcontroller.counter.ICounterStoreService; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.packet.Data; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; @@ -103,6 +105,7 @@ public class ForwardingTest extends FloodlightTestCase { flowReconcileMgr = new FlowReconcileManager(); routingEngine = createMock(IRoutingService.class); topology = createMock(ITopologyService.class); + DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); FloodlightModuleContext fmc = new FloodlightModuleContext(); @@ -114,15 +117,18 @@ public class ForwardingTest extends FloodlightTestCase { fmc.addService(ICounterStoreService.class, new CounterStore()); fmc.addService(IDeviceService.class, deviceManager); fmc.addService(IFlowReconcileService.class, flowReconcileMgr); + fmc.addService(IEntityClassifierService.class, entityClassifier); threadPool.init(fmc); forwarding.init(fmc); deviceManager.init(fmc); flowReconcileMgr.init(fmc); + entityClassifier.init(fmc); threadPool.startUp(fmc); deviceManager.startUp(fmc); forwarding.startUp(fmc); flowReconcileMgr.startUp(fmc); + entityClassifier.startUp(fmc); // Mock switches sw1 = EasyMock.createNiceMock(IOFSwitch.class); diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java index bcf0ac2be74d8a392ef4d363ce4f1d9e486e81a5..824fe03375b25df9d6335b28e4cb287e106ecec2 100644 --- a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java +++ b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java @@ -22,6 +22,8 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockThreadPoolService; import net.floodlightcontroller.core.test.PacketFactory; import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; import net.floodlightcontroller.devicemanager.test.MockDeviceManager; import net.floodlightcontroller.flowcache.FlowReconcileManager; import net.floodlightcontroller.flowcache.IFlowReconcileService; @@ -88,16 +90,19 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { FlowReconcileManager frm = new FlowReconcileManager(); MockThreadPoolService tps = new MockThreadPoolService(); vns = new VirtualNetworkFilter(); + DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); fmc.addService(IRestApiService.class, restApi); fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); fmc.addService(IDeviceService.class, deviceService); fmc.addService(IFlowReconcileService.class, frm); fmc.addService(IThreadPoolService.class, tps); + fmc.addService(IEntityClassifierService.class, entityClassifier); tps.init(fmc); frm.init(fmc); deviceService.init(fmc); restApi.init(fmc); getMockFloodlightProvider().init(fmc); + entityClassifier.init(fmc); tps.startUp(fmc); vns.init(fmc); frm.startUp(fmc); @@ -105,6 +110,7 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { restApi.startUp(fmc); getMockFloodlightProvider().startUp(fmc); vns.startUp(fmc); + entityClassifier.startUp(fmc); // Mock switches //fastWilcards mocked as this constant