diff --git a/.gitignore b/.gitignore index aeb0dd73cfa475e0201e0be90a255c6240b0ba04..ad6e64ae7652599743f9625971681d5529b7b232 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ target thrift logback.xml +*.swp +*.pyc diff --git a/build.xml b/build.xml index e985d6fbd58f05c5c38e79c3cf09f19c9fd7c1c3..61017da6f4543e6b90f1b95622e0fd304c41c636 100644 --- a/build.xml +++ b/build.xml @@ -173,6 +173,7 @@ <pathelement location="${build-coverage}"/> <pathelement location="${build}"/> <pathelement location="${build-test}"/> + <pathelement location="${floodlight-jar}"/> <path refid="classpath-test"/> </classpath> <formatter type="brief" usefile="true" /> @@ -181,6 +182,7 @@ <exclude name="**/storage/tests/StorageTest.java"/> <exclude name="**/test/Mock*"/> <exclude name="**/core/test/**"/> + <exclude name="**/core/module/**"/> </fileset> </batchtest> </junit> diff --git a/floodlight.sh b/floodlight.sh index 04e9afb9218de6f38cd62f6d5eee14bade1f8037..e67c6a7c13895b2c609a0040b4fa755dd5dee618 100755 --- a/floodlight.sh +++ b/floodlight.sh @@ -12,6 +12,7 @@ JVM_OPTS="$JVM_OPTS -Xmx2g -Xms2g -Xmn800m" JVM_OPTS="$JVM_OPTS -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods" JVM_OPTS="$JVM_OPTS -XX:InlineSmallCode=8192 -XX:MaxInlineSize=8192 -XX:FreqInlineSize=8192" JVM_OPTS="$JVM_OPTS -XX:CompileThreshold=1500 -XX:PreBlockSpin=8" +JVM_OPTS="$JVM_OPTS -Dpython.security.respectJavaAccessibility=false" # Create a logback file if required [ -f ${FL_LOGBACK} ] || cat <<EOF_LOGBACK >${FL_LOGBACK} diff --git a/src/main/images/Floodlight Icons.sketch/Data b/src/main/images/Floodlight Icons.sketch/Data index bb5c893b9e5d4172708bd150a5c1137548333db9..0fbceabe0c97c926b15762291f968aca11db5a96 100644 Binary files a/src/main/images/Floodlight Icons.sketch/Data and b/src/main/images/Floodlight Icons.sketch/Data differ diff --git a/src/main/images/Floodlight Icons.sketch/QuickLook/Preview.png b/src/main/images/Floodlight Icons.sketch/QuickLook/Preview.png index 77fc493ec2b292f44d48e5a360f0459b45dab267..4a36e7cbb504eacc1a3a70237183873e35cbb61c 100644 Binary files a/src/main/images/Floodlight Icons.sketch/QuickLook/Preview.png and b/src/main/images/Floodlight Icons.sketch/QuickLook/Preview.png differ diff --git a/src/main/images/Floodlight Icons.sketch/QuickLook/Thumbnail.png b/src/main/images/Floodlight Icons.sketch/QuickLook/Thumbnail.png index 942c917bac3f2f0d351c47e4ad18001e31c4132d..50ebe22932430361c768192df9e1f4a2e3c2423a 100644 Binary files a/src/main/images/Floodlight Icons.sketch/QuickLook/Thumbnail.png and b/src/main/images/Floodlight Icons.sketch/QuickLook/Thumbnail.png differ diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java index ec443e2076057ee754182dfa8bdff12bfe47d993..46a9b0e981d9e4ed2fd3a70834a4418e2b7722ac 100644 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java @@ -11,11 +11,13 @@ import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Queue; +import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; @@ -75,9 +77,20 @@ public class FloodlightModuleLoader { ServiceLoader<IFloodlightModule> moduleLoader = ServiceLoader.load(IFloodlightModule.class, cl); // Iterate for each module, iterate through and add it's services - for (IFloodlightModule m : moduleLoader) { - if (logger.isTraceEnabled()) { - logger.trace("Found module " + m.getClass().getName()); + Iterator<IFloodlightModule> moduleIter = moduleLoader.iterator(); + while (moduleIter.hasNext()) { + IFloodlightModule m = null; + try { + m = moduleIter.next(); + } catch (ServiceConfigurationError sce) { + logger.debug("Could not find module"); + //moduleIter.remove(); + continue; + } + //} + //for (IFloodlightModule m : moduleLoader) { + if (logger.isDebugEnabled()) { + logger.debug("Found module " + m.getClass().getName()); } // Set up moduleNameMap @@ -100,7 +113,7 @@ public class FloodlightModuleLoader { int dupInConf = 0; for (IFloodlightModule cMod : mods) { if (mList.contains(cMod.getClass().getCanonicalName())) - dupInConf++; + dupInConf += 1; } if (dupInConf > 1) { @@ -108,7 +121,7 @@ public class FloodlightModuleLoader { for (IFloodlightModule mod : mods) { duplicateMods += mod.getClass().getCanonicalName() + ", "; } - throw new FloodlightModuleException("ERROR! The configuraiton " + + throw new FloodlightModuleException("ERROR! The configuraiton" + " file specifies more than one module that provides the service " + s.getCanonicalName() +". Please specify only ONE of the " + "following modules in the config file: " + duplicateMods); @@ -164,12 +177,17 @@ public class FloodlightModuleLoader { /** * Loads modules (and their dependencies) specified in the list * @param mList The array of fully qualified module names + * @param ignoreList The list of Floodlight services NOT to + * load modules for. Used for unit testing. * @return The ModuleContext containing all the loaded modules * @throws FloodlightModuleException */ - public IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop) - throws FloodlightModuleException { - logger.trace("Starting module loader"); + protected IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop, + Collection<IFloodlightService> ignoreList) throws FloodlightModuleException { + logger.debug("Starting module loader"); + if (logger.isDebugEnabled() && ignoreList != null) + logger.debug("Not loading module services " + ignoreList.toString()); + findAllModules(configMods); Collection<IFloodlightModule> moduleSet = new ArrayList<IFloodlightModule>(); @@ -192,6 +210,30 @@ public class FloodlightModuleLoader { throw new FloodlightModuleException("Module " + moduleName + " not found"); } + // If the module provies a service that is in the + // services ignorelist don't load it. + if ((ignoreList != null) && (module.getModuleServices() != null)) { + for (IFloodlightService ifs : ignoreList) { + for (Class<?> intsIgnore : ifs.getClass().getInterfaces()) { + //System.out.println(intsIgnore.getName()); + // Check that the interface extends IFloodlightService + //if (intsIgnore.isAssignableFrom(IFloodlightService.class)) { + //System.out.println(module.getClass().getName()); + if (intsIgnore.isAssignableFrom(module.getClass())) { + // We now ignore loading this module. + logger.debug("Not loading module " + + module.getClass().getCanonicalName() + + " because interface " + + intsIgnore.getCanonicalName() + + " is in the ignore list."); + + continue; + } + //} + } + } + } + // Add the module to be loaded addModule(moduleMap, moduleSet, module); // Add it's dep's to the queue @@ -243,6 +285,18 @@ public class FloodlightModuleLoader { startupModules(moduleSet); return floodlightModuleContext; + } + + /** + * Loads modules (and their dependencies) specified in the list. + * @param configMods The collection of fully qualified module names to load. + * @param prop The list of properties that are configuration options. + * @return The ModuleContext containing all the loaded modules. + * @throws FloodlightModuleException + */ + public IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop) + throws FloodlightModuleException { + return loadModulesFromList(configMods, prop, null); } /** @@ -320,8 +374,7 @@ public class FloodlightModuleLoader { protected void startupModules(Collection<IFloodlightModule> moduleSet) { for (IFloodlightModule m : moduleSet) { if (logger.isDebugEnabled()) { - logger.debug("Starting " + - m.getClass().getCanonicalName()); + logger.debug("Starting " + m.getClass().getCanonicalName()); } m.startUp(floodlightModuleContext); } @@ -332,6 +385,8 @@ public class FloodlightModuleLoader { * @param prop The properties file to use */ protected void parseConfigParameters(Properties prop) { + if (prop == null) return; + Enumeration<?> e = prop.propertyNames(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); diff --git a/src/main/java/net/floodlightcontroller/counter/CountSeries.java b/src/main/java/net/floodlightcontroller/counter/CountSeries.java index c94e5bc613b66767903165ce74d8dec26ce8e4d0..e8a547a516bab83395aa9b73affd425e85b897bd 100644 --- a/src/main/java/net/floodlightcontroller/counter/CountSeries.java +++ b/src/main/java/net/floodlightcontroller/counter/CountSeries.java @@ -69,16 +69,18 @@ public class CountSeries { public static final long dateSpanToMilliseconds(DateSpan ds) { long delta = 1; switch(ds) { - case WEEKS: - delta *= 7; - case DAYS: - delta *= 24; - case HOURS: - delta *= 60; - case MINUTES: - delta *= 60; - case SECONDS: - delta *= 1000; + case WEEKS: + delta *= 7; + case DAYS: + delta *= 24; + case HOURS: + delta *= 60; + case MINUTES: + delta *= 60; + case SECONDS: + delta *= 1000; + default: + break; } return delta; } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java index 55efa582dccf873a716ded053ed7c6789ac0c4eb..e66aff98b458fd1cf0f223e9f8941dd81b577b53 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java @@ -28,10 +28,9 @@ public class AttachmentPoint { // Timeout for moving attachment points from OF/broadcast // domain to another. - protected static long NBD_TO_BD_TIMEDIFF_MS = 300000; // 5 minutes - public static final long OPENFLOW_TO_EXTERNAL_TIMEOUT = 5000; // 5 seconds public static final long EXTERNAL_TO_EXTERNAL_TIMEOUT = 5000; // 5 seconds - public static final long CONSISTENT_TIMEOUT = 5000; // 5 seconds + public static final long OPENFLOW_TO_EXTERNAL_TIMEOUT = 30000; // 30 seconds + public static final long CONSISTENT_TIMEOUT = 30000; // 30 seconds public AttachmentPoint(long sw, short port, long lastSeen) { super(); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index e654a6e798804a7469e0433acf62475d87563995..9caaa9779737ce7bc856ae41929a5746cc27edb1 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -37,6 +37,7 @@ import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.topology.ITopologyService; /** @@ -94,6 +95,61 @@ public class Device implements IDevice { } } + /** + * Create a device from a set of entities + * @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 entityClass the entity class associated with the entities + */ + public Device(DeviceManagerImpl deviceManager, + Long deviceKey, + Collection<AttachmentPoint> attachmentPoints, + Collection<Entity> entities, + IEntityClass entityClass) { + this.deviceManager = deviceManager; + this.deviceKey = deviceKey; + this.entities = entities.toArray(new Entity[entities.size()]); + if (attachmentPoints == null) { + this.attachmentPoints = null; + } else { + this.attachmentPoints = + new ArrayList<AttachmentPoint>(attachmentPoints); + } + this.macAddressString = + HexString.toHexString(this.entities[0].getMacAddress(), 6); + this.entityClass = entityClass; + Arrays.sort(this.entities); + } + + /** + * 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. newEntity must be have the same + * entity class as device + */ + public Device(Device device, + Entity newEntity) { + this.deviceManager = device.deviceManager; + this.deviceKey = device.deviceKey; + this.entities = Arrays.<Entity>copyOf(device.entities, + device.entities.length + 1); + this.entities[this.entities.length - 1] = newEntity; + Arrays.sort(this.entities); + + if (device.attachmentPoints != null) { + this.attachmentPoints = + new ArrayList<AttachmentPoint>(device.attachmentPoints); + } else + this.attachmentPoints = null; + + this.macAddressString = + HexString.toHexString(this.entities[0].getMacAddress(), 6); + + this.entityClass = device.entityClass; + } + private Map<Long, AttachmentPoint> getAPMap() { if (attachmentPoints == null) return null; @@ -103,6 +159,16 @@ public class Device implements IDevice { List<AttachmentPoint>oldAP = new ArrayList<AttachmentPoint>(attachmentPoints); + // Remove invalid attachment points before sorting. + List<AttachmentPoint>tempAP = + new ArrayList<AttachmentPoint>(); + for(AttachmentPoint ap: oldAP) { + if (deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())){ + tempAP.add(ap); + } + } + oldAP = tempAP; + Collections.sort(oldAP, deviceManager.apComparator); // Map of attachment point by L2 domain Id. @@ -263,60 +329,7 @@ public class Device implements IDevice { return sp.toArray(new SwitchPort[sp.size()]); } - /** - * Create a device from a set of entities - * @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 entityClass the entity class associated with the entities - */ - public Device(DeviceManagerImpl deviceManager, - Long deviceKey, - Collection<AttachmentPoint> attachmentPoints, - Collection<Entity> entities, - IEntityClass entityClass) { - this.deviceManager = deviceManager; - this.deviceKey = deviceKey; - this.entities = entities.toArray(new Entity[entities.size()]); - if (attachmentPoints == null) { - this.attachmentPoints = null; - } else { - this.attachmentPoints = - new ArrayList<AttachmentPoint>(attachmentPoints); - } - this.macAddressString = - HexString.toHexString(this.entities[0].getMacAddress(), 6); - this.entityClass = entityClass; - Arrays.sort(this.entities); - } - - /** - * 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. newEntity must be have the same - * entity class as device - */ - public Device(Device device, - Entity newEntity) { - this.deviceManager = device.deviceManager; - this.deviceKey = device.deviceKey; - this.entities = Arrays.<Entity>copyOf(device.entities, - device.entities.length + 1); - this.entities[this.entities.length - 1] = newEntity; - Arrays.sort(this.entities); - - if (device.attachmentPoints != null) { - this.attachmentPoints = - new ArrayList<AttachmentPoint>(device.attachmentPoints); - } else - this.attachmentPoints = null; - - this.macAddressString = - HexString.toHexString(this.entities[0].getMacAddress(), 6); - - this.entityClass = device.entityClass; - } + // ******* // IDevice @@ -476,7 +489,22 @@ public class Device implements IDevice { @Override public String toString() { - return "Device [entityClass=" + entityClass.getName() + - " entities=" + Arrays.toString(entities) + "]"; + StringBuilder builder = new StringBuilder(); + builder.append("Device [entityClass="); + builder.append(entityClass.getName()); + builder.append(", MAC="); + builder.append(macAddressString); + builder.append(", IPs=["); + boolean isFirst = true; + for (Integer ip: getIPv4Addresses()) { + if (!isFirst) + builder.append(", "); + isFirst = false; + builder.append(IPv4.fromIPv4Address(ip)); + } + builder.append("], APs="); + builder.append(Arrays.toString(getAttachmentPoints(true))); + builder.append("]"); + return builder.toString(); } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 62e4701e89a8ef96e0ee889bb5aef3cfa616a9f7..32d2ebc8ad73f148ae2c8f755cd07060de7cba3f 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -66,7 +66,6 @@ import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.storage.IStorageSourceService; -import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.topology.ITopologyListener; import net.floodlightcontroller.topology.ITopologyService; @@ -89,7 +88,7 @@ import org.slf4j.LoggerFactory; */ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, ITopologyListener, -IStorageSourceListener, IFloodlightModule, IEntityClassListener, +IFloodlightModule, IEntityClassListener, IFlowReconcileListener, IInfoProvider, IHAListener { protected static Logger logger = LoggerFactory.getLogger(DeviceManagerImpl.class); @@ -201,7 +200,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { * A device update event to be dispatched */ protected static class DeviceUpdate { - protected enum Change { + public enum Change { ADD, DELETE, CHANGE; } @@ -227,6 +226,15 @@ IFlowReconcileListener, IInfoProvider, IHAListener { this.change = change; this.fieldsChanged = fieldsChanged; } + + @Override + public String toString() { + String devIdStr = device.getEntityClass().getName() + "::" + + device.getMACAddressString(); + return "DeviceUpdate [device=" + devIdStr + ", change=" + change + + ", fieldsChanged=" + fieldsChanged + "]"; + } + } /** @@ -473,6 +481,46 @@ IFlowReconcileListener, IInfoProvider, IHAListener { return new MultiIterator<Device>(iterators.iterator()); } + + protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { + 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(deviceIterator, + null, + macAddress, + vlan, + ipv4Address, + switchDPID, + switchPort); + return di; + } @Override public void addListener(IDeviceListener listener) { @@ -520,6 +568,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener { case PACKET_IN: return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); + default: + break; } logger.error("received an unexpected message {} from switch {}", @@ -550,33 +600,22 @@ IFlowReconcileListener, IInfoProvider, IHAListener { // Find the device matching the destination from the entity // classes of the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid, false); - logger.trace("DeviceManager dstEntity {}", dstEntity); + Device dstDevice = null; if (dstEntity != null) { - Device dstDevice = - findDestByEntity(srcDevice, dstEntity); - logger.trace("DeviceManager dstDevice {}", dstDevice); + dstDevice = findDestByEntity(srcDevice, dstEntity); if (dstDevice != null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); } + if (logger.isTraceEnabled()) { + logger.trace("Reconciling flow: match={}, srcDev={}, " + + "dstEntity={}, dstDev={}", + new Object[] { ofm.ofmWithSwDpid, srcDevice, + dstEntity, dstDevice } ); + } } return Command.CONTINUE; } - // ********************** - // IStorageSourceListener - // ********************** - - @Override - public void rowsModified(String tableName, Set<Object> rowKeys) { - // TODO Auto-generated method stub - - } - - @Override - public void rowsDeleted(String tableName, Set<Object> rowKeys) { - // TODO Auto-generated method stub - - } // ***************** // IFloodlightModule @@ -686,6 +725,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener { logger.debug("Resetting device state because of role change"); startUp(null); break; + default: + break; } } @@ -1176,6 +1217,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (updates == null) return; DeviceUpdate update = null; while (null != (update = updates.poll())) { + if (logger.isTraceEnabled()) { + logger.trace("Dispatching device update: {}", update); + } for (IDeviceListener listener : deviceListeners) { switch (update.change) { case ADD: @@ -1197,6 +1241,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { case VLAN: listener.deviceVlanChanged(update.device); break; + default: + logger.error("Unknown device field changed {}", + update.fieldsChanged.toString()); + break; } } break; @@ -1414,13 +1462,25 @@ IFlowReconcileListener, IInfoProvider, IHAListener { d, newDevice)) { // concurrent modification; try again - continue; + // need to use device that is the map now for the next + // iteration + d = deviceMap.get(d.getDeviceKey()); + if (null != d) + continue; + else + break; } } else { deviceUpdates.add(new DeviceUpdate(d, DELETE, null)); if (!deviceMap.remove(d.getDeviceKey(), d)) // concurrent modification; try again - continue; + // need to use device that is the map now for the next + // iteration + d = deviceMap.get(d.getDeviceKey()); + if (null != d) + continue; + else + break; } processUpdates(deviceUpdates); break; @@ -1448,6 +1508,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener { others); } } + + /** + * method to delete a given device, remove all entities first and then + * finally delete the device itself. + * @param device + */ + protected void deleteDevice(Device device) { + ArrayList<Entity> emptyToKeep = new ArrayList<Entity>(); + for (Entity entity : device.getEntities()) { + this.removeEntity(entity, device.getEntityClass(), + device.getDeviceKey(), emptyToKeep); + } + if (!deviceMap.remove(device.getDeviceKey(), device)) { + logger.info("device map does not have this device -" + + device.toString()); + } + } private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan, diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 8496f0a8e4f9a5e4b670969599e98f04adacb218..5904a6108fde23831647e0ea10d7cc0065fe51c1 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -211,12 +211,28 @@ public class Entity implements Comparable<Entity> { return true; } + + @Override public String toString() { - return "Entity [macAddress=" + HexString.toHexString(macAddress, 6) - + ", ipv4Address=" - + IPv4.fromIPv4Address(ipv4Address==null ? 0 : ipv4Address.intValue()) + ", vlan=" + vlan + ", switchDPID=" - + switchDPID + ", switchPort=" + switchPort + "]"; + StringBuilder builder = new StringBuilder(); + builder.append("Entity [macAddress="); + builder.append(HexString.toHexString(macAddress, 6)); + builder.append(", ipv4Address="); + builder.append(IPv4.fromIPv4Address(ipv4Address==null ? + 0 : ipv4Address.intValue())); + builder.append(", vlan="); + builder.append(vlan); + builder.append(", switchDPID="); + builder.append(switchDPID); + builder.append(", switchPort="); + builder.append(switchPort); + builder.append(", lastSeenTimestamp="); + builder.append(lastSeenTimestamp.getTime()); + builder.append(", activeSince="); + builder.append(activeSince.getTime()); + builder.append("]"); + return builder.toString(); } @Override diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java index 9d738ead7df24f8f919b47b1a1c34f883ab89732..618169f99985de0e2ce75a014e53965ebca801f4 100644 --- a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java +++ b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java @@ -33,6 +33,10 @@ public class FlowReconcileManager * need to be reconciled with the current configuration of the controller. */ protected ListenerDispatcher<OFType, IFlowReconcileListener> flowReconcileListeners; + + /** Config to enable or disable flowReconcile */ + protected static final String EnableConfigKey = "enable"; + protected boolean flowReconcileEnabled; @Override public synchronized void addFlowReconcileListener(IFlowReconcileListener listener) { @@ -65,6 +69,10 @@ public class FlowReconcileManager * @param ofmRcIn the ofm rc in */ public void reconcileFlow(OFMatchReconcile ofmRcIn) { + if (!flowReconcileEnabled) { + return; + } + if (logger.isTraceEnabled()) { logger.trace("Reconciling flow: {}", ofmRcIn.toString()); } @@ -145,6 +153,17 @@ public class FlowReconcileManager throws FloodlightModuleException { flowReconcileListeners = new ListenerDispatcher<OFType, IFlowReconcileListener>(); + + Map<String, String> configParam = context.getConfigParams(this); + String enableValue = configParam.get(EnableConfigKey); + // Set flowReconcile default to true + flowReconcileEnabled = true; + if (enableValue != null && + enableValue.equalsIgnoreCase("false")) { + flowReconcileEnabled = false; + } + + logger.debug("FlowReconcile is {}", flowReconcileEnabled); } @Override diff --git a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java index 08fe99305a26b9cae6c8b48e58e737f4229e332a..005708d135b1b9f04e9539a58952e6a4c3318711 100644 --- a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java +++ b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java @@ -436,6 +436,8 @@ public class LearningSwitch case ERROR: log.info("received an error {} from switch {}", (OFError) msg, sw); return Command.CONTINUE; + default: + break; } log.error("received an unexpected message {} from switch {}", msg, sw); return Command.CONTINUE; diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 3e43b6c527ebe786e641d6a891ecf429369ddc0b..c4421acfcf4971431da1efa0f5686df4871cf54f 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -37,7 +37,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { * @param info * @return */ - public ILinkDiscovery.LinkType getLinkType(LinkInfo info); + public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info); /** * Returns an unmodifiable map from switch id to a set of all links with it diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 76a056010573b9accaf360a5a8cfb11d1de48bd2..b818e8f7b848be108218c92c1b007bbc68e8ed16 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -309,9 +309,6 @@ IFloodlightModule, IInfoProvider, IHAListener { if (lldpClock == 0) { discoverOnAllPorts(); - return; - } else { - discoverOnKnownLinkPorts(); } } @@ -546,6 +543,8 @@ IFloodlightModule, IInfoProvider, IHAListener { return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx); case PORT_STATUS: return this.handlePortStatus(sw.getId(), (OFPortStatus) msg); + default: + break; } log.error("Received an unexpected message {} from switch {}", msg, sw); @@ -741,17 +740,6 @@ IFloodlightModule, IInfoProvider, IHAListener { else return UpdateOperation.PORT_DOWN; } - @Override - public LinkType getLinkType(LinkInfo info) { - - if (info.getUnicastValidTime() != null) - return LinkType.DIRECT_LINK; - else if (info.getMulticastValidTime() != null) - return LinkType.MULTIHOP_LINK; - - return LinkType.INVALID_LINK; - } - protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) { NodePortTuple srcNpt, dstNpt; @@ -789,11 +777,11 @@ IFloodlightModule, IInfoProvider, IHAListener { switchLinks.get(lt.getDst()).add(lt); // index both ends by switch:port - if (!portLinks.containsKey(lt.getSrc())) + if (!portLinks.containsKey(srcNpt)) portLinks.put(srcNpt, new HashSet<Link>()); portLinks.get(srcNpt).add(lt); - if (!portLinks.containsKey(lt.getDst())) + if (!portLinks.containsKey(dstNpt)) portLinks.put(dstNpt, new HashSet<Link>()); portLinks.get(dstNpt).add(lt); @@ -1587,8 +1575,12 @@ IFloodlightModule, IInfoProvider, IHAListener { log.error("Exception in LLDP send timer.", e); } finally { if (!shuttingDown) { - discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, + // null role implies HA mode is not enabled. + Role role = floodlightProvider.getRole(); + if (role == null || role == Role.MASTER) { + discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS); + } } } } @@ -1607,6 +1599,10 @@ IFloodlightModule, IInfoProvider, IHAListener { }}, "Topology Updates"); updatesThread.start(); + // null role implies HA mode is not enabled. + Role role = floodlightProvider.getRole(); + if (role == null || role == Role.MASTER) + discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS); // Register for the OpenFlow messages we want to receive floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this); @@ -1712,19 +1708,17 @@ IFloodlightModule, IInfoProvider, IHAListener { } // IHARoleListener - @Override public void roleChanged(Role oldRole, Role newRole) { switch(newRole) { case MASTER: if (oldRole == Role.SLAVE) { - clearAllLinks(); if (log.isTraceEnabled()) { log.trace("Sending LLDPs " + "to HA change from SLAVE->MASTER"); } clearAllLinks(); - discoverLinks(); + discoveryTask.reschedule(1, TimeUnit.MICROSECONDS); } break; case SLAVE: @@ -1738,6 +1732,8 @@ IFloodlightModule, IInfoProvider, IHAListener { portBroadcastDomainLinks.clear(); discoverOnAllPorts(); break; + default: + break; } } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java index 514cfe4f62acd329e772879c38296f9c5f7b1ff4..893e4adf0175a491f6852cbab42a262bf24758bf 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java @@ -21,18 +21,25 @@ import net.floodlightcontroller.routing.Link; public class LinkWithType extends JsonSerializer<LinkWithType> { public long srcSwDpid; public short srcPort; + public int srcPortState; public long dstSwDpid; public short dstPort; + public int dstPortState; public LinkType type; // Do NOT delete this, it's required for the serializer public LinkWithType() {} - public LinkWithType(Link link, LinkType type) { + public LinkWithType(Link link, + int srcPortState, + int dstPortState, + LinkType type) { this.srcSwDpid = link.getSrc(); this.srcPort = link.getSrcPort(); + this.srcPortState = srcPortState; this.dstSwDpid = link.getDst(); this.dstPort = link.getDstPort(); + this.dstPortState = dstPortState; this.type = type; } @@ -43,8 +50,10 @@ public class LinkWithType extends JsonSerializer<LinkWithType> { jgen.writeStartObject(); jgen.writeStringField("src-switch", HexString.toHexString(lwt.srcSwDpid)); jgen.writeNumberField("src-port", lwt.srcPort); + jgen.writeNumberField("src-port-state", lwt.srcPortState); jgen.writeStringField("dst-switch", HexString.toHexString(lwt.dstSwDpid)); jgen.writeNumberField("dst-port", lwt.dstPort); + jgen.writeNumberField("dst-port-state", lwt.dstPortState); jgen.writeStringField("type", lwt.type.toString()); jgen.writeEndObject(); } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java index 8fe1ff046f192cd0bcded0792a0bbf3a7030a737..4cad18e61db608eea3479003880ffe25cb9f3638 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java @@ -25,7 +25,10 @@ public class LinksResource extends ServerResource { links.putAll(ld.getLinks()); for (Link link: links.keySet()) { LinkInfo info = links.get(link); - LinkWithType lwt = new LinkWithType(link, ld.getLinkType(info)); + LinkWithType lwt = new LinkWithType(link, + info.getSrcPortState(), + info.getDstPortState(), + ld.getLinkType(link, info)); returnLinkSet.add(lwt); } } diff --git a/src/main/java/net/floodlightcontroller/packet/BasePacket.java b/src/main/java/net/floodlightcontroller/packet/BasePacket.java index 6df676d6bf5eb3e74eb0b14e9d3ea6189636fd6b..4ecfdedddd813b1fbd42a3ad1a2dbc48eafc45ed 100644 --- a/src/main/java/net/floodlightcontroller/packet/BasePacket.java +++ b/src/main/java/net/floodlightcontroller/packet/BasePacket.java @@ -29,6 +29,7 @@ public abstract class BasePacket implements IPacket { /** * @return the parent */ + @Override public IPacket getParent() { return parent; } @@ -36,6 +37,7 @@ public abstract class BasePacket implements IPacket { /** * @param parent the parent to set */ + @Override public IPacket setParent(IPacket parent) { this.parent = parent; return this; @@ -44,6 +46,7 @@ public abstract class BasePacket implements IPacket { /** * @return the payload */ + @Override public IPacket getPayload() { return payload; } @@ -51,10 +54,17 @@ public abstract class BasePacket implements IPacket { /** * @param payload the payload to set */ + @Override public IPacket setPayload(IPacket payload) { this.payload = payload; return this; } + + @Override + public void resetChecksum() { + if (this.parent != null) + this.parent.resetChecksum(); + } /* (non-Javadoc) * @see java.lang.Object#hashCode() diff --git a/src/main/java/net/floodlightcontroller/packet/DHCP.java b/src/main/java/net/floodlightcontroller/packet/DHCP.java index 2001b4ff9dca8de0e0838aeb6647966747e744d8..f73d9c968019ab99ebfb4d5712c0e2a95a01e297 100644 --- a/src/main/java/net/floodlightcontroller/packet/DHCP.java +++ b/src/main/java/net/floodlightcontroller/packet/DHCP.java @@ -355,6 +355,9 @@ public class DHCP extends BasePacket { @Override public byte[] serialize() { + // not guaranteed to retain length/exact format + resetChecksum(); + // minimum size 240 including magic cookie, options generally padded to 300 int optionsLength = 0; for (DHCPOption option : this.options) { diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index 123c9e0d0eff5bb16e51f5d043b53f52b2130ce3..e547812efb87464ec4492d7f96f24de6218fead1 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -329,6 +329,8 @@ public class Ethernet extends BasePacket { int result = super.hashCode(); result = prime * result + destinationMACAddress.hashCode(); result = prime * result + etherType; + result = prime * result + vlanID; + result = prime * result + priorityCode; result = prime * result + (pad ? 1231 : 1237); result = prime * result + sourceMACAddress.hashCode(); return result; diff --git a/src/main/java/net/floodlightcontroller/packet/IPacket.java b/src/main/java/net/floodlightcontroller/packet/IPacket.java index 094cfc700a10a4aab4d1ca7531333aed3309ace9..02376cd990cbd1c1980b78bb7815323a46480cf2 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPacket.java +++ b/src/main/java/net/floodlightcontroller/packet/IPacket.java @@ -48,6 +48,11 @@ public interface IPacket { */ public IPacket setParent(IPacket packet); + /** + * Reset any checksums as needed, and call resetChecksum on all parents + */ + public void resetChecksum(); + /** * Sets all payloads parent packet if applicable, then serializes this * packet and all payloads diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java index ccfce792b3271d2aff56aee32b816d2ea45d6277..01f886da252bd59fa641f56b4684a0ce28087fdb 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPv4.java +++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java @@ -209,6 +209,11 @@ public class IPv4 extends BasePacket { this.checksum = checksum; return this; } + @Override + public void resetChecksum() { + this.checksum = 0; + super.resetChecksum(); + } /** * @return the sourceAddress diff --git a/src/main/java/net/floodlightcontroller/packet/TCP.java b/src/main/java/net/floodlightcontroller/packet/TCP.java index 5fc6279bc76f2d3aa66949c8b8f77b5a915e517d..889e4c6a88292d3e2ea704aa3b49e38e93054fdd 100644 --- a/src/main/java/net/floodlightcontroller/packet/TCP.java +++ b/src/main/java/net/floodlightcontroller/packet/TCP.java @@ -114,6 +114,13 @@ public class TCP extends BasePacket { this.checksum = checksum; return this; } + + @Override + public void resetChecksum() { + this.checksum = 0; + super.resetChecksum(); + } + public short getUrgentPointer(short urgentPointer) { return this.urgentPointer; } diff --git a/src/main/java/net/floodlightcontroller/packet/UDP.java b/src/main/java/net/floodlightcontroller/packet/UDP.java index 66f6b63307b296ed0b93a116b4dfa52870f6d33d..cbeeedfd18add16d1f657c7930cadf4bf08da6b8 100644 --- a/src/main/java/net/floodlightcontroller/packet/UDP.java +++ b/src/main/java/net/floodlightcontroller/packet/UDP.java @@ -97,6 +97,12 @@ public class UDP extends BasePacket { return this; } + @Override + public void resetChecksum() { + this.checksum = 0; + super.resetChecksum(); + } + /** * Serializes the packet. Will compute and set the following fields if they * are set to specific values at the time serialize is called: diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index d56bbb67f06f5bb7152e9d610a275efd207d1295..32525a8c8b5654fae27ee345295864aceada1a71 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -70,7 +70,8 @@ public abstract class ForwardingBase implements // for broadcast loop suppression protected boolean broadcastCacheFeature = true; - public final int prime = 2633; // for hash calculation + public final int prime1 = 2633; // for hash calculation + public final static int prime2 = 4357; // for hash calculation public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000); // 5 seconds interval; @@ -139,6 +140,8 @@ public abstract class ForwardingBase implements (OFPacketIn) msg, decision, cntx); + default: + break; } log.error("received an unexpected message {} from switch {}", msg, @@ -504,8 +507,8 @@ public abstract class ForwardingBase implements IFloodlightProviderService.CONTEXT_PI_PAYLOAD); Long broadcastHash; - broadcastHash = topology.getL2DomainId(sw.getId()) - * prime + eth.hashCode(); + broadcastHash = topology.getL2DomainId(sw.getId()) * prime1 + + pi.getInPort() * prime2 + eth.hashCode(); if (broadcastCache.update(broadcastHash)) { sw.updateBroadcastCache(broadcastHash, pi.getInPort()); return true; @@ -519,16 +522,15 @@ public abstract class ForwardingBase implements // If the feature is disabled, always return false; if (!broadcastCacheFeature) return false; - + // Get the hash of the Ethernet packet. Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - // some FORWARD_OR_FLOOD packets are unicast with unknown destination mac - // if (eth.isBroadcast() || eth.isMulticast()) - return sw.updateBroadcastCache(new Long(eth.hashCode()), pi.getInPort()); + long hash = pi.getInPort() * prime2 + eth.hashCode(); - // return false; + // some FORWARD_OR_FLOOD packets are unicast with unknown destination mac + return sw.updateBroadcastCache(hash, pi.getInPort()); } public static boolean diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index e2cc5ec2c38ecf738cecfcca846ba0701576148a..524757f181bf79333b91029c3d404cc0b17c6f23 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -625,6 +625,8 @@ public class StaticFlowEntryPusher entry2dpid.clear(); entriesFromStorage.clear(); break; + default: + break; } } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index c261a23a3b20977f1ebfed357d685d6786253389..71751f9950738f9a58460927c8175322ab08cb9a 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -106,11 +106,8 @@ public class TopologyManager implements @Override public void run() { try { - boolean recomputeFlag = false; - recomputeFlag = applyUpdates(); - if (recomputeFlag) { - createNewInstance(); - } + applyUpdates(); + createNewInstance(); lastUpdateTime = new Date(); informListeners(); } @@ -525,6 +522,8 @@ public class TopologyManager implements case PACKET_IN: return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); + default: + break; } log.error("received an unexpected message {} from switch {}", @@ -551,6 +550,8 @@ public class TopologyManager implements "HA change to SLAVE"); clearCurrentTopology(); break; + default: + break; } } @@ -798,9 +799,8 @@ public class TopologyManager implements } - public boolean applyUpdates() { + public void applyUpdates() { - boolean topologyRecomputeRequired = false; appliedUpdates.clear(); LDUpdate update = null; while (ldUpdates.peek() != null) { @@ -816,23 +816,18 @@ public class TopologyManager implements addOrUpdateLink(update.getSrc(), update.getSrcPort(), update.getDst(), update.getDstPort(), update.getType()); - topologyRecomputeRequired = true; } else if (update.getOperation() == UpdateOperation.LINK_REMOVED){ removeLink(update.getSrc(), update.getSrcPort(), update.getDst(), update.getDstPort()); - topologyRecomputeRequired = true; } else if (update.getOperation() == UpdateOperation.SWITCH_REMOVED) { - topologyRecomputeRequired = removeSwitch(update.getSrc()); + removeSwitch(update.getSrc()); } else if (update.getOperation() == UpdateOperation.PORT_DOWN) { - topologyRecomputeRequired = removeSwitchPort(update.getSrc(), - update.getSrcPort()); + removeSwitchPort(update.getSrc(), update.getSrcPort()); } // Add to the list of applied updates. appliedUpdates.add(update); } - - return topologyRecomputeRequired; } /** diff --git a/src/main/java/net/floodlightcontroller/topology/web/SwitchClustersResource.java b/src/main/java/net/floodlightcontroller/topology/web/SwitchClustersResource.java index 8edf5bd20a1efa3be4699469a3fc7906e2e30810..f52d27a0735e936fc8425c4a30e58a3b43517d21 100644 --- a/src/main/java/net/floodlightcontroller/topology/web/SwitchClustersResource.java +++ b/src/main/java/net/floodlightcontroller/topology/web/SwitchClustersResource.java @@ -28,6 +28,7 @@ import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.topology.ITopologyService; import org.openflow.util.HexString; +import org.restlet.data.Form; import org.restlet.resource.Get; import org.restlet.resource.ServerResource; @@ -43,10 +44,20 @@ public class SwitchClustersResource extends ServerResource { ITopologyService topology = (ITopologyService)getContext().getAttributes(). get(ITopologyService.class.getCanonicalName()); + + Form form = getQuery(); + String queryType = form.getFirstValue("type", true); + boolean openflowDomain = true; + if (queryType != null && "l2".equals(queryType)) { + openflowDomain = false; + } Map<String, List<String>> switchClusterMap = new HashMap<String, List<String>>(); for (Entry<Long, IOFSwitch> entry : floodlightProvider.getSwitches().entrySet()) { - Long clusterDpid = topology.getL2DomainId(entry.getValue().getId()); + Long clusterDpid = + (openflowDomain + ? topology.getOpenflowDomainId(entry.getValue().getId()) + :topology.getL2DomainId(entry.getValue().getId())); List<String> switchesInCluster = switchClusterMap.get(HexString.toHexString(clusterDpid)); if (switchesInCluster != null) { switchesInCluster.add(HexString.toHexString(entry.getKey())); diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java index 486bc7b21b5b417ea52cd9ce11a2739ce67ccb04..78d650779f63ca5b930b5f073b07ff270ad01ed2 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java @@ -300,6 +300,8 @@ public class VirtualNetworkFilter switch (msg.getType()) { case PACKET_IN: return processPacketIn(sw, (OFPacketIn)msg, cntx); + default: + break; } log.warn("Received unexpected message {}", msg); return Command.CONTINUE; diff --git a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java index 803db8a9167fee4918e7d795cb9b42b665191100..bea7f1e3b064a947a95d1398696462ae886cf697 100644 --- a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java +++ b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java @@ -270,6 +270,23 @@ public class OFFlowStatisticsReply implements OFStatistics, OFActionFactoryAware } } + @Override + public String toString() { + String str = "match=" + this.match; + str += " tableId=" + this.tableId; + str += " durationSeconds=" + this.durationSeconds; + str += " durationNanoseconds=" + this.durationNanoseconds; + str += " priority=" + this.priority; + str += " idleTimeout=" + this.idleTimeout; + str += " hardTimeout=" + this.hardTimeout; + str += " cookie=" + this.cookie; + str += " packetCount=" + this.packetCount; + str += " byteCount=" + this.byteCount; + str += " action=" + this.actions; + + return str; + } + @Override public int hashCode() { final int prime = 419; diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule index 430f7c6b6ede5ae1062ed7e0b36c7c7640320d58..0225a2b25a00e0e13c491d4af6de790db232a072 100644 --- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule +++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule @@ -19,4 +19,7 @@ net.floodlightcontroller.threadpool.ThreadPool net.floodlightcontroller.ui.web.StaticWebRoutable net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier +net.floodlightcontroller.devicemanager.test.MockDeviceManager +net.floodlightcontroller.core.test.MockFloodlightProvider +net.floodlightcontroller.core.test.MockThreadPoolService net.floodlightcontroller.firewall.Firewall \ No newline at end of file diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 2ac8d4a613323f8343ecc7cc5b1f22335998578e..811f860589ddd4eb114788228df418ba8356ce93 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -1,4 +1,7 @@ floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\ +net.floodlightcontroller.core.FloodlightProvider,\ +net.floodlightcontroller.threadpool.ThreadPool,\ +net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\ net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\ net.floodlightcontroller.firewall.Firewall,\ net.floodlightcontroller.forwarding.Forwarding,\ diff --git a/src/main/resources/web/img/switch.png b/src/main/resources/web/img/switch.png index d241d92d74d012aa886823a597936b7dcab759c3..f8e69108f7d9c26ae92fcc41d7fb286cfc390edb 100644 Binary files a/src/main/resources/web/img/switch.png and b/src/main/resources/web/img/switch.png differ diff --git a/src/main/resources/web/index.html b/src/main/resources/web/index.html index 1812037aed3ce286cdcd31ace15edd21f4a16e9c..cc2c2e8ed7cff9fd1a555589ea4f7c8b6bc9a712 100644 --- a/src/main/resources/web/index.html +++ b/src/main/resources/web/index.html @@ -50,37 +50,14 @@ <div class="header"></div> -<!-- FIXME closing these alerts returns to the dashboard; why? --> - - <div class="container"> -<!-- -<div class="row"> -<div class="span12"> -<div class="alert alert-info"> -Floodlight is the awesomest OpenFlow controller, and you're running it right now. -<a class="close" data-dismiss="alert" href="#">×</a> -</div> -</div> -</div> - -<div class="row"> -<div class="span12"> -<div class="alert alert-error"> -This Web interface is <strong>incomplete and buggy</strong>! Don't be surprised if you see errors or missing data. -<a class="close" data-dismiss="alert" href="#">×</a> -</div> -</div> -</div> ---> - <div id="content"></div> <hr> <footer class="footer"> - <p><a href="http://floodlight.openflowhub.org/">Floodlight </a> © <a href="http://www.bigswitch.com/">Big Switch Networks</a>, <a href="http://www.research.ibm.com/arl/">IBM</a>, et. al. - Powered by <a href="http://documentcloud.github.com/backbone/">Backbone.js</a>, <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>, <a href="http://jquery.com/">jQuery</a>, <a href="http://mbostock.github.com/d3/">D3.js</a>, etc.</p> + <p><a href="http://floodlight.openflowhub.org/">Floodlight </a> © <a href="http://www.bigswitch.com/">Big Switch Networks</a>, <a href="http://www.research.ibm.com/arl/">IBM</a>, et. al. + Powered by <a href="http://documentcloud.github.com/backbone/">Backbone.js</a>, <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>, <a href="http://jquery.com/">jQuery</a>, <a href="http://mbostock.github.com/d3/">D3.js</a>, etc.</p> </footer> </div> <!-- /container --> diff --git a/src/main/resources/web/js/main.js b/src/main/resources/web/js/main.js index 7e0ac8106a78109f9b48da40c70f61d43c749ad3..2362757ac690fb3c27bc6a6a54fbe3e2eead68a6 100644 --- a/src/main/resources/web/js/main.js +++ b/src/main/resources/web/js/main.js @@ -47,8 +47,8 @@ var AppRouter = Backbone.Router.extend({ }, topology:function () { - //console.log("switching to topology view"); - var topo = new Topology(); + //console.log("switching to topology view"); + var topo = new Topology(); $('#content').html(new TopologyView({model:topo, hosts:hl}).render().el); // TODO factor this code out $('ul.nav > li').removeClass('active'); @@ -56,30 +56,30 @@ var AppRouter = Backbone.Router.extend({ }, switchDetails:function (id) { - //console.log("switching [sic] to single switch view"); - var sw = swl.get(id); - $('#content').html(new SwitchView({model:sw}).render().el); + //console.log("switching [sic] to single switch view"); + var sw = swl.get(id); + $('#content').html(new SwitchView({model:sw}).render().el); $('ul.nav > li').removeClass('active'); $('li > a[href*="/switches"]').parent().addClass('active'); }, switchList:function () { - //console.log("switching [sic] to switch list view"); + //console.log("switching [sic] to switch list view"); $('#content').html(new SwitchListView({model:swl}).render().el); $('ul.nav > li').removeClass('active'); $('li > a[href*="/switches"]').parent().addClass('active'); }, hostDetails:function (id) { - //console.log("switching to single host view"); - var h = hl.get(id); - $('#content').html(new HostView({model:h}).render().el); + //console.log("switching to single host view"); + var h = hl.get(id); + $('#content').html(new HostView({model:h}).render().el); $('ul.nav > li').removeClass('active'); $('li > a[href*="/hosts"]').parent().addClass('active'); }, hostList:function () { - //console.log("switching to host list view"); + //console.log("switching to host list view"); $('#content').html(new HostListView({model:hl}).render().el); $('ul.nav > li').removeClass('active'); $('li > a[href*="/hosts"]').parent().addClass('active'); @@ -99,21 +99,21 @@ tpl.loadTemplates(['home', 'status', 'topology', 'header', 'switch', 'switch-lis $(document).ready(function () { // trigger Backbone routing when clicking on links, thanks to Atinux and pbnv - app.navigate("", true); - - window.document.addEventListener('click', function(e) { - e = e || window.event - var target = e.target || e.srcElement - if ( target.nodeName.toLowerCase() === 'a' ) { - e.preventDefault() - var uri = target.getAttribute('href') - app.navigate(uri.substr(1), true) - } - }); - window.addEventListener('popstate', function(e) { - app.navigate(location.pathname.substr(1), true); - }); - + app.navigate("", true); + + window.document.addEventListener('click', function(e) { + e = e || window.event + var target = e.target || e.srcElement + if ( target.nodeName.toLowerCase() === 'a' ) { + e.preventDefault() + var uri = target.getAttribute('href') + app.navigate(uri.substr(1), true) + } + }); + window.addEventListener('popstate', function(e) { + app.navigate(location.pathname.substr(1), true); + }); + }); }); diff --git a/src/main/resources/web/js/models/flowmodel.js b/src/main/resources/web/js/models/flowmodel.js index 4923f610c415ca1e8e518e02c5bd3229c00f5a6b..80777c334f15c01bf7590bdf456320a2657a23ad 100644 --- a/src/main/resources/web/js/models/flowmodel.js +++ b/src/main/resources/web/js/models/flowmodel.js @@ -16,13 +16,13 @@ window.Flow = Backbone.Model.extend({ - defaults: { - receiveBytes: 0, - receivePackets: 0, - transmitBytes: 0, - transmitPackets: 0, - }, - + defaults: { + receiveBytes: 0, + receivePackets: 0, + transmitBytes: 0, + transmitPackets: 0, + }, + // initialize:function () {} }); diff --git a/src/main/resources/web/js/models/hostmodel.js b/src/main/resources/web/js/models/hostmodel.js index ec37d1573c043dd9f61cbdb159696da2e7c49774..47ae4207e97fb2168c81e900e42f38ac853fe2c4 100644 --- a/src/main/resources/web/js/models/hostmodel.js +++ b/src/main/resources/web/js/models/hostmodel.js @@ -62,11 +62,11 @@ window.HostCollection = Backbone.Collection.extend({ } /* - * findByName:function (key) { // TODO: Modify service to include firstName - * in search var url = (key == '') ? '/host/' : "/host/search/" + key; - * console.log('findByName: ' + key); var self = this; $.ajax({ url:url, - * dataType:"json", success:function (data) { console.log("search success: " + - * data.length); self.reset(data); } }); } - */ + * findByName:function (key) { // TODO: Modify service to include firstName + * in search var url = (key == '') ? '/host/' : "/host/search/" + key; + * console.log('findByName: ' + key); var self = this; $.ajax({ url:url, + * dataType:"json", success:function (data) { console.log("search success: " + + * data.length); self.reset(data); } }); } + */ }); diff --git a/src/main/resources/web/js/models/statusmodel.js b/src/main/resources/web/js/models/statusmodel.js index aac2f8d2aab44609bae4805d2667ade896f058ce..31bdff37565b54d2ccd2a66d1cac348d5ac8a0b8 100644 --- a/src/main/resources/web/js/models/statusmodel.js +++ b/src/main/resources/web/js/models/statusmodel.js @@ -16,14 +16,14 @@ window.Status = Backbone.Model.extend({ defaults: { - host: 'localhost', - ofport: 6633, - uptime: 'unknown', - free: 0, - total: 0, - healthy: 'unknown', - modules: [], - moduleText: '' + host: 'localhost', + ofport: 6633, + uptime: 'unknown', + free: 0, + total: 0, + healthy: 'unknown', + modules: [], + moduleText: '' }, initialize:function () { @@ -54,7 +54,8 @@ window.Status = Backbone.Model.extend({ console.log("fetched controller status: modules loaded"); // console.log(data); self.set({modules:_.keys(data)}); - self.set({moduleText:_.reduce(_.keys(data), function(s, m){return s+m.replace("net.floodlightcontroller", "n.f")+", "}, '')}); + self.set({moduleText:_.reduce(_.keys(data), function(s, m) + {return s+m.replace("net.floodlightcontroller", "n.f")+", "}, '')}); } }); diff --git a/src/main/resources/web/js/models/switchmodel.js b/src/main/resources/web/js/models/switchmodel.js index 252b316e45c605e02477a57a167827cdd560865d..285c912cd148478a1c0a3c627dd0ab208efe9dab 100644 --- a/src/main/resources/web/js/models/switchmodel.js +++ b/src/main/resources/web/js/models/switchmodel.js @@ -153,18 +153,92 @@ window.Switch = Backbone.Model.extend({ dataType:"json", success:function (data) { //console.log("fetched switch " + self.id + " flows"); - // console.log(data[self.id]); + var flows = data[self.id]; + //console.log(flows); + // create flow models var i = 0; - _.each(data[self.id], function(f) { + _.each(flows, function(f) { f.id = self.id + '-' + i++; - f.matchHTML = "src=<a href='/host/" + f.match.dataLayerSource + "'>" + - f.match.dataLayerSource + - "</a>, dst=<a href='/host/" + f.match.dataLayerDestination + "'>" + - f.match.dataLayerDestination + - "</a>, port=" + f.match.inputPort; // FIXME - f.actionText = f.actions[0].type + " " + f.actions[0].port; // FIXME - // console.log(f); + + // build human-readable match + f.matchHTML = ''; + if(!(f.match.wildcards & (1<<0))) { // input port + f.matchHTML += "port=" + f.match.inputPort + ", "; + } + if(!(f.match.wildcards & (1<<1))) { // VLAN ID + f.matchHTML += "VLAN=" + f.match.dataLayerVirtualLan + ", "; + } + if(!(f.match.wildcards & (1<<20))) { // VLAN prio + f.matchHTML += "prio=" + f.match.dataLayerVirtualLanPriorityCodePoint + ", "; + } + if(!(f.match.wildcards & (1<<2))) { // src MAC + f.matchHTML += "src=<a href='/host/" + f.match.dataLayerSource + "'>" + + f.match.dataLayerSource + "</a>, "; + } + if(!(f.match.wildcards & (1<<3))) { // dest MAC + f.matchHTML =+ "dest=<a href='/host/" + f.match.dataLayerDestination + "'>" + + f.match.dataLayerDestination + "</a>, "; + } + if(!(f.match.wildcards & (1<<4))) { // Ethertype + // TODO print a human-readable name instead of hex + f.matchHTML += "ethertype=" + f.match.dataLayerType + ", "; + } + if(!(f.match.wildcards & (1<<5))) { // IP protocol + // TODO print a human-readable name + f.matchHTML += "proto=" + f.match.networkProtocol + ", "; + } + if(!(f.match.wildcards & (1<<6))) { // TCP/UDP source port + f.matchHTML += "IP src port=" + f.match.transportSource + ", "; + } + if(!(f.match.wildcards & (1<<7))) { // TCP/UDP dest port + f.matchHTML += "IP dest port=" + f.match.transportDestination + ", "; + } + if(!(f.match.wildcards & (32<<8))) { // src IP + f.matchHTML += "src=" + f.match.networkSource + ", "; + } + if(!(f.match.wildcards & (32<<14))) { // dest IP + f.matchHTML += "dest=" + f.match.networkDestination + ", "; + } + if(!(f.match.wildcards & (1<<21))) { // IP TOS + f.matchHTML += "TOS=" + f.match.networkTypeOfService + ", "; + } + // remove trailing ", " + f.matchHTML = f.matchHTML.substr(0, f.matchHTML.length - 2); + + // build human-readable action list + f.actionText = _.reduce(f.actions, function (memo, a) { + switch (a.type) { + case "OUTPUT": + return memo + "output " + a.port + ', '; + case "OPAQUE_ENQUEUE": + return memo + "enqueue " + a.port + ':' + a.queueId + ', '; + case "STRIP_VLAN": + return memo + "strip VLAN, "; + case "SET_VLAN_ID": + return memo + "VLAN=" + a.virtualLanIdentifier + ', '; + case "SET_VLAN_PCP": + return memo + "prio=" + a.virtualLanPriorityCodePoint + ', '; + case "SET_DL_SRC": + return memo + "src=" + a.dataLayerAddress + ', '; + case "SET_DL_DST": + return memo + "dest=" + a.dataLayerAddress + ', '; + case "SET_NW_TOS": + return memo + "TOS=" + a.networkTypeOfService + ', '; + case "SET_NW_SRC": + return memo + "src=" + a.networkAddress + ', '; + case "SET_NW_DST": + return memo + "dest=" + a.networkAddress + ', '; + case "SET_TP_SRC": + return memo + "src port=" + a.transportPort + ', '; + case "SET_TP_DST": + return memo + "dest port=" + a.transportPort + ', '; + } + }, ""); + // remove trailing ", " + f.actionText = f.actionText.substr(0, f.actionText.length - 2); + + //console.log(f); self.flows.add(f, {silent: true}); }); self.flows.trigger('add'); @@ -185,8 +259,10 @@ window.SwitchCollection = Backbone.Collection.extend({ dataType:"json", success:function (data) { //console.log("fetched switch list: " + data.length); - // console.log(data); - _.each(data, function(sw) {self.add({id: sw['dpid']})}); + //console.log(data); + _.each(data, function(sw) {self.add({id: sw['dpid'], + inetAddress: sw.inetAddress, + connectedSince: new Date(sw.connectedSince).toLocaleString()})}); } }); }, diff --git a/src/main/resources/web/js/models/topologymodel.js b/src/main/resources/web/js/models/topologymodel.js index be9fa302fa2b78ed0260e04118acaddd9823ade9..c5d8f9bff2cc9bbf13a7ee615faca1f173f23457 100644 --- a/src/main/resources/web/js/models/topologymodel.js +++ b/src/main/resources/web/js/models/topologymodel.js @@ -19,8 +19,8 @@ window.Topology = Backbone.Model.extend({ url:"/wm/topology/links/json", defaults:{ - nodes: [], - links: [], + nodes: [], + links: [], }, initialize:function () { @@ -34,19 +34,25 @@ window.Topology = Backbone.Model.extend({ // console.log(data); self.nodes = {}; self.links = []; + // step 1: build unique array of switch IDs + /* this doesn't work if there's only one switch, + because there are no switch-switch links _.each(data, function (l) { - self.nodes[l['src-switch']] = true; - self.nodes[l['dst-switch']] = true; + self.nodes[l['src-switch']] = true; + self.nodes[l['dst-switch']] = true; }); // console.log(self.nodes); var nl = _.keys(self.nodes); + */ + var nl = swl.pluck('id'); self.nodes = _.map(nl, function (n) {return {name:n}}); + // step 2: build array of links in format D3 expects _.each(data, function (l) { - self.links.push({source:nl.indexOf(l['src-switch']), - target:nl.indexOf(l['dst-switch']), - value:10}); + self.links.push({source:nl.indexOf(l['src-switch']), + target:nl.indexOf(l['dst-switch']), + value:10}); }); // console.log(self.nodes); // console.log(self.links); diff --git a/src/main/resources/web/js/views/flow.js b/src/main/resources/web/js/views/flow.js index 3d0609240edef0e4281d6b9741cea1d36e39d5d7..65e0b71d9e70a4522250e9c53f26499f68ad4c1e 100644 --- a/src/main/resources/web/js/views/flow.js +++ b/src/main/resources/web/js/views/flow.js @@ -48,7 +48,7 @@ window.FlowListItemView = Backbone.View.extend({ // TODO throughput (bps) and pps sparklines would be nice here // TODO hovering over a MAC address could show a compact view of that host window.FlowListView = Backbone.View.extend({ - + initialize:function () { this.template = _.template(tpl.get('flow-list')); this.model.bind("change", this.render, this); @@ -56,11 +56,11 @@ window.FlowListView = Backbone.View.extend({ }, render:function (eventName) { - // console.log("rendering flow list view: " + this.model.models.length); + // console.log("rendering flow list view: " + this.model.models.length); $(this.el).html(this.template({nflows:this.model.length})); _.each(this.model.models, function (f) { $(this.el).find('table.flow-table > tbody') - .append(new FlowListItemView({model:f}).render().el); + .append(new FlowListItemView({model:f}).render().el); }, this); return this; }, diff --git a/src/main/resources/web/js/views/port.js b/src/main/resources/web/js/views/port.js index e6001e334df06e0d219a08a69af8f67dae2d7892..e9aadb9103a13b320e9131add8e0dfc68ca015fe 100644 --- a/src/main/resources/web/js/views/port.js +++ b/src/main/resources/web/js/views/port.js @@ -49,7 +49,7 @@ window.PortListItemView = Backbone.View.extend({ // TODO throughput sparklines would be nice here window.PortListView = Backbone.View.extend({ - + initialize:function () { this.template = _.template(tpl.get('port-list')); this.model.bind("change", this.render, this); @@ -57,11 +57,11 @@ window.PortListView = Backbone.View.extend({ }, render:function (eventName) { - // console.log("rendering port list view"); + // console.log("rendering port list view"); $(this.el).html(this.template({nports:this.model.length})); _.each(this.model.models, function (p) { $(this.el).find('table.port-table > tbody') - .append(new PortListItemView({model:p}).render().el); + .append(new PortListItemView({model:p}).render().el); }, this); return this; }, diff --git a/src/main/resources/web/js/views/status.js b/src/main/resources/web/js/views/status.js index d56507e29e142be3993f0d708fbd1fe5ed1d5c64..52c6c1cbaa3e5a3279983979f625086ebe742f67 100644 --- a/src/main/resources/web/js/views/status.js +++ b/src/main/resources/web/js/views/status.js @@ -22,7 +22,7 @@ window.StatusView = Backbone.View.extend({ }, render:function (eventName) { - // console.log("rendering status"); + // console.log("rendering status"); $(this.el).html(this.template(this.model.toJSON())); //$(this.el).html(this.template()); return this; diff --git a/src/main/resources/web/js/views/switch.js b/src/main/resources/web/js/views/switch.js index 9aa850a364ced8f85374fa1522268127da8c9590..178dd99bba9021ac1675a17a46af2e77dc90e7f3 100644 --- a/src/main/resources/web/js/views/switch.js +++ b/src/main/resources/web/js/views/switch.js @@ -42,7 +42,7 @@ window.SwitchListItemView = Backbone.View.extend({ initialize:function () { this.template = _.template(tpl.get('switch-list-item')); this.model.bind("change", this.render, this); - //this.model.bind("destroy", this.close, this); + //this.model.bind("destroy", this.close, this); }, render:function (eventName) { @@ -53,7 +53,7 @@ window.SwitchListItemView = Backbone.View.extend({ }); window.SwitchListView = Backbone.View.extend({ - + initialize:function () { this.template = _.template(tpl.get('switch-list')); this.model.bind("change", this.render, this); @@ -63,7 +63,7 @@ window.SwitchListView = Backbone.View.extend({ $(this.el).html(this.template({nswitches:swl.length})); _.each(this.model.models, function (sw) { $(this.el).find('table.switch-table > tbody') - .append(new SwitchListItemView({model:sw}).render().el); + .append(new SwitchListItemView({model:sw}).render().el); }, this); return this; }, diff --git a/src/main/resources/web/tpl/flow-list-item.html b/src/main/resources/web/tpl/flow-list-item.html index 59734906aacb0e2e830c113dcd24ba312c9e7815..7c099c3e7e0e43c9612412dc7b4220fcbe7b1001 100644 --- a/src/main/resources/web/tpl/flow-list-item.html +++ b/src/main/resources/web/tpl/flow-list-item.html @@ -1 +1 @@ -<td><%= cookie %></td><td><%= priority %></td><td><%= matchHTML %></td><td><%= actionText %></td><td><%= packetCount %></td><td><%= byteCount %></td><td><%= durationSeconds %> s</td><td><%= idleTimeout %> s</td> + <td><%= cookie %></td><td><%= priority %></td><td><%= matchHTML %></td><td><%= actionText %></td><td><%= packetCount %></td><td><%= byteCount %></td><td><%= durationSeconds %> s</td><td><%= idleTimeout %> s</td> diff --git a/src/main/resources/web/tpl/flow-list.html b/src/main/resources/web/tpl/flow-list.html index 4b6e4065b97cbdd5fe3b98c4eeea92c270bb804c..21a30cf8d1b67633cb5869e68e88c51f96db82ec 100644 --- a/src/main/resources/web/tpl/flow-list.html +++ b/src/main/resources/web/tpl/flow-list.html @@ -1,19 +1,19 @@ <div class="page-header"> - <h1>Flows (<%= nflows %>)</h1> + <h1>Flows (<%= nflows %>)</h1> </div> -<table class="table striped-table flow-table"> - <thead><tr><th>Cookie</th><th>Priority</th><th>Match</th><th>Action</th><th>Packets</th><th>Bytes</th><th>Age</th><th>Timeout</th></tr></thead> - <tbody> - <!-- flows will be inserted here by FlowListView:render --> - </tbody> +<table class="table table-striped flow-table"> + <thead><tr><th>Cookie</th><th>Priority</th><th>Match</th><th>Action</th><th>Packets</th><th>Bytes</th><th>Age</th><th>Timeout</th></tr></thead> + <tbody> + <!-- flows will be inserted here by FlowListView:render --> + </tbody> </table> <!-- TODO implement pagination --> <!-- <div class="pagination pagination-right"><ul> - <li><a href="">←</a></li> - <li class="active"><a href="">1</a></li> - <li><a href="">2</a></li> - <li><a href="">→</a> + <li><a href="">←</a></li> + <li class="active"><a href="">1</a></li> + <li><a href="">2</a></li> + <li><a href="">→</a> </ul></div> --> \ No newline at end of file diff --git a/src/main/resources/web/tpl/header.html b/src/main/resources/web/tpl/header.html index ae4545bab75917dea2a1783b4f38146140c153a6..78045ef69aa31ab758eda3716232c18230a205dc 100644 --- a/src/main/resources/web/tpl/header.html +++ b/src/main/resources/web/tpl/header.html @@ -1,4 +1,3 @@ - <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> @@ -7,8 +6,7 @@ <span class="icon-bar"></span> <span class="icon-bar"></span> </a> - <!-- TODO we could use a better version of the OF logo without the drop shadow --> - <img src="img/floodlight.png" style="float:left;"> + <img src="img/floodlight.png" style="float:left;"> <div class="nav-collapse"> <ul class="nav"> @@ -19,10 +17,10 @@ <!-- <li><a href="/vlans">VLANs</a></li> --> </ul> <form id="searchForm" class="navbar-search pull-right dropdown"> - <input id="searchText" type="text" class="search-query dropdown-toggle" placeholder="Search (try an IP or MAC address)"> + <input id="searchText" type="text" class="search-query dropdown-toggle" + placeholder="Search (try an IP or MAC address)"> </form> - </div> - <!--/.nav-collapse --> + </div> <!--/.nav-collapse --> </div> </div> </div> diff --git a/src/main/resources/web/tpl/home.html b/src/main/resources/web/tpl/home.html index 1530c0f19f819a3b7ec9051d2822be0c612b0c22..6abf49441fef2210ae9866f74dd3a6e920dbfc8d 100644 --- a/src/main/resources/web/tpl/home.html +++ b/src/main/resources/web/tpl/home.html @@ -5,20 +5,3 @@ <div id="host-list"></div> <!-- <div id="vlan-list"></div> --> - -<!-- Floodlight doesn't provide an API for this -<div class="row"> -<div class="span12"> -<div class="page-header"> - <h1>Recent Events</h1> -</div> -<table class="table striped-table"> - <thead><tr><th>Time</th><th>Device</th><th>Message</th></tr></thead> - <tbody> - <tr><td>Fri Mar 30 15:16:52 CDT 2012</td><td>Switch <a href="">C</a></td><td><i class="icon-fire"></i> caught on fire.</td></tr> - <tr><td>Fri Mar 30 15:14:15 CDT 2012</td><td>Host <a href="">BroVM</a></td><td><i class="icon-resize-horizontal"></i> moved from <a href="">switch a port x</a> to <a href="">switch b port y</a>.</td></tr> - </tbody> -</table> -</div> -</div> ---> diff --git a/src/main/resources/web/tpl/host-list-item.html b/src/main/resources/web/tpl/host-list-item.html index bf6797f871ceab879b9a8795637a5d424c48d0f6..169477e3601ab775dc9a74782ef4c4571521b09b 100644 --- a/src/main/resources/web/tpl/host-list-item.html +++ b/src/main/resources/web/tpl/host-list-item.html @@ -1 +1 @@ - <td><a href="/host/<%= mac %>"><%= mac %></a></td><!-- <td><%= vlan %></td> --><td><%= ipv4 %></td><td><%= swport %></td><td><%= lastSeen %></td> + <td><a href="/host/<%= mac %>"><%= mac %></a></td><!-- <td><%= vlan %></td> --><td><%= ipv4 %></td><td><%= swport %></td><td><%= lastSeen %></td> diff --git a/src/main/resources/web/tpl/host-list.html b/src/main/resources/web/tpl/host-list.html index ec63b3a76cab06455e251644f4e61736b209e0f8..bc1f36437e40aeab513eac8e483cee065fc1ae1d 100644 --- a/src/main/resources/web/tpl/host-list.html +++ b/src/main/resources/web/tpl/host-list.html @@ -1,21 +1,22 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Hosts (<%= nhosts %>)</h1> + <h1>Hosts (<%= nhosts %>)</h1> </div> -<table class="table striped-table host-table"> - <thead><tr><th>MAC Address</th><!-- <th>VLAN</th> --><th>IP Address</th><th>Switch Port</th><th>Last Seen</th></tr></thead> - <tbody> - <!-- hosts will be inserted here by HostListView.render --> - </tbody> +<table class="table table-striped host-table"> + <thead><tr><th>MAC Address</th><!-- <th>VLAN</th> --><th>IP Address</th><th>Switch Port</th><th>Last Seen</th> + </tr></thead> + <tbody> + <!-- hosts will be inserted here by HostListView.render --> + </tbody> </table> <!-- TODO implement pagination --> -<!-- +<!-- <div class="pagination pagination-right"><ul> - <li><a href="">←</a></li> - <li class="active"><a href="">1</a></li> - <li><a href="">2</a></li> - <li><a href="">→</a> + <li><a href="">←</a></li> + <li class="active"><a href="">1</a></li> + <li><a href="">2</a></li> + <li><a href="">→</a> </ul></div> --> </div> diff --git a/src/main/resources/web/tpl/host.html b/src/main/resources/web/tpl/host.html index 1aa38015a1bedd1748b3f8c9da131e00e035bf07..985940f4bd54ace32a162db35f5fb524c89e3ddf 100644 --- a/src/main/resources/web/tpl/host.html +++ b/src/main/resources/web/tpl/host.html @@ -1,7 +1,7 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Host <%= id %></h1> + <h1>Host <%= id %></h1> </div> <p> <!-- VLAN: <a href="/vlan/<%= vlan %>"><%= vlan %></a><br> --> diff --git a/src/main/resources/web/tpl/port-list-item.html b/src/main/resources/web/tpl/port-list-item.html index 93252c961359c64769d9288c64ae1eee20f05324..bfc197844591a942baf2bd6807be2516683f0ec6 100644 --- a/src/main/resources/web/tpl/port-list-item.html +++ b/src/main/resources/web/tpl/port-list-item.html @@ -1 +1 @@ - <td><a id="<%= portNumber %>"><%= name %></a></td><td><%= status %></td><td><%= transmitBytes %></td><td><%= receiveBytes %></td><td><%= transmitPackets %></td><td><%= receivePackets %></td><td><%= dropped %></td><td><%= errors %></td> + <td><a id="<%= portNumber %>"><%= name %></a></td><td><%= status %></td><td><%= transmitBytes %></td><td><%= receiveBytes %></td><td><%= transmitPackets %></td><td><%= receivePackets %></td><td><%= dropped %></td><td><%= errors %></td> diff --git a/src/main/resources/web/tpl/port-list.html b/src/main/resources/web/tpl/port-list.html index d4c30606af8270b512d537497c8c144e732e377d..9ceb6c3346a78583b713bc0f5d30aa7867b82a20 100644 --- a/src/main/resources/web/tpl/port-list.html +++ b/src/main/resources/web/tpl/port-list.html @@ -1,19 +1,19 @@ <div class="page-header"> - <h1>Ports (<%= nports %>)</h1> + <h1>Ports (<%= nports %>)</h1> </div> -<table class="table striped-table port-table"> - <thead><tr><th>#</th><th>Link Status</th><th>TX Bytes</th><th>RX Bytes</th><th>TX Pkts</th><th>RX Pkts</th><th>Dropped</th><th>Errors</th></tr></thead> - <tbody> - <!-- ports will be inserted here by PortListView:render --> - </tbody> +<table class="table table-striped port-table"> + <thead><tr><th>#</th><th>Link Status</th><th>TX Bytes</th><th>RX Bytes</th><th>TX Pkts</th><th>RX Pkts</th><th>Dropped</th><th>Errors</th></tr></thead> + <tbody> + <!-- ports will be inserted here by PortListView:render --> + </tbody> </table> <!-- TODO implement pagination --> -<!-- +<!-- <div class="pagination pagination-right"><ul> - <li><a href="">←</a></li> - <li class="active"><a href="">1</a></li> - <li><a href="">2</a></li> - <li><a href="">→</a> + <li><a href="">←</a></li> + <li class="active"><a href="">1</a></li> + <li><a href="">2</a></li> + <li><a href="">→</a> </ul></div> --> \ No newline at end of file diff --git a/src/main/resources/web/tpl/status.html b/src/main/resources/web/tpl/status.html index 86b77bf396f5a30cfb946bf7a6f13c06bd69395a..5a768697476a047bf734aec5281aaf993475b1b0 100644 --- a/src/main/resources/web/tpl/status.html +++ b/src/main/resources/web/tpl/status.html @@ -1,13 +1,13 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Controller Status</h1> + <h1>Controller Status</h1> </div> <table class="status-table"> - <tr><td class="status-head">Hostname:<td><%= host %>:<%= ofport %></td></tr> - <tr><td class="status-head">Healthy:<td><%= healthy %></td> - <tr><td class="status-head">Uptime:<td><%= uptime %><td> - <tr><td class="status-head">JVM memory bloat:<td><%= free %> free out of <%= total %></td> - <Tr><td class="status-head">Modules loaded:<td><%= moduleText %></td> + <tr><td class="status-head">Hostname:<td><%= host %>:<%= ofport %></td></tr> + <tr><td class="status-head">Healthy:<td><%= healthy %></td> + <tr><td class="status-head">Uptime:<td><%= uptime %><td> + <tr><td class="status-head">JVM memory bloat:<td><%= free %> free out of <%= total %></td> + <Tr><td class="status-head">Modules loaded:<td><%= moduleText %></td> </div> </div> diff --git a/src/main/resources/web/tpl/switch-list-item.html b/src/main/resources/web/tpl/switch-list-item.html index 7a20a5a09ad02cb3123d38e4029ddb7c4c0e98c9..7ce026278f23f7e060214a660576bd8ce1ef79bf 100644 --- a/src/main/resources/web/tpl/switch-list-item.html +++ b/src/main/resources/web/tpl/switch-list-item.html @@ -1 +1 @@ -<td><a href="/switch/<%= id %>"><%= id %></a></td><td><%= manufacturerDescription %><td><%= packetCount %></td><td><%= byteCount %></td><td><%= flowCount %></td> + <td><a href="/switch/<%= id %>"><%= id %></a></td><td><%= inetAddress %></td><td><%= manufacturerDescription %><td><%= packetCount %></td><td><%= byteCount %></td><td><%= flowCount %></td><td><%= connectedSince %></td> diff --git a/src/main/resources/web/tpl/switch-list.html b/src/main/resources/web/tpl/switch-list.html index 45c02f55f13b1024d9882bb489cda6cce8f6c8f4..e7dac09c1d84bc23d50724de1dd46e547fc9fb66 100644 --- a/src/main/resources/web/tpl/switch-list.html +++ b/src/main/resources/web/tpl/switch-list.html @@ -1,21 +1,21 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Switches (<%= nswitches %>)</h1> + <h1>Switches (<%= nswitches %>)</h1> </div> -<table class="table striped-table switch-table"> - <thead><tr><th>DPID</th><th>Vendor</th><th>Packets</th><th>Bytes</th><th>Flows</th></tr></thead> - <tbody> - <!-- switches will be inserted here by SwitchListView:render --> - </tbody> +<table class="table table-striped switch-table"> + <thead><tr><th>DPID</th><th>IP Address</th><th>Vendor</th><th>Packets</th><th>Bytes</th><th>Flows</th><th>Connected Since</th></tr></thead> + <tbody> + <!-- switches will be inserted here by SwitchListView:render --> + </tbody> </table> <!-- TODO implement pagination --> -<!-- +<!-- <div class="pagination pagination-right"><ul> - <li><a href="">←</a></li> - <li class="active"><a href="">1</a></li> - <li><a href="">2</a></li> - <li><a href="">→</a> + <li><a href="">←</a></li> + <li class="active"><a href="">1</a></li> + <li><a href="">2</a></li> + <li><a href="">→</a> </ul></div> --> </div> diff --git a/src/main/resources/web/tpl/switch.html b/src/main/resources/web/tpl/switch.html index bfba96795031241c30500042d1331bd6ba5b400e..af89797e9c7bbef6d5f7e80ce7b6e35f08a609d3 100644 --- a/src/main/resources/web/tpl/switch.html +++ b/src/main/resources/web/tpl/switch.html @@ -1,13 +1,13 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Switch <%= id %></h1> + <h1>Switch <%= id %> <%= inetAddress %></h1> </div> -<p><%= manufacturerDescription %><br> +<p>Connected since <%= connectedSince %><br> +<%= manufacturerDescription %><br> <%= hardwareDescription %><br> <%= softwareDescription %><br> S/N: <%= serialNumber %><br> -<!-- <%= flowCount %> flows --> </p> <div id="port-list"></div> <!-- TODO would be nice to make this collapsible --> diff --git a/src/main/resources/web/tpl/topology.html b/src/main/resources/web/tpl/topology.html index feb88df84431f305dad86691cea877f7b566f4aa..ce77cc75b550a8c64973eb96d403a2ea943d66ec 100644 --- a/src/main/resources/web/tpl/topology.html +++ b/src/main/resources/web/tpl/topology.html @@ -1,7 +1,7 @@ <div class="row"> <div class="span12"> <div class="page-header"> - <h1>Network Topology</h1> + <h1>Network Topology</h1> </div> <div id="topology-graph"></div> </div> diff --git a/src/main/resources/web/tpl/vlan-list-item.html b/src/main/resources/web/tpl/vlan-list-item.html index 785517b0ac42ad319e064e2e434f0a9f5680d39f..55a2f35567b78f6497c7ce2ce43538fc6b699c40 100644 --- a/src/main/resources/web/tpl/vlan-list-item.html +++ b/src/main/resources/web/tpl/vlan-list-item.html @@ -1 +1 @@ - <tr><td><a href="/vlan/<%= id %>"><%= id %></a></td><td><%= name %></td><td><%= nhosts %></td></tr> + <tr><td><a href="/vlan/<%= id %>"><%= id %></a></td><td><%= name %></td><td><%= nhosts %></td></tr> diff --git a/src/test/java/net/floodlightcontroller/core/module/FloodlightTestModuleLoader.java b/src/test/java/net/floodlightcontroller/core/module/FloodlightTestModuleLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..2ba838e51fc268ac0a7ea7086b90d9476950a638 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/core/module/FloodlightTestModuleLoader.java @@ -0,0 +1,182 @@ +package net.floodlightcontroller.core.module; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.floodlightcontroller.core.module.FloodlightModuleLoader; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.test.MockFloodlightProvider; +import net.floodlightcontroller.core.test.MockThreadPoolService; +import net.floodlightcontroller.counter.NullCounterStore; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; +import net.floodlightcontroller.devicemanager.test.MockDeviceManager; +import net.floodlightcontroller.perfmon.NullPktInProcessingTime; +import net.floodlightcontroller.storage.memory.MemoryStorageSource; +import net.floodlightcontroller.topology.TopologyManager; + +public class FloodlightTestModuleLoader extends FloodlightModuleLoader { + protected static Logger log = LoggerFactory.getLogger(FloodlightTestModuleLoader.class); + + // List of default modules to use unless specified otherwise + public static final Class<? extends IFloodlightModule> DEFAULT_STORAGE_SOURCE = + MemoryStorageSource.class; + public static final Class<? extends IFloodlightModule> DEFAULT_FLOODLIGHT_PRPOVIDER = + MockFloodlightProvider.class; + public static final Class<? extends IFloodlightModule> DEFAULT_TOPOLOGY_PROVIDER = + TopologyManager.class; + public static final Class<? extends IFloodlightModule> DEFAULT_DEVICE_SERVICE = + MockDeviceManager.class; + public static final Class<? extends IFloodlightModule> DEFAULT_COUNTER_STORE = + NullCounterStore.class; + public static final Class<? extends IFloodlightModule> DEFAULT_THREADPOOL = + MockThreadPoolService.class; + public static final Class<? extends IFloodlightModule> DEFAULT_ENTITY_CLASSIFIER = + DefaultEntityClassifier.class; + public static final Class<? extends IFloodlightModule> DEFAULT_PERFMON = + NullPktInProcessingTime.class; + + protected static final Collection<Class<? extends IFloodlightModule>> DEFAULT_MODULE_LIST; + + static { + DEFAULT_MODULE_LIST = new ArrayList<Class<? extends IFloodlightModule>>(); + DEFAULT_MODULE_LIST.add(DEFAULT_DEVICE_SERVICE); + DEFAULT_MODULE_LIST.add(DEFAULT_FLOODLIGHT_PRPOVIDER); + DEFAULT_MODULE_LIST.add(DEFAULT_STORAGE_SOURCE); + DEFAULT_MODULE_LIST.add(DEFAULT_TOPOLOGY_PROVIDER); + DEFAULT_MODULE_LIST.add(DEFAULT_COUNTER_STORE); + DEFAULT_MODULE_LIST.add(DEFAULT_THREADPOOL); + DEFAULT_MODULE_LIST.add(DEFAULT_ENTITY_CLASSIFIER); + DEFAULT_MODULE_LIST.add(DEFAULT_PERFMON); + } + + protected IFloodlightModuleContext fmc; + + /** + * Adds default modules to the list of modules to load. This is done + * in order to avoid the module loader throwing errors about duplicate + * modules and neither one is specified by the user. + * @param userModules The list of user specified modules to add to. + */ + protected void addDefaultModules(Collection<Class<? extends IFloodlightModule>> userModules) { + Collection<Class<? extends IFloodlightModule>> defaultModules = + new ArrayList<Class<? extends IFloodlightModule>>(DEFAULT_MODULE_LIST.size()); + defaultModules.addAll(DEFAULT_MODULE_LIST); + + Iterator<Class<? extends IFloodlightModule>> modIter = userModules.iterator(); + while (modIter.hasNext()) { + Class<? extends IFloodlightModule> userMod = modIter.next(); + Iterator<Class<? extends IFloodlightModule>> dmIter = defaultModules.iterator(); + while (dmIter.hasNext()) { + Class<? extends IFloodlightModule> dmMod = dmIter.next(); + Collection<Class<? extends IFloodlightService>> userModServs; + Collection<Class<? extends IFloodlightService>> dmModServs; + try { + dmModServs = dmMod.newInstance().getModuleServices(); + userModServs = userMod.newInstance().getModuleServices(); + } catch (InstantiationException e) { + log.error(e.getMessage()); + break; + } catch (IllegalAccessException e) { + log.error(e.getMessage()); + break; + } + + // If either of these are null continue as they have no services + if (dmModServs == null || userModServs == null) continue; + + // If the user supplied modules has a service + // that is in the default module list we remove + // the default module from the list. + boolean shouldBreak = false; + Iterator<Class<? extends IFloodlightService>> userModServsIter + = userModServs.iterator(); + while (userModServsIter.hasNext()) { + Class<? extends IFloodlightService> userModServIntf = userModServsIter.next(); + Iterator<Class<? extends IFloodlightService>> dmModsServsIter + = dmModServs.iterator(); + while (dmModsServsIter.hasNext()) { + Class<? extends IFloodlightService> dmModServIntf + = dmModsServsIter.next(); + + if (dmModServIntf.getCanonicalName().equals( + userModServIntf.getCanonicalName())) { + logger.debug("Removing default module {} because it was " + + "overriden by an explicitly specified module", + dmModServIntf.getCanonicalName()); + dmIter.remove(); + shouldBreak = true; + break; + } + } + if (shouldBreak) break; + } + if (shouldBreak) break; + } + } + + // Append the remaining default modules to the user specified ones. + // This avoids the module loader throwing duplicate module errors. + userModules.addAll(defaultModules); + log.debug("Using module set " + userModules.toString()); + } + + /** + * Sets up all modules and their dependencies. + * @param modules The list of modules that the user wants to load. + * @param mockedServices The list of services that will be mocked. Any + * module that provides this service will not be loaded. + */ + public void setupModules(Collection<Class<? extends IFloodlightModule>> modules, + Collection<IFloodlightService> mockedServices) { + addDefaultModules(modules); + Collection<String> modulesAsString = new ArrayList<String>(); + for (Class<? extends IFloodlightModule> m : modules) { + modulesAsString.add(m.getCanonicalName()); + } + + try { + fmc = loadModulesFromList(modulesAsString, null, mockedServices); + } catch (FloodlightModuleException e) { + log.error(e.getMessage()); + } + } + + /** + * Gets the inited/started instance of a module from the context. + * @param ifl The name if the module to get, i.e. "LearningSwitch.class". + * @return The inited/started instance of the module. + */ + public IFloodlightModule getModuleByName(Class<? extends IFloodlightModule> ifl) { + Collection<IFloodlightModule> modules = fmc.getAllModules(); + for (IFloodlightModule m : modules) { + if (ifl.getCanonicalName().equals(m.getClass().getCanonicalName())) { + return m; + } + } + return null; + } + + /** + * Gets an inited/started instance of a service from the context. + * @param ifs The name of the service to get, i.e. "ITopologyService.class". + * @return The inited/started instance of the service from teh context. + */ + public IFloodlightService getModuleByService(Class<? extends IFloodlightService> ifs) { + Collection<IFloodlightModule> modules = fmc.getAllModules(); + for (IFloodlightModule m : modules) { + Collection<Class<? extends IFloodlightService>> mServs = m.getModuleServices(); + if (mServs == null) continue; + for (Class<? extends IFloodlightService> mServClass : mServs) { + if (mServClass.getCanonicalName().equals(ifs.getCanonicalName())) { + assert(m instanceof IFloodlightService); + return (IFloodlightService)m; + } + } + } + return null; + } +} diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java index 577e5ded581e9326e9d30fd452e4fbb73c8b2ba9..3daa04f5248c19190a68658746051134651ac0a8 100644 --- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java +++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java @@ -19,6 +19,7 @@ package net.floodlightcontroller.core.test; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -199,28 +200,31 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { - // TODO Auto-generated method stub - return null; + Collection<Class<? extends IFloodlightService>> services = + new ArrayList<Class<? extends IFloodlightService>>(1); + services.add(IFloodlightProviderService.class); + return services; } @Override public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { - // TODO Auto-generated method stub - return null; + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + m.put(IFloodlightProviderService.class, this); + return m; } @Override public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - // TODO Auto-generated method stub - return null; + return null; } @Override - public - void - init(FloodlightModuleContext context) + public void init(FloodlightModuleContext context) throws FloodlightModuleException { // TODO Auto-generated method stub diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 2efcd4cf6b4bedad26da9908a8d3a3ef63a77269..5626a543ace242a99ee027b68d375d664930a58b 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -19,13 +19,17 @@ package net.floodlightcontroller.devicemanager.internal; import static org.easymock.EasyMock.*; + +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import static org.easymock.EasyMock.expectLastCall; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -779,12 +783,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(device, rdevice); } + /** * Note: Entity expiration does not result in device moved notification. * @throws Exception */ - @Test - public void testEntityExpiration() throws Exception { + public void doTestEntityExpiration() throws Exception { IDeviceListener mockListener = createStrictMock(IDeviceListener.class); mockListener.deviceIPV4AddrChanged(isA(IDevice.class)); @@ -854,8 +858,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockListener); } - @Test - public void testDeviceExpiration() throws Exception { + public void doTestDeviceExpiration() throws Exception { Calendar c = Calendar.getInstance(); c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime()); @@ -895,7 +898,104 @@ public class DeviceManagerImplTest extends FloodlightTestCase { r = deviceManager.findDevice(1L, null, null, null, null); assertNull(r); } - + + /* + * A ConcurrentHashMap for devices (deviceMap) that can be used to test + * code that specially handles concurrent modification situations. In + * particular, we overwrite values() and will replace / remove all the + * elements returned by values. + * + * The remove flag in the constructor specifies if devices returned by + * values() should be removed or replaced. + */ + protected static class ConcurrentlyModifiedDeviceMap + extends ConcurrentHashMap<Long, Device> { + private static final long serialVersionUID = 7784938535441180562L; + protected boolean remove; + public ConcurrentlyModifiedDeviceMap(boolean remove) { + super(); + this.remove = remove; + } + + @Override + public Collection<Device> values() { + // Get the values from the real map and copy them since + // the collection returned by values can reflect changed + Collection<Device> devs = new ArrayList<Device>(super.values()); + for (Device d: devs) { + if (remove) { + // We remove the device from the underlying map + super.remove(d.getDeviceKey()); + } else { + super.remove(d.getDeviceKey()); + // We add a different Device instance with the same + // key to the map. We'll do some hackery so the device + // is different enough to compare differently in equals + // but otherwise looks the same. + // It's ugly but it works. + Entity[] curEntities = new Entity[d.getEntities().length]; + int i = 0; + // clone entities + for (Entity e: d.getEntities()) { + curEntities[i] = new Entity (e.macAddress, + e.vlan, + e.ipv4Address, + e.switchDPID, + e.switchPort, + e.lastSeenTimestamp); + if (e.vlan == null) + curEntities[i].vlan = (short)1; + else + curEntities[i].vlan = (short)((e.vlan + 1 % 4095)+1); + i++; + } + Device newDevice = new Device(d, curEntities[0]); + newDevice.entities = curEntities; + assertEquals(false, newDevice.equals(d)); + super.put(newDevice.getDeviceKey(), newDevice); + } + } + return devs; + } + } + + @Test + public void testEntityExpiration() throws Exception { + doTestEntityExpiration(); + } + + @Test + public void testDeviceExpiration() throws Exception { + doTestDeviceExpiration(); + } + + /* Test correct entity cleanup behavior when a concurrent modification + * occurs. + */ + @Test + public void testEntityExpirationConcurrentModification() throws Exception { + deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false); + doTestEntityExpiration(); + } + + /* Test correct entity cleanup behavior when a concurrent remove + * occurs. + */ + @Test + public void testDeviceExpirationConcurrentRemove() throws Exception { + deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(true); + doTestDeviceExpiration(); + } + + /* Test correct entity cleanup behavior when a concurrent modification + * occurs. + */ + @Test + public void testDeviceExpirationConcurrentModification() throws Exception { + deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false); + doTestDeviceExpiration(); + } + /* @Test public void testAttachmentPointFlapping() throws Exception { diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 2f952254c310c2216a489d0b9d606311e4b8b60f..9da4359cf8a34aa4f771e72ec433364a1169003b 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -97,10 +97,30 @@ public class ForwardingTest extends FloodlightTestCase { public void setUp() throws Exception { super.setUp(); - // Mock context cntx = new FloodlightContext(); + + // Module loader setup + /* + Collection<Class<? extends IFloodlightModule>> mods = new ArrayList<Class<? extends IFloodlightModule>>(); + Collection<IFloodlightService> mockedServices = new ArrayList<IFloodlightService>(); + mods.add(Forwarding.class); + routingEngine = createMock(IRoutingService.class); + topology = createMock(ITopologyService.class); + mockedServices.add(routingEngine); + mockedServices.add(topology); + FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader(); + fml.setupModules(mods, mockedServices); + mockFloodlightProvider = + (MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class); + deviceManager = + (MockDeviceManager) fml.getModuleByName(MockDeviceManager.class); + threadPool = + (MockThreadPoolService) fml.getModuleByName(MockThreadPoolService.class); + forwarding = + (Forwarding) fml.getModuleByName(Forwarding.class); + */ mockFloodlightProvider = getMockFloodlightProvider(); - forwarding = getForwarding(); + forwarding = new Forwarding(); threadPool = new MockThreadPoolService(); deviceManager = new MockDeviceManager(); flowReconcileMgr = new FlowReconcileManager(); @@ -251,10 +271,6 @@ public class ForwardingTest extends FloodlightTestCase { srcDevice); } - private Forwarding getForwarding() { - return new Forwarding(); - } - @Test public void testForwardMultiSwitchPath() throws Exception { diff --git a/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java b/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java index 0272de2dac25cb6f6cd6839522d692f023201ae2..a68a1b8734fca292dddedb3121d76ef64b7fe346 100644 --- a/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java +++ b/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java @@ -22,21 +22,20 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; -import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.counter.CounterStore; -import net.floodlightcontroller.counter.ICounterStoreService; +import net.floodlightcontroller.core.module.FloodlightTestModuleLoader; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.packet.Data; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.packet.UDP; -import net.floodlightcontroller.restserver.IRestApiService; -import net.floodlightcontroller.restserver.RestApiServer; import net.floodlightcontroller.test.FloodlightTestCase; import org.junit.Before; @@ -69,18 +68,15 @@ public class LearningSwitchTest extends FloodlightTestCase { @Before public void setUp() throws Exception { super.setUp(); - - FloodlightModuleContext fmc = new FloodlightModuleContext(); - fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); - fmc.addService(ICounterStoreService.class, new CounterStore()); - RestApiServer restApi = new RestApiServer(); - fmc.addService(IRestApiService.class, restApi); - restApi.init(fmc); - restApi.startUp(fmc); - learningSwitch = new LearningSwitch(); - learningSwitch.init(fmc); - learningSwitch.startUp(fmc); - + FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader(); + Collection<Class<? extends IFloodlightModule>> mods + = new ArrayList<Class<? extends IFloodlightModule>>(); + mods.add(LearningSwitch.class); + fml.setupModules(mods, null); + learningSwitch = (LearningSwitch) fml.getModuleByName(LearningSwitch.class); + mockFloodlightProvider = + (MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class); + // Build our test packet this.testPacket = new Ethernet() .setDestinationMACAddress("00:11:22:33:44:55") @@ -153,7 +149,7 @@ public class LearningSwitchTest extends FloodlightTestCase { // Mock up our expected behavior IOFSwitch mockSwitch = createMock(IOFSwitch.class); - expect(mockSwitch.getStringId()).andReturn("00:11:22:33:44:55:66:77"); + expect(mockSwitch.getStringId()).andReturn("00:11:22:33:44:55:66:77").anyTimes(); mockSwitch.write(po, null); // Start recording the replay on the mocks @@ -161,6 +157,7 @@ public class LearningSwitchTest extends FloodlightTestCase { // Get the listener and trigger the packet in IOFMessageListener listener = mockFloodlightProvider.getListeners().get( OFType.PACKET_IN).get(0); + // Make sure it's the right listener listener.receive(mockSwitch, this.packetIn, parseAndAnnotate(this.packetIn)); // Verify the replay matched our expectations @@ -214,7 +211,6 @@ public class LearningSwitchTest extends FloodlightTestCase { expect(mockSwitch.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer) (OFMatch.OFPFW_IN_PORT | OFMatch.OFPFW_NW_PROTO | OFMatch.OFPFW_TP_SRC | OFMatch.OFPFW_TP_DST | OFMatch.OFPFW_NW_SRC_ALL | OFMatch.OFPFW_NW_DST_ALL | OFMatch.OFPFW_NW_TOS)); - expect(mockSwitch.getStringId()).andReturn("00:11:22:33:44:55:66:77").anyTimes(); mockSwitch.write(fm1, null); mockSwitch.write(fm2, null); diff --git a/src/test/java/net/floodlightcontroller/packet/DHCPTest.java b/src/test/java/net/floodlightcontroller/packet/DHCPTest.java index 1f3a48585bcfa2b949dec07570ae25e71d912949..b83ffa816e196873b00158426a0177347c92905a 100644 --- a/src/test/java/net/floodlightcontroller/packet/DHCPTest.java +++ b/src/test/java/net/floodlightcontroller/packet/DHCPTest.java @@ -216,7 +216,71 @@ public class DHCPTest extends TestCase { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; - + + public byte[] dhcpPacket3 = new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0x74, (byte) 0x44, (byte) 0x01, (byte) 0x72, + (byte) 0xd8, (byte) 0x41, (byte) 0x08, (byte) 0x00, (byte) 0x45, + (byte) 0x00, (byte) 0x01, (byte) 0x1f, (byte) 0x48, (byte) 0xcd, + (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0x6f, + (byte) 0x6a, (byte) 0xc0, (byte) 0xa8, (byte) 0x00, (byte) 0xef, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, + (byte) 0x44, (byte) 0x00, (byte) 0x43, (byte) 0x01, (byte) 0x0b, + (byte) 0xb3, (byte) 0x0f, (byte) 0x01, (byte) 0x01, (byte) 0x06, + (byte) 0x00, (byte) 0x82, (byte) 0x88, (byte) 0xa6, (byte) 0xc9, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x74, (byte) 0x44, (byte) 0x01, (byte) 0x72, (byte) 0xd8, + (byte) 0x41, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x63, (byte) 0x82, + (byte) 0x53, (byte) 0x63, (byte) 0x35, (byte) 0x01, (byte) 0x01, + (byte) 0x32, (byte) 0x04, (byte) 0xc0, (byte) 0xa8, (byte) 0x0a, + (byte) 0xa9, (byte) 0x39, (byte) 0x02, (byte) 0x02, (byte) 0x40, + (byte) 0x37, (byte) 0x03, (byte) 0x01, (byte) 0x03, (byte) 0x06, + (byte) 0xff + }; + public byte[] dhcpPacketPXE = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x19, (byte) 0xb9, (byte) 0xb0, (byte) 0x01, (byte) 0x44, @@ -444,6 +508,24 @@ public class DHCPTest extends TestCase { resetChecksumsAndLengths(ipv4, udp); assertEquals(DHCP.OPCODE_REQUEST, dhcp.getOpCode()); } + + public void testDeSerializeReSerialize() { + Ethernet eth = new Ethernet(); + eth.deserialize(dhcpPacket3, 0, dhcpPacket3.length); + assertTrue(eth.getPayload() instanceof IPv4); + IPv4 ipv4 = (IPv4) eth.getPayload(); + assertTrue(ipv4.getPayload() instanceof UDP); + + byte[] serializedPacket = eth.serialize(); + Ethernet eth2 = new Ethernet(); + eth2.deserialize(serializedPacket, 0, serializedPacket.length); + IPv4 ipv42 = (IPv4) eth2.getPayload(); + + short ipchecksum = ipv42.getChecksum(); + ipv42.setChecksum((short) 0); + eth2.serialize(); + assertEquals(ipchecksum, ipv42.getChecksum()); + } public void testDeSerializePXE() { Ethernet eth = new Ethernet(); diff --git a/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java b/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java index 550cd1f520dc5f6ee6b8b9816adbfba07c7d501e..b0e83cc115fbc7293be6ec70dae7a78dbd325a3e 100644 --- a/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java +++ b/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java @@ -87,9 +87,9 @@ public class FloodlightTestCase extends TestCase { public void setUp() throws Exception { mockFloodlightProvider = new MockFloodlightProvider(); } - + @Test - public void testSanity() { - assertTrue(true); + public void testSanity() throws Exception { + assertTrue(true); } }