diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 84e43ee293d7dffc69876caac46914d039535644..d0b24f272a3e7d8fec9a4b6b45f24a0594372f0f 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -874,7 +874,7 @@ public class Controller @Override public Map<Long, IOFSwitch> getSwitches() { - return this.switches; + return Collections.unmodifiableMap(this.switches); } @Override diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerAware.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerAware.java index 7be350ea17a113c78d6c557ee6af7363cd52933b..cbd9b4512eb48ed83f001967377ba714027688ec 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerAware.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerAware.java @@ -51,7 +51,7 @@ public interface IDeviceManagerAware { * * @param device the device that changed */ - public void deviceNetworkAddressChanged(IDevice device); + public void deviceIPV4AddrChanged(IDevice device); /** * Called when a VLAN tag for the device has been added or removed diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerService.java index bc603ff1e37c9989659d8cb8e6cb9ddf9342f009..c3ff4eb00e57c5c920e453c89465152e7209eb1d 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceManagerService.java @@ -18,6 +18,7 @@ package net.floodlightcontroller.devicemanager; import java.util.Collection; +import java.util.EnumSet; import java.util.Iterator; import net.floodlightcontroller.core.FloodlightContextStore; @@ -121,11 +122,11 @@ public interface IDeviceManagerService extends IFloodlightService { * @param unique set to true if only the set of fields specified should be * unique. If multiple devices would match the field, the devices will be * updated so that only one will match. - * @param fields the set of fields on which to index + * @param keyFields the set of fields on which to index */ public void addIndex(boolean perClass, boolean unique, - DeviceField... fields); + EnumSet<DeviceField> keyFields); /** * Find devices that match the provided query. Any fields that are diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java index 54b0d79e35e4bdd27f1d379e0bc308b08c8ad338..5696a766b240587bc6e60ae715e77678ee555911 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java @@ -18,9 +18,8 @@ package net.floodlightcontroller.devicemanager; import java.util.Collection; -import java.util.Set; - -import net.floodlightcontroller.devicemanager.internal.Device; +import java.util.EnumSet; +import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; import net.floodlightcontroller.devicemanager.internal.Entity; /** @@ -67,7 +66,7 @@ public interface IEntityClassifier { * @see {@link IEntityClass#getKeyFields()} * @see {@link IEntityClassifier#classifyEntity} */ - Set<IDeviceManagerService.DeviceField> getKeyFields(); + EnumSet<DeviceField> getKeyFields(); /** * Reclassify the given entity into a class. When reclassifying entities, @@ -84,7 +83,7 @@ public interface IEntityClassifier { * @param entity the entity to reclassify * @return the IEntityClass resulting from the classification */ - Collection<IEntityClass> reclassifyEntity(Device curDevice, + Collection<IEntityClass> reclassifyEntity(IDevice curDevice, Entity entity); /** @@ -99,7 +98,8 @@ public interface IEntityClassifier { * @param newDevices all the new devices derived from the entities of the * old device. If null, the old device was unchanged. */ - void deviceUpdate(Device oldDevice, Collection<Device> newDevices); + void deviceUpdate(IDevice oldDevice, + Collection<? extends IDevice> newDevices); } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java index 327ae99863943d0602a6e07fd6b85828d79265cf..5d178bf64ccb1c64a2b4c00c8501cee4a7883307 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java @@ -17,10 +17,11 @@ package net.floodlightcontroller.devicemanager.internal; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; +import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceManagerService; import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; import net.floodlightcontroller.devicemanager.IEntityClass; @@ -50,8 +51,7 @@ public class DefaultEntityClassifier implements IEntityClassifier { public static Collection<IEntityClass> entityClasses; static { - entityClasses = new ArrayList<IEntityClass>(1); - entityClasses.add(entityClass); + entityClasses = Arrays.asList(entityClass); } @Override @@ -60,14 +60,14 @@ public class DefaultEntityClassifier implements IEntityClassifier { } @Override - public Collection<IEntityClass> reclassifyEntity(Device curDevice, + public Collection<IEntityClass> reclassifyEntity(IDevice curDevice, Entity entity) { return entityClasses; } @Override - public void deviceUpdate(Device oldDevice, - Collection<Device> newDevices) { + public void deviceUpdate(IDevice oldDevice, + Collection<? extends IDevice> newDevices) { // 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 26b1a09c1e0f9fa809f68cac5abbf5aeaefa3921..f1eb3e991aa1a8b005085d1cb46cbf74c306ffc7 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -204,10 +204,10 @@ public class Device implements IDevice { /** * Check whether the device contains the specified entity * @param entity the entity to search for - * @return true if the entity is in the device, or false otherwise + * @return true the index of the entity, or <0 if not found */ - public boolean containsEntity(Entity entity) { - return (0 <= Arrays.binarySearch(entities, entity)); + public int containsEntity(Entity entity) { + return Arrays.binarySearch(entities, entity); } // ****** diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..418c2170333f231f23db1c9f917674d497016ba9 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java @@ -0,0 +1,107 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.devicemanager.internal; + +import java.util.EnumSet; +import java.util.Iterator; + +import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; + +/** + * An index that maps key fields of an entity to device keys + */ +public abstract class DeviceIndex { + /** + * The key fields for this index + */ + protected EnumSet<DeviceField> keyFields; + + /** + * Construct a new device index using the provided key fields + * @param keyFields the key fields to use + */ + public DeviceIndex(EnumSet<DeviceField> keyFields) { + super(); + this.keyFields = keyFields; + } + + /** + * Get a device index instance, which is either a multi-values or + * single-valued index. + * @param keyFields the key fields for the index + * @param unique true if this index should allow just one value per + * compound key + * @return the newly-allocated index + */ + public static DeviceIndex getInstance(EnumSet<DeviceField> keyFields, + boolean unique) { + DeviceIndex di; + if (unique) { + di = new DeviceUniqueIndex(keyFields); + } else { + di = new DeviceMultiIndex(keyFields); + } + return di; + } + + /** + * Find all device keys in the index that match the given entity + * on all the key fields for this index + * @param e the entity to search for + * @return an iterator over device keys + */ + public abstract Iterator<Long> queryByEntity(Entity entity); + + /** + * Get all device keys in the index. If certain devices exist + * multiple times, then these devices may be returned multiple times + * @return an iterator over device keys + */ + public abstract Iterator<Long> getAll(); + + /** + * Attempt to update an index with the entities in the provided + * {@link Device}. If the update fails because of a concurrent update, + * will return false. + * @param device the device to update + * @param deviceKey the device key for the device + * @return true if the update succeeded, false otherwise. + */ + public abstract boolean updateIndex(Device device, Long deviceKey); + + /** + * Add a mapping from the given entity to the given device key. This + * update will not fail because of a concurrent update + * @param device the device to update + * @param deviceKey the device key for the device + */ + public abstract void updateIndex(Entity entity, Long deviceKey); + + /** + * Remove the entry for the given entity + * @param entity the entity to remove + */ + public abstract void removeEntity(Entity entity); + + /** + * Remove the given device key from the index for the given entity + * @param entity the entity to search for + * @param deviceKey the key to remove + */ + public abstract void removeEntity(Entity entity, Long deviceKey); +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java new file mode 100644 index 0000000000000000000000000000000000000000..2015bbe60d648d86c33a582d2c9bae9b5274e5c8 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java @@ -0,0 +1,59 @@ +/** +* Copyright 2012, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.devicemanager.internal; + +import java.util.Iterator; + +/** + * An iterator for handling device index queries + */ +public class DeviceIndexInterator implements Iterator<Device> { + private DeviceManagerImpl deviceManager; + private Iterator<Long> subIterator; + + /** + * Construct a new device index iterator referring to a device manager + * instance and an iterator over device keys + * + * @param deviceManager the device manager + * @param subIterator an iterator over device keys + */ + public DeviceIndexInterator(DeviceManagerImpl deviceManager, + Iterator<Long> subIterator) { + super(); + this.deviceManager = deviceManager; + this.subIterator = subIterator; + } + + @Override + public boolean hasNext() { + return subIterator.hasNext(); + } + + @Override + public Device next() { + Long next = subIterator.next(); + return deviceManager.deviceMap.get(next); + } + + @Override + public void remove() { + subIterator.remove(); + } + +} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java index 730f8dfc4c8d1fc82f5cb2c5addba4f10acc5cc6..0ff058d542a049cd047313109915213bad4af14d 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java @@ -21,14 +21,13 @@ import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; -import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.SwitchPort; /** * An iterator for handling device queries */ -public class DeviceIterator implements Iterator<IDevice> { +public class DeviceIterator implements Iterator<Device> { private Iterator<Device> subIterator; private IEntityClass[] entityClasses; @@ -131,9 +130,9 @@ public class DeviceIterator implements Iterator<IDevice> { } @Override - public IDevice next() { + public Device next() { if (hasNext()) { - IDevice cur = next; + Device cur = next; next = null; return cur; } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 6cdfb61ef139c3b60ecca50b92239b1b67354d94..c2fbd0059dfb8ecdccfebed873356af5e965dbbf 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -18,6 +18,7 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -25,7 +26,9 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -52,6 +55,7 @@ import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.topology.ITopologyListener; +import net.floodlightcontroller.util.MultiIterator; import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPacketIn; @@ -78,11 +82,6 @@ public class DeviceManagerImpl implements protected ITopologyService topology; protected IStorageSourceService storageSource; - /** - * Device manager event listeners - */ - protected Set<IDeviceManagerAware> deviceManagerAware; - /** * This is the master device map that maps device IDs to {@link Device} * objects. @@ -100,28 +99,26 @@ public class DeviceManagerImpl implements protected Object deviceKeyLock = new Object(); /** - * This is the primary entity index that maps entities to device IDs. - */ - protected ConcurrentHashMap<IndexedEntity, Long> primaryIndex; - - /** - * The primary key fields used in the primary index - * @see DeviceManagerImpl#primaryKeyFieldsArr + * This is the primary entity index that contains all entities */ - protected Set<IDeviceManagerService.DeviceField> primaryKeyFields; + protected DeviceUniqueIndex primaryIndex; /** - * A serialization of the primary key fields set. - * @see DeviceManagerImpl#primaryKeyFields + * This stores secondary indices over the fields in the devices */ - protected IDeviceManagerService.DeviceField[] primaryKeyFieldsArr; - + protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap; + /** * This map contains state for each of the {@ref IEntityClass} * that exist */ protected ConcurrentHashMap<IEntityClass, ClassState> classStateMap; + /** + * This is the list of indices we want on a per-class basis + */ + protected Map<EnumSet<DeviceField>, Boolean> perClassIndices; + /** * The entity classifier currently in use */ @@ -131,36 +128,75 @@ public class DeviceManagerImpl implements * Used to cache state about specific entity classes */ protected class ClassState { + /** - * True if the key field set matches the primary key fields. - */ - protected boolean keyFieldsMatchPrimary; - - /** - * An array version of the key fields + * The class index */ - protected IDeviceManagerService.DeviceField[] keyFieldsArr; + protected DeviceUniqueIndex classIndex; /** - * The class index + * This stores secondary indices over the fields in the device for the + * class */ - protected ConcurrentHashMap<IndexedEntity, Long> classIndex; + protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap; /** * Allocate a new {@link ClassState} object for the class * @param clazz the class to use for the state */ public ClassState(IEntityClass clazz) { - Set<IDeviceManagerService.DeviceField> keyFields = - clazz.getKeyFields(); - keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields); - keyFieldsArr = - new IDeviceManagerService.DeviceField[keyFields.size()]; - keyFieldsArr = keyFields.toArray(keyFieldsArr); + EnumSet<DeviceField> keyFields = clazz.getKeyFields(); + EnumSet<DeviceField> primaryKeyFields = + entityClassifier.getKeyFields(); + boolean keyFieldsMatchPrimary = + primaryKeyFields.equals(keyFields); + if (!keyFieldsMatchPrimary) - classIndex = new ConcurrentHashMap<IndexedEntity, Long>(); + classIndex = new DeviceUniqueIndex(keyFields); + + secondaryIndexMap = + new HashMap<EnumSet<DeviceField>, DeviceIndex>(); + for (Map.Entry<EnumSet<DeviceField>,Boolean> keys : + perClassIndices.entrySet()) { + secondaryIndexMap.put(keys.getKey(), + DeviceIndex.getInstance(keys.getKey(), + keys.getValue())); + } } } + + /** + * Device manager event listeners + */ + protected Set<IDeviceManagerAware> deviceListeners; + + /** + * A device update event to be dispatched + */ + protected static class DeviceUpdate { + /** + * The affected device + */ + protected IDevice device; + + /** + * True if the device was added + */ + protected boolean added; + + /** + * If not added, then this is the list of fields changed + */ + protected EnumSet<DeviceField> fieldsChanged; + + public DeviceUpdate(IDevice device, boolean added, + EnumSet<DeviceField> fieldsChanged) { + super(); + this.device = device; + this.added = added; + this.fieldsChanged = fieldsChanged; + } + } // ********************* // IDeviceManagerService @@ -207,27 +243,54 @@ public class DeviceManagerImpl implements @Override public void addIndex(boolean perClass, boolean unique, - DeviceField... fields) { - // TODO Auto-generated method stub - + EnumSet<DeviceField> keyFields) { + if (perClass) { + perClassIndices.put(keyFields, unique); + } else { + secondaryIndexMap.put(keyFields, + DeviceIndex.getInstance(keyFields, unique)); + } } - + @Override public Iterator<? extends IDevice> queryDevices(Long macAddress, Short vlan, Integer ipv4Address, Long switchDPID, Integer switchPort) { - // XXX - TODO ... umm, should probably actually have indices :-) + DeviceIndex index = null; + if (secondaryIndexMap.size() > 0) { + EnumSet<DeviceField> keys = + getEntityKeys(macAddress, vlan, ipv4Address, + switchDPID, switchPort); + index = secondaryIndexMap.get(keys); + } + + Iterator<Device> deviceIterator = null; + if (index == null) { + // Do a full table scan + deviceIterator = deviceMap.values().iterator(); + } else { + // index lookup + Entity entity = new Entity((macAddress == null ? 0 : macAddress), + vlan, + ipv4Address, + switchDPID, + switchPort, + null); + deviceIterator = + new DeviceIndexInterator(this, index.queryByEntity(entity)); + } + DeviceIterator di = - new DeviceIterator(deviceMap.values().iterator(), + new DeviceIterator(deviceIterator, null, macAddress, vlan, ipv4Address, switchDPID, switchPort); - return di; + return di; } @Override @@ -237,29 +300,61 @@ public class DeviceManagerImpl implements Integer ipv4Address, Long switchDPID, Integer switchPort) { - // XXX - TODO ... umm, should probably actually have indices :-) - DeviceIterator di = new DeviceIterator(deviceMap.values().iterator(), - reference.getEntityClasses(), - macAddress, - vlan, - ipv4Address, - switchDPID, - switchPort); - return di; + IEntityClass[] entityClasses = reference.getEntityClasses(); + 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; + 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()); + } + } 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()); } @Override public void addListener(IDeviceManagerAware listener) { - deviceManagerAware.add(listener); + deviceListeners.add(listener); } @Override public void setEntityClassifier(IEntityClassifier classifier) { entityClassifier = classifier; - primaryKeyFields = classifier.getKeyFields(); - primaryKeyFieldsArr = - new IDeviceManagerService.DeviceField[primaryKeyFields.size()]; - primaryKeyFieldsArr = primaryKeyFields.toArray(primaryKeyFieldsArr); + primaryIndex = new DeviceUniqueIndex(classifier.getKeyFields()); + secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>(); } @Override @@ -268,6 +363,20 @@ public class DeviceManagerImpl implements // TODO Auto-generated method stub } + // ************* + // IInfoProvider + // ************* + + @Override + public Map<String, Object> getInfo(String type) { + if (!"summary".equals(type)) + return null; + + Map<String, Object> info = new HashMap<String, Object>(); + info.put("# hosts", deviceMap.size()); + return info; + } + // ****************** // IOFMessageListener // ****************** @@ -385,7 +494,9 @@ public class DeviceManagerImpl implements @Override public void init(FloodlightModuleContext fmc) { - this.deviceManagerAware = new HashSet<IDeviceManagerAware>(); + this.perClassIndices = + new HashMap<EnumSet<DeviceField>, Boolean>(); + this.deviceListeners = new HashSet<IDeviceManagerAware>(); this.floodlightProvider = fmc.getServiceImpl(IFloodlightProviderService.class); @@ -399,9 +510,7 @@ public class DeviceManagerImpl implements public void startUp(FloodlightModuleContext fmc) { if (entityClassifier == null) setEntityClassifier(new DefaultEntityClassifier()); - deviceMap = new ConcurrentHashMap<Long, Device>(); - primaryIndex = new ConcurrentHashMap<IndexedEntity, Long>(); classStateMap = new ConcurrentHashMap<IEntityClass, ClassState>(); @@ -409,7 +518,7 @@ public class DeviceManagerImpl implements // Register to get updates from topology topology.addListener(this); } else { - logger.error("Could add not toplogy listener"); + logger.error("Could not add topology listener"); } floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); @@ -438,56 +547,47 @@ public class DeviceManagerImpl implements */ protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { - try { - Ethernet eth = - IFloodlightProviderService.bcStore. - get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - - // Extract source entity information - Entity srcEntity = - getSourceEntityFromPacket(eth, sw, pi.getInPort()); - if (srcEntity == null) - return Command.STOP; - - if (isGratArp(eth) || - isBroadcastDHCPReq(eth)) { - // XXX - TODO - Clear attachment points from other clusters - } - - // Learn/lookup device information - Device srcDevice = learnDeviceByEntity(srcEntity); - if (srcDevice == null) - return Command.STOP; - - // Store the source device in the context - fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice); - - // Find the device matching the destination from the entity - // classes of the source. - Entity dstEntity = getDestEntityFromPacket(eth); - if (dstEntity != null) { - Device dstDevice = - findDestByEntity(srcDevice, dstEntity); - if (dstDevice != null) - fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); - } - - return Command.CONTINUE; + Ethernet eth = + IFloodlightProviderService.bcStore. + get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - } finally { - processUpdates(); - } - } - - private void processUpdates() { - // XXX - TODO + // Extract source entity information + Entity srcEntity = + getSourceEntityFromPacket(eth, sw, pi.getInPort()); + if (srcEntity == null) + return Command.STOP; + + if (isGratArp(eth) || + isBroadcastDHCPReq(eth)) { + // XXX - TODO - Clear attachment points from other clusters + } + + // Learn/lookup device information + Device srcDevice = learnDeviceByEntity(srcEntity); + if (srcDevice == null) + return Command.STOP; + + // Store the source device in the context + fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice); + + // Find the device matching the destination from the entity + // classes of the source. + Entity dstEntity = getDestEntityFromPacket(eth); + if (dstEntity != null) { + Device dstDevice = + findDestByEntity(srcDevice, dstEntity); + if (dstDevice != null) + fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); + } + + return Command.CONTINUE; } /** * Check whether the port is a physical port. We should not learn * attachment points on "special" ports. * @param port the port to check - * @return + * @return true if the port is a valid input port */ private boolean isValidInputPort(short port) { return ((int)port & 0xff00) != 0xff00 || @@ -603,13 +703,10 @@ public class DeviceManagerImpl implements * @return The {@link Device} object if found */ protected Device findDeviceByEntity(Entity entity) { - IndexedEntity ie = new IndexedEntity(primaryKeyFieldsArr, entity); - Long deviceKey = primaryIndex.get(ie); - if (deviceKey == null) - return null; + Long deviceKey = primaryIndex.findByEntity(entity); + if (deviceKey == null) return null; return deviceMap.get(deviceKey); - } - + } /** * Get a destination device using entity fields that corresponds with @@ -667,7 +764,7 @@ public class DeviceManagerImpl implements // XXX - TODO throw new UnsupportedOperationException(); } - + /** * Look up a {@link Device} based on the provided {@link Entity}. Also * learns based on the new entity, and will update existing devices as @@ -677,8 +774,8 @@ public class DeviceManagerImpl implements * @return The {@link Device} object if found */ protected Device learnDeviceByEntity(Entity entity) { - IndexedEntity ie = new IndexedEntity(primaryKeyFieldsArr, entity); ArrayList<Long> deleteQueue = null; + LinkedList<DeviceUpdate> deviceUpdates = null; Device device = null; // we may need to restart the learning process if we detect @@ -686,9 +783,11 @@ public class DeviceManagerImpl implements // one thread should always succeed so we don't get into infinite // starvation loops while (true) { + deviceUpdates = null; + // Look up the fully-qualified entity to see if it already // exists in the primary entity index. - Long deviceKey = primaryIndex.get(ie); + Long deviceKey = primaryIndex.findByEntity(entity); Collection<IEntityClass> classes = null; if (deviceKey == null) { @@ -701,10 +800,8 @@ public class DeviceManagerImpl implements ClassState classState = getClassState(clazz); if (classState.classIndex != null) { - IndexedEntity sie = - new IndexedEntity(classState.keyFieldsArr, - entity); - deviceKey = classState.classIndex.get(sie); + deviceKey = + classState.classIndex.findByEntity(entity); } } } @@ -734,11 +831,20 @@ public class DeviceManagerImpl implements deleteQueue.add(deviceKey); continue; } + + updateSecondaryIndices(entity, classes, deviceKey); + + deviceUpdates = + updateUpdates(deviceUpdates, + new DeviceUpdate(device, true, null)); + break; } - if (device.containsEntity(entity)) { - // XXX - TODO - Update entity timestamp + int entityindex = -1; + if ((entityindex = device.containsEntity(entity)) >= 0) { + // update timestamp on the found entity + device.entities[entityindex].setLastSeenTimestamp(new Date()); break; } else { // When adding an entity, any existing entities on the @@ -752,6 +858,9 @@ public class DeviceManagerImpl implements Entity[] entities = device.getEntities(); ArrayList<Entity> newEntities = null; ArrayList<Entity> removedEntities = null; + EnumSet<DeviceField> changedFields = + findChangedFields(device, entity); + // iterate backwards since we want indices for removed entities // to update in reverse order for (int i = entities.length - 1; i >= 0; i--) { @@ -767,9 +876,13 @@ public class DeviceManagerImpl implements if (edpid != null && cdpid != null && topology != null && topology.inSameCluster(edpid, cdpid)) { + // XXX - TODO don't delete entities; we should just + // filter out the attachment points on read removedEntities = updateEntityList(removedEntities, entities[i]); + changedFields.add(DeviceField.SWITCH); + Entity shim = makeShimEntity(entity, entities[i], device.getEntityClasses()); newEntities = updateEntityList(newEntities, shim); @@ -783,6 +896,20 @@ public class DeviceManagerImpl implements Device newDevice = new Device(device, newEntities, classes); + + updateSecondaryIndices(entity, + newDevice.getEntityClasses(), + deviceKey); + for (Entity e : newEntities) { + updateSecondaryIndices(e, + newDevice.getEntityClasses(), + deviceKey); + } + + deviceUpdates = + updateUpdates(deviceUpdates, + new DeviceUpdate(device, false, + changedFields)); boolean res = deviceMap.replace(deviceKey, device, newDevice); // If replace returns false, restart the process from the @@ -799,14 +926,86 @@ public class DeviceManagerImpl implements break; } } - + if (deleteQueue != null) { for (Long l : deleteQueue) { deviceMap.remove(l); } } + + processUpdates(deviceUpdates); + return device; } + + protected EnumSet<DeviceField> findChangedFields(Device device, + Entity newEntity) { + EnumSet<DeviceField> changedFields = + EnumSet.of(DeviceField.IPV4, + DeviceField.VLAN, + DeviceField.SWITCH); + + if (newEntity.getIpv4Address() == null) + changedFields.remove(DeviceField.IPV4); + if (newEntity.getVlan() == null) + changedFields.remove(DeviceField.VLAN); + if (newEntity.getSwitchDPID() == null || + newEntity.getSwitchPort() == null) + changedFields.remove(DeviceField.SWITCH); + + if (changedFields.size() == 0) return changedFields; + + for (Entity entity : device.getEntities()) { + if (newEntity.getIpv4Address() != null && + entity.getIpv4Address() != null && + entity.getIpv4Address().equals(newEntity.getIpv4Address())) + changedFields.remove(DeviceField.IPV4); + if (newEntity.getVlan() != null && + entity.getVlan() != null && + entity.getVlan().equals(newEntity.getVlan())) + changedFields.remove(DeviceField.VLAN); + if (newEntity.getSwitchDPID() != null && + entity.getSwitchDPID() != null && + newEntity.getSwitchPort() != null && + entity.getSwitchPort() != null && + entity.getSwitchDPID().equals(newEntity.getSwitchDPID()) && + entity.getSwitchPort().equals(newEntity.getSwitchPort())) + changedFields.remove(DeviceField.SWITCH); + } + + return changedFields; + } + + /** + * Send update notifications to listeners + * @param updates the updates to process. + */ + protected void processUpdates(Queue<DeviceUpdate> updates) { + if (updates == null) return; + DeviceUpdate update = null; + while (null != (update = updates.poll())) { + for (IDeviceManagerAware listener : deviceListeners) { + if (update.added) { + listener.deviceAdded(update.device); + } else { + for (DeviceField field : update.fieldsChanged) { + switch (field) { + case IPV4: + listener.deviceIPV4AddrChanged(update.device); + break; + case SWITCH: + case PORT: + listener.deviceMoved(update.device); + break; + case VLAN: + listener.deviceVlanChanged(update.device); + break; + } + } + } + } + } + } /** * If there's information that would be lost about a device because @@ -873,6 +1072,16 @@ public class DeviceManagerImpl implements return list; } + private LinkedList<DeviceUpdate> + updateUpdates(LinkedList<DeviceUpdate> list, DeviceUpdate update) { + if (update == null) return list; + if (list == null) + list = new LinkedList<DeviceUpdate>(); + list.add(update); + + return list; + } + /** * Get the secondary index for a class. Will return null if the * secondary index was created concurrently in another thread. @@ -894,24 +1103,21 @@ public class DeviceManagerImpl implements /** * Update both the primary and class indices for the provided device. - * If the update fails because of a concurrent update, will return false. + * If the update fails because of aEn concurrent update, will return false. * @param device the device to update * @param deviceKey the device key for the device * @return true if the update succeeded, false otherwise. */ private boolean updateIndices(Device device, Long deviceKey) { - if (!updateIndex(device, deviceKey, - primaryIndex, primaryKeyFieldsArr)) { + if (!primaryIndex.updateIndex(device, deviceKey)) { return false; } for (IEntityClass clazz : device.getEntityClasses()) { ClassState classState = getClassState(clazz); if (classState.classIndex != null) { - if (!updateIndex(device, - deviceKey, - classState.classIndex, - classState.keyFieldsArr)) + if (!classState.classIndex.updateIndex(device, + deviceKey)) return false; } } @@ -919,62 +1125,69 @@ public class DeviceManagerImpl implements 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 deviceKey the device key to set up + */ + private void updateSecondaryIndices(Entity entity, + Collection<IEntityClass> entityClasses, + 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); + } + } + } + + /** + * 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); + } + private void removeEntities(ArrayList<Entity> removed, IEntityClass[] classes) { if (removed == null) return; for (Entity rem : removed) { - IndexedEntity ie = new IndexedEntity(primaryKeyFieldsArr, rem); - primaryIndex.remove(ie); + primaryIndex.removeEntity(rem); for (IEntityClass clazz : classes) { ClassState classState = getClassState(clazz); - ie = new IndexedEntity(classState.keyFieldsArr, rem); if (classState.classIndex != null) { - classState.classIndex.remove(ie); + classState.classIndex.removeEntity(rem); } } } // XXX - TODO handle indexed views into data } - - /** - * Attempt to update an index with the entities in the provided - * {@link Device}. If the update fails because of a concurrent update, - * will return false. - * @param device the device to update - * @param deviceKey the device key for the device - * @param index the index to update - * @param keyFields the key fields to use for the index - * @return true if the update succeeded, false otherwise. - */ - private boolean updateIndex(Device device, - Long deviceKey, - ConcurrentHashMap<IndexedEntity, - Long> index, - DeviceField[] keyFields) { - for (Entity e : device.entities) { - IndexedEntity ie = new IndexedEntity(keyFields, e); - Long ret = index.putIfAbsent(ie, deviceKey); - if (ret != null && !ret.equals(deviceKey)) { - // If the return value is non-null, then fail the insert - // (this implies that a device using this entity has - // already been created in another thread). - return false; - } - } - - return true; - } - - @Override - public Map<String, Object> getInfo(String type) { - if (!"summary".equals(type)) - return null; - - Map<String, Object> info = new HashMap<String, Object>(); - info.put("# hosts", deviceMap.size()); - return info; - } + private EnumSet<DeviceField> getEntityKeys(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { + EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class); + if (macAddress != null) keys.add(DeviceField.MAC); + if (vlan != null) keys.add(DeviceField.VLAN); + if (ipv4Address != null) keys.add(DeviceField.IPV4); + if (switchDPID != null) keys.add(DeviceField.SWITCH); + if (switchPort != null) keys.add(DeviceField.PORT); + return keys; + } + } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..7fd7c995d567ca6291dda9c9df231d11d9a8dece --- /dev/null +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java @@ -0,0 +1,108 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.devicemanager.internal; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; +import net.floodlightcontroller.util.IterableIterator; + +/** + * An index that maps key fields of an entity to device keys, with multiple + * device keys allowed per entity + */ +public class DeviceMultiIndex extends DeviceIndex { + /** + * The index + */ + private ConcurrentHashMap<IndexedEntity, Collection<Long>> index; + + /** + * @param keyFields + */ + public DeviceMultiIndex(EnumSet<DeviceField> keyFields) { + super(keyFields); + index = new ConcurrentHashMap<IndexedEntity, Collection<Long>>(); + } + + // *********** + // DeviceIndex + // *********** + + @Override + public Iterator<Long> queryByEntity(Entity entity) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + Collection<Long> devices = index.get(ie); + if (devices != null) + return devices.iterator(); + + return Collections.<Long>emptySet().iterator(); + } + + @Override + public Iterator<Long> getAll() { + Iterator<Collection<Long>> iter = index.values().iterator(); + return new IterableIterator<Long>(iter); + } + + @Override + public boolean updateIndex(Device device, Long deviceKey) { + for (Entity e : device.entities) { + updateIndex(e, deviceKey); + } + return true; + } + + @Override + public void updateIndex(Entity entity, Long deviceKey) { + Collection<Long> devices = null; + + IndexedEntity ie = new IndexedEntity(keyFields, entity); + if (!ie.hasNonNullKeys()) return; + + devices = index.get(ie); + if (devices == null) { + Map<Long,Boolean> chm = new ConcurrentHashMap<Long,Boolean>(); + devices = Collections.newSetFromMap(chm); + Collection<Long> r = index.putIfAbsent(ie, devices); + if (r != null) + devices = r; + } + + devices.add(deviceKey); + } + + @Override + public void removeEntity(Entity entity) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + index.remove(ie); + } + + @Override + public void removeEntity(Entity entity, Long deviceKey) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + Collection<Long> devices = index.get(ie); + if (devices != null) + devices.remove(deviceKey); + } +} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..0e1e049fbe0675dd4f967c76073d509e40e81d78 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java @@ -0,0 +1,116 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.devicemanager.internal; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; + +/** + * An index that maps key fields of an entity uniquely to a device key + */ +public class DeviceUniqueIndex extends DeviceIndex { + /** + * The index + */ + private ConcurrentHashMap<IndexedEntity, Long> index; + + /** + * Construct a new device index using the provided key fields + * @param keyFields the key fields to use + */ + public DeviceUniqueIndex(EnumSet<DeviceField> keyFields) { + super(keyFields); + index = new ConcurrentHashMap<IndexedEntity, Long>(); + } + + // *********** + // DeviceIndex + // *********** + + @Override + public Iterator<Long> queryByEntity(Entity entity) { + final Long deviceKey = findByEntity(entity); + if (deviceKey != null) + return Collections.<Long>singleton(deviceKey).iterator(); + + return Collections.<Long>emptySet().iterator(); + } + + @Override + public Iterator<Long> getAll() { + return index.values().iterator(); + } + + @Override + public boolean updateIndex(Device device, Long deviceKey) { + for (Entity e : device.entities) { + IndexedEntity ie = new IndexedEntity(keyFields, e); + if (!ie.hasNonNullKeys()) continue; + + Long ret = index.putIfAbsent(ie, deviceKey); + if (ret != null && !ret.equals(deviceKey)) { + // If the return value is non-null, then fail the insert + // (this implies that a device using this entity has + // already been created in another thread). + return false; + } + } + return true; + } + + @Override + public void updateIndex(Entity entity, Long deviceKey) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + if (!ie.hasNonNullKeys()) return; + index.put(ie, deviceKey); + } + + @Override + public void removeEntity(Entity entity) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + index.remove(ie); + } + + @Override + public void removeEntity(Entity entity, Long deviceKey) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + index.remove(ie, deviceKey); + } + + // ************** + // Public Methods + // ************** + + /** + * Look up a {@link Device} based on the provided {@link Entity}. + * @param entity the entity to search for + * @return The key for the {@link Device} object if found + */ + public Long findByEntity(Entity entity) { + IndexedEntity ie = new IndexedEntity(keyFields, entity); + Long deviceKey = index.get(ie); + if (deviceKey == null) + return null; + return deviceKey; + } + +} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 1ec3a44a6d9a03c685dc6ec0e03b96b3283474a5..574677f100a712ff172b335a87825af27234233a 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -211,7 +211,17 @@ public class Entity implements Comparable<Entity> { r = 1; else r = switchPort.compareTo(o.switchPort); - return r; + if (r != 0) return r; + + if (lastSeenTimestamp == null) + r = o.lastSeenTimestamp == null ? 0 : -1; + else if (o.lastSeenTimestamp == null) + r = 1; + else + r = lastSeenTimestamp.compareTo(o.lastSeenTimestamp); + if (r != 0) return r; + + return 0; } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java index 6b2f3ff190808434457cf0fa2c8a8899cc5b2904..3ca3551041c80cd8d9b9f4c667da1f617691782a 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java @@ -1,6 +1,9 @@ package net.floodlightcontroller.devicemanager.internal; +import java.util.EnumSet; + import net.floodlightcontroller.devicemanager.IDeviceManagerService; +import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; /** * This is a thin wrapper around {@link Entity} that allows overriding @@ -9,7 +12,7 @@ import net.floodlightcontroller.devicemanager.IDeviceManagerService; * @author readams */ public class IndexedEntity { - protected IDeviceManagerService.DeviceField[] keyFields; + protected EnumSet<DeviceField> keyFields; protected Entity entity; private int hashCode = 0; @@ -20,19 +23,45 @@ public class IndexedEntity { * {@link IndexedEntity#hashCode()} and {@link IndexedEntity#equals(Object)} * @param entity the entity to wrap */ - public IndexedEntity(IDeviceManagerService.DeviceField[] keyFields, Entity entity) { + public IndexedEntity(EnumSet<DeviceField> keyFields, Entity entity) { super(); this.keyFields = keyFields; this.entity = entity; } + /** + * Check whether this entity has non-null values in any of its key fields + * @return true if any key fields have a non-null value + */ + public boolean hasNonNullKeys() { + for (DeviceField f : keyFields) { + switch (f) { + case MAC: + return true; + case IPV4: + if (entity.ipv4Address != null) return true; + break; + case SWITCH: + if (entity.switchDPID != null) return true; + break; + case PORT: + if (entity.switchPort != null) return true; + break; + case VLAN: + if (entity.vlan != null) return true; + break; + } + } + return false; + } + @Override public int hashCode() { if (hashCode != 0) return hashCode; final int prime = 31; hashCode = 1; - for (IDeviceManagerService.DeviceField f : keyFields) { + for (DeviceField f : keyFields) { switch (f) { case MAC: hashCode = prime * hashCode diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index c294e35dd66ddba7c0a797b787c550b1216254c4..0d1878e8d4b2cea1f5a107b356112cbb032a882c 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -478,7 +478,7 @@ public abstract class ForwardingBase implements } @Override - public void deviceNetworkAddressChanged(IDevice device) { + public void deviceIPV4AddrChanged(IDevice device) { } diff --git a/src/main/java/net/floodlightcontroller/util/IterableIterator.java b/src/main/java/net/floodlightcontroller/util/IterableIterator.java new file mode 100644 index 0000000000000000000000000000000000000000..584de0865870bf02ca889a104ed2e6a58dd45297 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/IterableIterator.java @@ -0,0 +1,66 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Iterator over all values in an iterator of iterators + * + * @param <T> the type of elements returned by this iterator + */ +public class IterableIterator<T> implements Iterator<T> { + Iterator<? extends Iterable<T>> subIterator; + Iterator<T> current = null; + + public IterableIterator(Iterator<? extends Iterable<T>> subIterator) { + super(); + this.subIterator = subIterator; + } + + @Override + public boolean hasNext() { + if (current == null) { + if (subIterator.hasNext()) { + current = subIterator.next().iterator(); + } else { + return false; + } + } + while (!current.hasNext() && subIterator.hasNext()) { + current = subIterator.next().iterator(); + } + + return current.hasNext(); + } + + @Override + public T next() { + if (hasNext()) + return current.next(); + throw new NoSuchElementException(); + } + + @Override + public void remove() { + if (hasNext()) + current.remove(); + throw new NoSuchElementException(); + } +} diff --git a/src/main/java/net/floodlightcontroller/util/MultiIterator.java b/src/main/java/net/floodlightcontroller/util/MultiIterator.java new file mode 100644 index 0000000000000000000000000000000000000000..bcbc9160ac5986a4454fdca802932af5e160c247 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/MultiIterator.java @@ -0,0 +1,66 @@ +/** +* Copyright 2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Iterator over all values in an iterator of iterators + * + * @param <T> the type of elements returned by this iterator + */ +public class MultiIterator<T> implements Iterator<T> { + Iterator<Iterator<T>> subIterator; + Iterator<T> current = null; + + public MultiIterator(Iterator<Iterator<T>> subIterator) { + super(); + this.subIterator = subIterator; + } + + @Override + public boolean hasNext() { + if (current == null) { + if (subIterator.hasNext()) { + current = subIterator.next(); + } else { + return false; + } + } + while (!current.hasNext() && subIterator.hasNext()) { + current = subIterator.next(); + } + + return current.hasNext(); + } + + @Override + public T next() { + if (hasNext()) + return current.next(); + throw new NoSuchElementException(); + } + + @Override + public void remove() { + if (hasNext()) + current.remove(); + throw new NoSuchElementException(); + } +} diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 575bf5fe76870786a2e02348b1e464056a281dff..aa615fb1d5af3a9c69b2a1f83c16206df9875a71 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -18,31 +18,30 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.List; import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.createStrictMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; +import static org.easymock.EasyMock.isA; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockFloodlightProvider; import static net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField.*; +import net.floodlightcontroller.devicemanager.IDeviceManagerAware; import net.floodlightcontroller.devicemanager.IDeviceManagerService.DeviceField; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IEntityClass; -import net.floodlightcontroller.devicemanager.IEntityClassifier; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.devicemanager.IDeviceManagerService; import net.floodlightcontroller.packet.ARP; @@ -55,15 +54,12 @@ import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.memory.MemoryStorageSource; import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.topology.ITopologyService; -import net.floodlightcontroller.topology.SwitchPortTuple; - import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFType; import org.openflow.protocol.OFPacketIn.OFPacketInReason; -import org.openflow.protocol.OFPhysicalPort; /** * @@ -143,9 +139,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { @Override public Collection<IEntityClass> classifyEntity(Entity entity) { if (entity.switchDPID >= 10L) { - ArrayList<IEntityClass> l = new ArrayList<IEntityClass>(); - l.add(testEC); - return l; + return Arrays.asList(testEC); } return DefaultEntityClassifier.entityClasses; } @@ -159,6 +153,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { @Test public void testEntityLearning() throws Exception { + IDeviceManagerAware mockListener = + createStrictMock(IDeviceManagerAware.class); + + deviceManager.addListener(mockListener); deviceManager.setEntityClassifier(new TestEntityClassifier()); Entity entity1 = new Entity(1L, null, null, 1L, 1, new Date()); @@ -168,6 +166,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity entity5 = new Entity(2L, (short)4, 1, 5L, 2, new Date()); Entity entity6 = new Entity(2L, (short)4, 1, 50L, 3, new Date()); + + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + Device d1 = deviceManager.learnDeviceByEntity(entity1); assertSame(d1, deviceManager.learnDeviceByEntity(entity1)); assertSame(d1, deviceManager.findDeviceByEntity(entity1)); @@ -175,7 +177,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d1.entityClasses); assertEquals(1, deviceManager.getAllDevices().size()); - + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + Device d2 = deviceManager.learnDeviceByEntity(entity2); assertFalse(d1.equals(d2)); assertNotSame(d1, d2); @@ -183,6 +190,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d2.entityClasses); assertEquals(2, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceIPV4AddrChanged(isA(IDevice.class)); + replay(mockListener); Device d3 = deviceManager.learnDeviceByEntity(entity3); assertNotSame(d2, d3); @@ -194,7 +206,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d3.getAttachmentPoints()); assertEquals(2, deviceManager.getAllDevices().size()); - + verify(mockListener); + + reset(mockListener); + mockListener.deviceIPV4AddrChanged(isA(IDevice.class)); + replay(mockListener); Device d4 = deviceManager.learnDeviceByEntity(entity4); assertNotSame(d1, d4); @@ -206,6 +222,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d4.getAttachmentPoints()); assertEquals(2, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded((isA(IDevice.class))); + replay(mockListener); Device d5 = deviceManager.learnDeviceByEntity(entity5); assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 2) }, @@ -214,6 +235,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d5.getVlanId()); assertEquals(2L, d5.getMACAddress()); assertEquals("00:00:00:00:00:02", d5.getMACAddressString()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); Device d6 = deviceManager.learnDeviceByEntity(entity6); assertArrayEquals(new SwitchPort[] { new SwitchPort(50L, 3) }, @@ -222,10 +248,16 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d6.getVlanId()); assertEquals(4, deviceManager.getAllDevices().size()); + verify(mockListener); } @Test public void testAttachmentPointLearning() throws Exception { + IDeviceManagerAware mockListener = + createStrictMock(IDeviceManagerAware.class); + + deviceManager.addListener(mockListener); + ITopologyService mockTopology = createMock(ITopologyService.class); expect(mockTopology.inSameCluster(1L, 2L)).andReturn(true).anyTimes(); expect(mockTopology.inSameCluster(2L, 1L)).andReturn(true).anyTimes(); @@ -255,20 +287,33 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDevice d; SwitchPort[] aps; Integer[] ips; - + + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + d = deviceManager.learnDeviceByEntity(entity1); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps); ips = d.getIPv4Addresses(); assertArrayEquals(new Integer[] { 1 }, ips); - + verify(mockListener); + + reset(mockListener); + mockListener.deviceMoved((isA(IDevice.class))); + replay(mockListener); + d = deviceManager.learnDeviceByEntity(entity2); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); assertArrayEquals(new SwitchPort[] { new SwitchPort(2L, 1) }, aps); ips = d.getIPv4Addresses(); assertArrayEquals(new Integer[] { 1 }, ips); + verify(mockListener); + + reset(mockListener); + mockListener.deviceMoved((isA(IDevice.class))); + replay(mockListener); d = deviceManager.learnDeviceByEntity(entity3); assertEquals(1, deviceManager.getAllDevices().size()); @@ -277,6 +322,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { new SwitchPort(2L, 1) }, aps); ips = d.getIPv4Addresses(); assertArrayEquals(new Integer[] { 1 }, ips); + verify(mockListener); + + reset(mockListener); + mockListener.deviceMoved((isA(IDevice.class))); + replay(mockListener); d = deviceManager.learnDeviceByEntity(entity4); assertEquals(1, deviceManager.getAllDevices().size()); @@ -284,12 +334,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new SwitchPort[] { new SwitchPort(2L, 1), new SwitchPort(4L, 1) }, aps); ips = d.getIPv4Addresses(); - assertArrayEquals(new Integer[] { 1 }, ips); + assertArrayEquals(new Integer[] { 1 }, ips); + verify(mockListener); } @Test public void testPacketIn() throws Exception { - + deviceManager.addIndex(true, true, EnumSet.of(DeviceField.IPV4)); byte[] dataLayerSource = ((Ethernet)this.testPacket).getSourceMACAddress(); // Mock up our expected behavior