From e5415588f1a1ee3506a8edc362b7654d5991016d Mon Sep 17 00:00:00 2001 From: Gregor Maier <gregor.maier@bigswitch.com> Date: Thu, 28 Jun 2012 21:02:17 -0700 Subject: [PATCH] Update entity classification * IEntityClassifier only returns a single entity class per entity and all entities of a device must belong to the same entity class. Semantics for multiple classes was unclear. * IEntityClassifier is now an IFloodlightService to have a cleaner way of setting the classifier to use (the classifier needs to be set before DeviceManager.startUp() is called). [BSC-1838] --- .../devicemanager/IDevice.java | 10 +- .../devicemanager/IDeviceService.java | 25 ++-- .../devicemanager/IEntityClass.java | 8 +- ...ier.java => IEntityClassifierService.java} | 25 ++-- .../internal/DefaultEntityClassifier.java | 70 ++++++++-- .../devicemanager/internal/Device.java | 72 ++++------ .../internal/DeviceIterator.java | 23 ++- .../internal/DeviceManagerImpl.java | 131 +++++++----------- .../devicemanager/web/DeviceSerializer.java | 8 +- .../internal/DeviceManagerImplTest.java | 38 ++--- .../devicemanager/test/MockDevice.java | 13 +- .../devicemanager/test/MockDeviceManager.java | 13 +- .../forwarding/ForwardingTest.java | 6 + .../VirtualNetworkFilterTest.java | 6 + 14 files changed, 228 insertions(+), 220 deletions(-) rename src/main/java/net/floodlightcontroller/devicemanager/{IEntityClassifier.java => IEntityClassifierService.java} (83%) diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java index 4deba4b62..39bce1cf0 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java @@ -80,10 +80,10 @@ 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 + * @see IDeviceService#setEntityClassifier(IEntityClassifierService) */ - public IEntityClass[] getEntityClasses(); + public IEntityClass getEntityClass(); } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 79cb2699b..4bb6a7bad 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -67,17 +67,19 @@ 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. + * Only the key fields as defined by the {@link IEntityClassifierService} will + * be important in this search. All key fields MUST be supplied, otherwise + * use queryDevices(). * * @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) */ public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address, Long switchDPID, @@ -87,7 +89,7 @@ public interface IDeviceService extends IFloodlightService { * 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. @@ -177,16 +179,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 1053a79a1..bb077f116 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 31cf4a70d..8d693ce92 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 32bbeabb6..353a72d1b 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 447be63f3..443c5b40c 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -46,7 +46,7 @@ public class Device implements IDevice { protected DeviceManagerImpl deviceManager; protected Entity[] entities; - protected IEntityClass[] entityClasses; + protected IEntityClass entityClass; protected String macAddressString; @@ -59,19 +59,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 +79,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 +98,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 +113,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; } // ******* @@ -180,36 +170,34 @@ public class Device implements IDevice { 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(); + 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) break; } - + if (!validIP) + break; + if (validIP) vals.add(e.getIpv4Address()); } @@ -379,8 +367,8 @@ public class Device implements IDevice { // Getters/Setters // *************** - public IEntityClass[] getEntityClasses() { - return entityClasses; + public IEntityClass getEntityClass() { + return entityClass; } public Entity[] getEntities() { diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java index 795775c15..2cbea66e5 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 ab1d8788d..06000e2ea 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; + IEntityClassifierService entityClassifier; /** * Used to cache state about specific entity classes @@ -406,11 +405,10 @@ 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); + ClassState classState = getClassState(entityClass); DeviceIndex index = null; if (classState.secondaryIndexMap.size() > 0) { @@ -426,7 +424,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (index == null) { // scan all devices return new DeviceIterator(deviceMap.values().iterator(), - entityClasses, + new IEntityClass[] { entityClass }, macAddress, vlan, ipv4Address, switchDPID, switchPort); } else { @@ -446,7 +444,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { index.queryByEntity(entity)); } iterators.add(iter); - } return new MultiIterator<Device>(iterators.iterator()); } @@ -456,11 +453,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { deviceListeners.add(listener); } - @Override - public void setEntityClassifier(IEntityClassifier classifier) { - entityClassifier = classifier; - } - @Override public void flushEntityCache(IEntityClass entityClass, boolean reclassify) { @@ -622,13 +614,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>(); @@ -967,21 +957,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 +986,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { synchronized (deviceKeyLock) { deviceKey = Long.valueOf(deviceKeyCounter++); } - device = allocateDevice(deviceKey, entity, classes); + device = allocateDevice(deviceKey, entity, entityClass); // Add the new device to the primary map with a simple put deviceMap.put(deviceKey, device); @@ -1011,7 +999,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { continue; } - updateSecondaryIndices(entity, classes, deviceKey); + updateSecondaryIndices(entity, entityClass, deviceKey); // generate new device update deviceUpdates = @@ -1029,7 +1017,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 +1043,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { continue; } updateSecondaryIndices(entity, - device.getEntityClasses(), + device.getEntityClass(), deviceKey); break; } @@ -1178,7 +1166,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 +1175,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 +1241,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 +1277,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); } } @@ -1357,20 +1323,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/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java index fa9bcc793..66bdaef93 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/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 25ea828b0..82eb8c925 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -17,9 +17,7 @@ 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; @@ -46,6 +44,7 @@ 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.flowcache.FlowReconcileManager; @@ -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); @@ -229,6 +232,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public EnumSet<DeviceField> getKeyFields() { return testKeyFields; } + + @Override + public String getName() { + return "TestEntityClass"; + } } protected static IEntityClass testEC = new TestEntityClass(); @@ -236,11 +244,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public static class TestEntityClassifier extends DefaultEntityClassifier { @Override - public Collection<IEntityClass> classifyEntity(Entity entity) { + public IEntityClass classifyEntity(Entity entity) { if (entity.switchDPID >= 10L) { - return Arrays.asList(testEC); + return testEC; } - return DefaultEntityClassifier.entityClasses; + return DefaultEntityClassifier.entityClass; } @Override @@ -276,7 +284,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { createStrictMock(IDeviceListener.class); deviceManager.addListener(mockListener); - deviceManager.setEntityClassifier(new TestEntityClassifier()); + deviceManager.entityClassifier= new TestEntityClassifier(); deviceManager.startUp(null); ITopologyService mockTopology = createMock(ITopologyService.class); @@ -314,8 +322,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 +337,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d2 = deviceManager.learnDeviceByEntity(entity2); assertFalse(d1.equals(d2)); assertNotSame(d1, d2); - assertArrayEquals(new IEntityClass[]{ testEC }, - d2.entityClasses); + assertEquals(testEC, d2.entityClass); assertArrayEquals(new Short[] { -1 }, d2.getVlanId()); assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses()); @@ -343,8 +350,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d3 = deviceManager.learnDeviceByEntity(entity3); assertNotSame(d2, d3); - assertArrayEquals(new IEntityClass[]{ testEC }, - d3.entityClasses); + assertEquals(testEC, d3.entityClass); assertArrayEquals(new Integer[] { 1 }, d3.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) }, @@ -363,8 +369,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d4 = deviceManager.learnDeviceByEntity(entity4); assertNotSame(d1, d4); - assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass }, - d4.entityClasses); + assertEquals(DefaultEntityClassifier.entityClass, d4.entityClass); assertArrayEquals(new Integer[] { 1 }, d4.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, @@ -699,7 +704,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { 1L, 1, currentDate), - DefaultEntityClassifier.entityClasses); + DefaultEntityClassifier.entityClass); expect(mockTopology.isAllowed(EasyMock.anyLong(), EasyMock.anyShort())). @@ -738,8 +743,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { ipaddr, 5L, 2, - currentDate), - DefaultEntityClassifier.entityClasses); + currentDate)); reset(mockTopology); expect(mockTopology.isAttachmentPointPort(anyLong(), diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java index 47c39d176..6e91f314f 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 2502f7c04..24379b3d8 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java @@ -66,21 +66,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/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 96f1a2bc3..0d850a789 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 bcf0ac2be..824fe0337 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 -- GitLab