diff --git a/.gitignore b/.gitignore index dcc106a8e1436ff767ad147545bdfe308800b665..6bb47c581906a2d592173e1b0532486f4ace9bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ thrift *.swp *.pyc findbugs-results +*.launch diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..710e6dc392122d7fbf59b48cb9640ddbf9ae3dc8 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +Floodlight is the leading open source SDN controller. It is supported by a community of developers including a number of engineers from Big Switch Networks (http://www.bigswitch.com/). + +OpenFlow is a open standard managed by Open Networking Foundation. It specifies a protocol through switch a remote controller can modify the behavior of networking devices through a well-defined “forwarding instruction setâ€. Floodlight is designed to work with the growing number of switches, routers, virtual witches, and access points that support the OpenFlow standard. + +Feature Highlights: + +- Offers a module loading system that make it simple to extend and enhance. +- Easy to set up with minimal dependencies +- Supports a broad range of virtual- and physical- OpenFlow switches\n +- Can handle mixed OpenFlow and non-OpenFlow networks – it can manage multiple “islands†of OpenFlow hardware switches\n +- Designed to be high-performance – is the core of a commercial product from Big Switch Networks.\n +- Support for OpenStack Quantum cloud orchestration platform} + +To download a pre-built VM appliance, access documentation, and sign up for the mailing list, go to: + + http://www.projectfloodlight.org/floodlight diff --git a/README.txt b/README.txt deleted file mode 100644 index 44f509d5fb35d5874e331a94f520ef4b05273ae3..0000000000000000000000000000000000000000 --- a/README.txt +++ /dev/null @@ -1,10 +0,0 @@ - - Floodlight - An Apache licensed, Java based OpenFlow controller - -Floodlight is a Java based OpenFlow controller originally written by David Erickson at Stanford -University. It is available under the Apache 2.0 license. - -For documentation, forums, issue tracking and more visit: - - http://www.openflowhub.org/display/Floodlight/Floodlight+Home diff --git a/setup-eclipse.sh b/setup-eclipse.sh index 01a6a95ef0f34cee64e85d0707c3d5b29ca0d219..4224a3c5c982f4fee5768a0e5bd788ea36f0b765 100755 --- a/setup-eclipse.sh +++ b/setup-eclipse.sh @@ -25,6 +25,36 @@ cat >"$d/.project" <<EOF </projectDescription> EOF +cat >"$d/Floodlight-Default-Conf.launch" << EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> + <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/> + </listAttribute> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> + <listEntry value="1"/> + </listAttribute> + <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/> + <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/> + <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> +EOF + +cat > "$d/Floodlight-Quantum-Conf.launch" << EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> + <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/> + </listAttribute> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> + <listEntry value="1"/> + </listAttribute> + <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/> + <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-cf src/main/resources/quantum.properties"/> + <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/> + <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> +EOF cat >"$d/.classpath" <<EOF <?xml version="1.0" encoding="UTF-8"?> diff --git a/src/main/java/net/floodlightcontroller/core/FloodlightProvider.java b/src/main/java/net/floodlightcontroller/core/FloodlightProvider.java index bf7d6524b587d0535bfcd6e77a4aefff9eb7bff3..cfe315ddc2d0fd57f4fdeb7bf459b23c9bc28d7f 100644 --- a/src/main/java/net/floodlightcontroller/core/FloodlightProvider.java +++ b/src/main/java/net/floodlightcontroller/core/FloodlightProvider.java @@ -27,6 +27,11 @@ import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.counter.ICounterStoreService; +<<<<<<< HEAD +======= +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.flowcache.IFlowCacheService; +>>>>>>> bigswitch/master import net.floodlightcontroller.perfmon.IPktInProcessingTimeService; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.storage.IStorageSourceService; @@ -34,7 +39,7 @@ import net.floodlightcontroller.threadpool.IThreadPoolService; public class FloodlightProvider implements IFloodlightModule { Controller controller; - + @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { Collection<Class<? extends IFloodlightService>> services = @@ -47,9 +52,9 @@ public class FloodlightProvider implements IFloodlightModule { public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { controller = new Controller(); - + Map<Class<? extends IFloodlightService>, - IFloodlightService> m = + IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); m.put(IFloodlightProviderService.class, controller); @@ -64,6 +69,11 @@ public class FloodlightProvider implements IFloodlightModule { dependencies.add(IPktInProcessingTimeService.class); dependencies.add(IRestApiService.class); dependencies.add(ICounterStoreService.class); +<<<<<<< HEAD +======= + dependencies.add(IDebugCounterService.class); + dependencies.add(IFlowCacheService.class); +>>>>>>> bigswitch/master dependencies.add(IThreadPoolService.class); return dependencies; } @@ -76,6 +86,13 @@ public class FloodlightProvider implements IFloodlightModule { context.getServiceImpl(IPktInProcessingTimeService.class)); controller.setCounterStore( context.getServiceImpl(ICounterStoreService.class)); +<<<<<<< HEAD +======= + controller.setDebugCounter( + context.getServiceImpl(IDebugCounterService.class)); + controller.setFlowCacheMgr( + context.getServiceImpl(IFlowCacheService.class)); +>>>>>>> bigswitch/master controller.setRestApiService( context.getServiceImpl(IRestApiService.class)); controller.setThreadPoolService( diff --git a/src/main/java/net/floodlightcontroller/core/IListener.java b/src/main/java/net/floodlightcontroller/core/IListener.java index 1bd656022d257cac1508eb80e064b79e03fbfdbc..9a3b8af9182196ad2fa2aa834d8734ff32657311 100644 --- a/src/main/java/net/floodlightcontroller/core/IListener.java +++ b/src/main/java/net/floodlightcontroller/core/IListener.java @@ -31,9 +31,9 @@ public interface IListener<T> { /** * Check if the module called name is a callback ordering prerequisite * for this module. In other words, if this function returns true for - * the given name, then this message listener will be called after that + * the given name, then this listener will be called after that * message listener. - * @param type the message type to which this applies + * @param type the object type to which this applies * @param name the name of the module * @return whether name is a prerequisite. */ @@ -42,9 +42,9 @@ public interface IListener<T> { /** * Check if the module called name is a callback ordering post-requisite * for this module. In other words, if this function returns true for - * the given name, then this message listener will be called before that + * the given name, then this listener will be called before that * message listener. - * @param type the message type to which this applies + * @param type the object type to which this applies * @param name the name of the module * @return whether name is a post-requisite. */ diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index 428dc7f7e07ceb0598b2854668d6705a2cfa7efe..6bc92c98fae1e9866c99606d352f6115d1b0c618 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -107,7 +107,28 @@ public interface IOFSwitch { public void setChannel(Channel channel); /** - * Writes to the OFMessage to the output stream. + * Write OFMessage to the output stream, subject to switch rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners + * @param msg + * @param cntx + * @throws IOException + */ + public void writeThrottled(OFMessage msg, FloodlightContext cntx) throws IOException; + + /** + * Writes the list of messages to the output stream, subject to rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners. + * @param msglist + * @param bc + * @throws IOException + */ + void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) + throws IOException; + + /** + * Writes to the OFMessage to the output stream, bypassing rate limiting. * The message will be handed to the floodlightProvider for possible filtering * and processing by message listeners * @param m @@ -117,7 +138,7 @@ public interface IOFSwitch { public void write(OFMessage m, FloodlightContext bc) throws IOException; /** - * Writes the list of messages to the output stream + * Writes the list of messages to the output stream, bypassing rate limiting. * The message will be handed to the floodlightProvider for possible filtering * and processing by message listeners. * @param msglist @@ -488,5 +509,4 @@ public interface IOFSwitch { * @return */ public List<Short> getUplinkPorts(); - } diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java index 98eb372404b3bbcf490d5dcde5bd43baaee43afc..5c8248d5e88e5c4ce17d5a7768622e7cb2e79974 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java @@ -36,6 +36,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.internal.Controller; import net.floodlightcontroller.core.internal.OFFeaturesReplyFuture; import net.floodlightcontroller.core.internal.OFStatisticsFuture; @@ -48,6 +49,7 @@ import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.ser.ToStringSerializer; import org.jboss.netty.channel.Channel; +import org.openflow.protocol.OFBarrierRequest; import org.openflow.protocol.OFFeaturesReply; import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFMatch; @@ -109,6 +111,8 @@ public abstract class OFSwitchBase implements IOFSwitch { private final ReentrantReadWriteLock listenerLock; private final ConcurrentMap<Short, AtomicLong> portBroadcastCacheHitMap; + // Private members for throttling + private boolean writeThrottleEnabled = false; protected final static ThreadLocal<Map<IOFSwitch,List<OFMessage>>> local_msg_buffer = new ThreadLocal<Map<IOFSwitch,List<OFMessage>>>() { @@ -181,7 +185,52 @@ public abstract class OFSwitchBase implements IOFSwitch { public void setChannel(Channel channel) { this.channel = channel; } - + + // For driver subclass to set throttling + protected void enableWriteThrottle(boolean enable) { + this.writeThrottleEnabled = enable; + } + + @Override + @LogMessageDocs({ + @LogMessageDoc(level="WARN", + message="Drop throttled OF message to switch {switch}", + explanation="The controller is sending more messages" + + "than the switch can handle. Some messages are dropped" + + "to prevent switch outage", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + }) + public void writeThrottled(OFMessage m, FloodlightContext bc) + throws IOException { + /** + * By default, channel uses an unbounded send queue. Enable throttling + * prevents the queue from growing big. + * + * channel.isWritable() returns true when queue length is less than + * high water mark (64 kbytes). Once exceeded, isWritable() becomes + * false after queue length drops below low water mark (32 kbytes). + */ + if (!writeThrottleEnabled || channel.isWritable()) { + write(m, bc); + } else { + // Let logback duplicate filtering take care of excessive logs + // TODO Convert to counter and events + log.warn("Drop throttled OF message to switch {}", this); + } + } + + @Override + public void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) + throws IOException { + if (!writeThrottleEnabled || channel.isWritable()) { + write(msglist, bc); + } else { + // Let logback duplicate filtering take care of excessive logs + // TODO Convert to counter and events + log.warn("Drop throttled OF message to switch {}", this); + } + } + @Override public void write(OFMessage m, FloodlightContext bc) throws IOException { @@ -201,7 +250,6 @@ public abstract class OFSwitchBase implements IOFSwitch { msg_buffer.clear(); } } - @Override @LogMessageDoc(level="WARN", message="Sending OF message that modifies switch " + @@ -230,6 +278,11 @@ public abstract class OFSwitchBase implements IOFSwitch { this.write(msglist); } + /** + * Not callable by writers, but allow IOFSwitch implementation to override + * @param msglist + * @throws IOException + */ private void write(List<OFMessage> msglist) throws IOException { this.channel.write(msglist); } @@ -397,7 +450,7 @@ public abstract class OFSwitchBase implements IOFSwitch { this.iofMsgListenersMap.put(xid, caller); List<OFMessage> msglist = new ArrayList<OFMessage>(1); msglist.add(request); - this.channel.write(msglist); + this.write(msglist); return; } @@ -408,7 +461,7 @@ public abstract class OFSwitchBase implements IOFSwitch { this.statsFutureMap.put(request.getXid(), future); List<OFMessage> msglist = new ArrayList<OFMessage>(1); msglist.add(request); - this.channel.write(msglist); + this.write(msglist); return future; } @@ -509,10 +562,18 @@ public abstract class OFSwitchBase implements IOFSwitch { .setCommand(OFFlowMod.OFPFC_DELETE) .setOutPort(OFPort.OFPP_NONE) .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH)); + fm.setXid(getNextTransactionId()); + OFMessage barrierMsg = (OFBarrierRequest) + floodlightProvider.getOFMessageFactory().getMessage( + OFType.BARRIER_REQUEST); + barrierMsg.setXid(getNextTransactionId()); try { List<OFMessage> msglist = new ArrayList<OFMessage>(1); msglist.add(fm); - channel.write(msglist); + write(msglist); + msglist = new ArrayList<OFMessage>(1); + msglist.add(barrierMsg); + write(msglist); } catch (Exception e) { log.error("Failed to clear all flows on switch " + this, e); } @@ -630,7 +691,7 @@ public abstract class OFSwitchBase implements IOFSwitch { this.featuresFutureMap.put(request.getXid(), future); List<OFMessage> msglist = new ArrayList<OFMessage>(1); msglist.add(request); - this.channel.write(msglist); + this.write(msglist); return future; } diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index a71e7abc3da905873f60d23ed417b26414788e3a..b745f4636c41ece93bca66c043ec6865e4a38238 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -66,6 +66,11 @@ import net.floodlightcontroller.core.util.ListenerDispatcher; import net.floodlightcontroller.core.util.SingletonTask; import net.floodlightcontroller.core.web.CoreWebRoutable; import net.floodlightcontroller.counter.ICounterStoreService; +<<<<<<< HEAD +======= +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.flowcache.IFlowCacheService; +>>>>>>> bigswitch/master import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.perfmon.IPktInProcessingTimeService; import net.floodlightcontroller.restserver.IRestApiService; @@ -171,6 +176,11 @@ public class Controller implements IFloodlightProviderService, // Module dependencies protected IRestApiService restApi; protected ICounterStoreService counterStore = null; +<<<<<<< HEAD +======= + protected IDebugCounterService debugCounter; + protected IFlowCacheService bigFlowCacheMgr; +>>>>>>> bigswitch/master protected IStorageSourceService storageSource; protected IPktInProcessingTimeService pktinProcTime; protected IThreadPoolService threadPool; @@ -189,7 +199,7 @@ public class Controller implements IFloodlightProviderService, // we have sent to the listeners. On a transition to slave we first set // this role and then notify, on a transition to master we first notify // and then set the role. We then use it to make sure we don't forward - // OF messages while the modules are in slave role. + // OF messages while the modules are in slave role. // The pendingRole is a role change just received, but not sent out // notifications yet. protected Role pendingRole; @@ -217,7 +227,7 @@ public class Controller implements IFloodlightProviderService, protected static final String CONTROLLER_INTERFACE_DISCOVERED_IP = "discovered_ip"; // Perf. related configuration - protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; + protected int sendBufferSize = 4 * 1024 * 1024; public static final int BATCH_MAX_SIZE = 100; protected static final boolean ALWAYS_DECODE_ETH = true; @@ -300,7 +310,7 @@ public class Controller implements IFloodlightProviderService, newRole, oldRole); } // Set notified role to slave before notifying listeners. This - // stops OF messages from being sent to listeners + // stops OF messages from being sent to listeners if (newRole == Role.SLAVE) Controller.this.notifiedRole = newRole; if (haListeners != null) { @@ -308,7 +318,7 @@ public class Controller implements IFloodlightProviderService, listener.roleChanged(oldRole, newRole); } } - // Set notified role to master/equal after notifying listeners. + // Set notified role to master/equal after notifying listeners. // We now forward messages again if (newRole != Role.SLAVE) Controller.this.notifiedRole = newRole; @@ -361,6 +371,17 @@ public class Controller implements IFloodlightProviderService, this.counterStore = counterStore; } +<<<<<<< HEAD +======= + public void setDebugCounter(IDebugCounterService debugCounter) { + this.debugCounter = debugCounter; + } + + public void setFlowCacheMgr(IFlowCacheService flowCacheMgr) { + this.bigFlowCacheMgr = flowCacheMgr; + } + +>>>>>>> bigswitch/master public void setPktInProcessingService(IPktInProcessingTimeService pits) { this.pktinProcTime = pits; } @@ -663,6 +684,11 @@ public class Controller implements IFloodlightProviderService, // Flush all flow-mods/packet-out/stats generated from this "train" OFSwitchBase.flush_all(); counterStore.updateFlush(); +<<<<<<< HEAD +======= + debugCounter.flushCounters(); + bigFlowCacheMgr.updateFlush(); +>>>>>>> bigswitch/master } } @@ -1054,7 +1080,7 @@ public class Controller implements IFloodlightProviderService, if (sw.isConnected()) { // Only dispatch message if the switch is in the - // activeSwitch map and if the switches role is + // activeSwitch map and if the switches role is // not slave and the modules are not in slave // TODO: Should we dispatch messages that we expect to // receive when we're in the slave role, e.g. port @@ -1064,7 +1090,7 @@ public class Controller implements IFloodlightProviderService, // to them. On the other hand there might be special // modules that care about all of the connected switches // and would like to receive port status notifications. - if (sw.getHARole() == Role.SLAVE || + if (sw.getHARole() == Role.SLAVE || notifiedRole == Role.SLAVE || !activeSwitches.containsKey(sw.getId())) { // Don't log message if it's a port status message @@ -1092,19 +1118,31 @@ public class Controller implements IFloodlightProviderService, // Message handlers // **************** + @LogMessageDocs({ + @LogMessageDoc(message="Port modified on switch {switch}: {port} ", + explanation="Received notification from switch about port status change"), + @LogMessageDoc(message="Port added on switch {switch}: {port} ", + explanation="Received notification from switch about a new port addition"), + @LogMessageDoc(message="Port deleted on switch {switch}: port_no = {port} ", + explanation="Received notification from switch about a port removal"), + @LogMessageDoc(level="ERROR", + message="Failure adding update to queue", + explanation="Failed to add port status change to internal queue for processing", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + }) protected void handlePortStatusMessage(IOFSwitch sw, OFPortStatus m) { short portNumber = m.getDesc().getPortNumber(); OFPhysicalPort port = m.getDesc(); if (m.getReason() == (byte)OFPortReason.OFPPR_MODIFY.ordinal()) { sw.setPort(port); - log.debug("Port #{} modified for {}", portNumber, sw); + log.info("Port modified on switch {}: {}", sw, port); } else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) { sw.setPort(port); - log.debug("Port #{} added for {}", portNumber, sw); + log.info("Port added on switch {}: {}", sw, port); } else if (m.getReason() == (byte)OFPortReason.OFPPR_DELETE.ordinal()) { sw.deletePort(portNumber); - log.debug("Port #{} deleted for {}", portNumber, sw); + log.info("Port deleted on switch {}: port_no = {}", sw, portNumber); } SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED); try { @@ -1175,7 +1213,7 @@ public class Controller implements IFloodlightProviderService, FloodlightContext bContext) throws IOException { Ethernet eth = null; - + switch (m.getType()) { case PACKET_IN: OFPacketIn pi = (OFPacketIn)m; @@ -1241,7 +1279,10 @@ public class Controller implements IFloodlightProviderService, } pktinProcTime.recordEndTimePktIn(sw, m, bc); } else { - log.warn("Unhandled OF Message: {} from {}", m, sw); + if (m.getType() != OFType.BARRIER_REPLY) + log.warn("Unhandled OF Message: {} from {}", m, sw); + else + log.debug("Received a Barrier Reply, no listeners for it"); } if ((bContext == null) && (bc != null)) flcontext_free(bc); @@ -1692,7 +1733,7 @@ public class Controller implements IFloodlightProviderService, bootstrap.setOption("reuseAddr", true); bootstrap.setOption("child.keepAlive", true); bootstrap.setOption("child.tcpNoDelay", true); - bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); + bootstrap.setOption("child.sendBufferSize", sendBufferSize); ChannelPipelineFactory pfact = new OpenflowPipelineFactory(this, null); @@ -1748,7 +1789,11 @@ public class Controller implements IFloodlightProviderService, this.workerThreads = Integer.parseInt(threads); } log.debug("Number of worker threads set to {}", this.workerThreads); - + String sendBufferStr = configParams.get("sendBufferSize"); + if (sendBufferStr != null) { + this.sendBufferSize = Integer.parseInt(sendBufferStr); + } + log.debug("Send buffer size set to {}", sendBufferSize); } private void initVendorMessages() { diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java index 77b7e76c79717189a825715fff7c96d3b510d46b..2ebe41e3ca404992a379cc7ec12d4adc7461cc46 100644 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java @@ -25,96 +25,97 @@ import java.util.Map; * @author alexreimers */ public class FloodlightModuleContext implements IFloodlightModuleContext { - protected Map<Class<? extends IFloodlightService>, IFloodlightService> serviceMap; - protected Map<Class<? extends IFloodlightModule>, Map<String, String>> configParams; - protected Collection<IFloodlightModule> moduleSet; - - /** - * Creates the ModuleContext for use with this IFloodlightProvider. - * This will be used as a module registry for all IFloodlightModule(s). - */ - public FloodlightModuleContext() { - serviceMap = - new HashMap<Class<? extends IFloodlightService>, - IFloodlightService>(); - configParams = - new HashMap<Class<? extends IFloodlightModule>, - Map<String, String>>(); - } - - /** - * Adds a IFloodlightModule for this Context. - * @param clazz the service class - * @param service The IFloodlightService to add to the registry - */ - public void addService(Class<? extends IFloodlightService> clazz, - IFloodlightService service) { - serviceMap.put(clazz, service); - } - - @SuppressWarnings("unchecked") + protected Map<Class<? extends IFloodlightService>, IFloodlightService> serviceMap; + protected Map<Class<? extends IFloodlightModule>, Map<String, String>> configParams; + protected Collection<IFloodlightModule> moduleSet; + + /** + * Creates the ModuleContext for use with this IFloodlightProvider. + * This will be used as a module registry for all IFloodlightModule(s). + */ + public FloodlightModuleContext() { + serviceMap = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + configParams = + new HashMap<Class<? extends IFloodlightModule>, + Map<String, String>>(); + } + + /** + * Adds a IFloodlightModule for this Context. + * @param clazz the service class + * @param service The IFloodlightService to add to the registry + */ + public void addService(Class<? extends IFloodlightService> clazz, + IFloodlightService service) { + serviceMap.put(clazz, service); + } + + @SuppressWarnings("unchecked") @Override - public <T extends IFloodlightService> T getServiceImpl(Class<T> service) { - IFloodlightService s = serviceMap.get(service); - return (T)s; - } - - @Override - public Collection<Class<? extends IFloodlightService>> getAllServices() { - return serviceMap.keySet(); - } - - @Override - public Collection<IFloodlightModule> getAllModules() { - return moduleSet; - } - - public void setModuleSet(Collection<IFloodlightModule> modSet) { - this.moduleSet = modSet; - } - - /** - * Gets the configuration parameter map for a module - * @param module The module to get the configuration map for, usually yourself - * @return A map containing all the configuration parameters for the module, may be empty - */ - @Override - public Map<String, String> getConfigParams(IFloodlightModule module) { - Map<String, String> retMap = configParams.get(module.getClass()); - if (retMap == null) { - // Return an empty map if none exists so the module does not - // need to null check the map - retMap = new HashMap<String, String>(); - configParams.put(module.getClass(), retMap); - } + public <T extends IFloodlightService> T getServiceImpl(Class<T> service) { + IFloodlightService s = serviceMap.get(service); + return (T)s; + } + + @Override + public Collection<Class<? extends IFloodlightService>> getAllServices() { + return serviceMap.keySet(); + } + + @Override + public Collection<IFloodlightModule> getAllModules() { + return moduleSet; + } + + public void setModuleSet(Collection<IFloodlightModule> modSet) { + this.moduleSet = modSet; + } + + @Override + public Map<String, String> getConfigParams(IFloodlightModule module) { + Class<? extends IFloodlightModule> clazz = module.getClass(); + return getConfigParams(clazz); + } + + @Override + public Map<String, String> getConfigParams(Class<? extends IFloodlightModule> clazz) { + Map<String, String> retMap = configParams.get(clazz); + if (retMap == null) { + // Return an empty map if none exists so the module does not + // need to null check the map + retMap = new HashMap<String, String>(); + configParams.put(clazz, retMap); + } - // also add any configuration parameters for superclasses, but - // only if more specific configuration does not override it - for (Class<? extends IFloodlightModule> c : configParams.keySet()) { - if (c.isInstance(module)) { - for (Map.Entry<String, String> ent : configParams.get(c).entrySet()) { - if (!retMap.containsKey(ent.getKey())) { - retMap.put(ent.getKey(), ent.getValue()); - } - } - } - } + // also add any configuration parameters for superclasses, but + // only if more specific configuration does not override it + for (Class<? extends IFloodlightModule> c : configParams.keySet()) { + if (c.isAssignableFrom(clazz)) { + for (Map.Entry<String, String> ent : configParams.get(c).entrySet()) { + if (!retMap.containsKey(ent.getKey())) { + retMap.put(ent.getKey(), ent.getValue()); + } + } + } + } - return retMap; - } - - /** - * Adds a configuration parameter for a module - * @param mod The fully qualified module name to add the parameter to - * @param key The configuration parameter key - * @param value The configuration parameter value - */ - public void addConfigParam(IFloodlightModule mod, String key, String value) { - Map<String, String> moduleParams = configParams.get(mod.getClass()); - if (moduleParams == null) { - moduleParams = new HashMap<String, String>(); - configParams.put(mod.getClass(), moduleParams); - } - moduleParams.put(key, value); - } + return retMap; + } + + /** + * Adds a configuration parameter for a module + * @param mod The fully qualified module name to add the parameter to + * @param key The configuration parameter key + * @param value The configuration parameter value + */ + public void addConfigParam(IFloodlightModule mod, String key, String value) { + Map<String, String> moduleParams = configParams.get(mod.getClass()); + if (moduleParams == null) { + moduleParams = new HashMap<String, String>(); + configParams.put(mod.getClass(), moduleParams); + } + moduleParams.put(key, value); + } } diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java index 1bb1e1e1a6b3042399104057e2081b3127c29c44..5cddd37fccb8564ae7e31b6f2418a2339ce354c7 100644 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java @@ -22,4 +22,16 @@ public class FloodlightModuleException extends Exception { public FloodlightModuleException(String error) { super(error); } + + public FloodlightModuleException() { + super(); + } + + public FloodlightModuleException(String message, Throwable cause) { + super(message, cause); + } + + public FloodlightModuleException(Throwable cause) { + super(cause); + } } diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java index c4b7d76fb39b9a2c0c245d78bc24935cb508e6dc..78422e50a9472970a57d446f042f9b3fb2ac910d 100644 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java @@ -305,6 +305,7 @@ public class FloodlightModuleLoader { throw new FloodlightModuleException("ERROR! Found more " + "than one (" + mods.size() + ") IFloodlightModules that provides " + "service " + c.toString() + + ". This service is required for " + moduleName + ". Please specify one of the following modules in the config: " + duplicateMods); } @@ -405,8 +406,10 @@ public class FloodlightModuleLoader { /** * Call each loaded module's startup method * @param moduleSet the module set to start up + * @throws FloodlightModuleException */ - protected void startupModules(Collection<IFloodlightModule> moduleSet) { + protected void startupModules(Collection<IFloodlightModule> moduleSet) + throws FloodlightModuleException { for (IFloodlightModule m : moduleSet) { if (logger.isDebugEnabled()) { logger.debug("Starting " + m.getClass().getCanonicalName()); diff --git a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java index 217ab8072e17a4b241e4d324d420fd8e5a7499ec..982e4be26a7e073dd700599a96a4393697088ed3 100644 --- a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java +++ b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java @@ -34,57 +34,59 @@ import java.util.Map; * @author alexreimers */ public interface IFloodlightModule { - - /** - * Return the list of interfaces that this module implements. - * All interfaces must inherit IFloodlightService - * @return - */ - - public Collection<Class<? extends IFloodlightService>> getModuleServices(); - - /** - * Instantiate (as needed) and return objects that implement each - * of the services exported by this module. The map returned maps - * the implemented service to the object. The object could be the - * same object or different objects for different exported services. - * @return The map from service interface class to service implementation - */ - public Map<Class<? extends IFloodlightService>, - IFloodlightService> getServiceImpls(); - - /** - * Get a list of Modules that this module depends on. The module system - * will ensure that each these dependencies is resolved before the - * subsequent calls to init(). - * @return The Collection of IFloodlightServices that this module depends - * on. - */ - - public Collection<Class<? extends IFloodlightService>> getModuleDependencies(); - - /** - * This is a hook for each module to do its <em>internal</em> initialization, - * e.g., call setService(context.getService("Service")) - * - * All module dependencies are resolved when this is called, but not every module - * is initialized. - * - * @param context - * @throws FloodlightModuleException - */ - - void init(FloodlightModuleContext context) throws FloodlightModuleException; - - /** - * This is a hook for each module to do its <em>external</em> initializations, - * e.g., register for callbacks or query for state in other modules - * - * It is expected that this function will not block and that modules that want - * non-event driven CPU will spawn their own threads. - * - * @param context - */ - - void startUp(FloodlightModuleContext context); + + /** + * Return the list of interfaces that this module implements. + * All interfaces must inherit IFloodlightService + * @return + */ + + public Collection<Class<? extends IFloodlightService>> getModuleServices(); + + /** + * Instantiate (as needed) and return objects that implement each + * of the services exported by this module. The map returned maps + * the implemented service to the object. The object could be the + * same object or different objects for different exported services. + * @return The map from service interface class to service implementation + */ + public Map<Class<? extends IFloodlightService>, + IFloodlightService> getServiceImpls(); + + /** + * Get a list of Modules that this module depends on. The module system + * will ensure that each these dependencies is resolved before the + * subsequent calls to init(). + * @return The Collection of IFloodlightServices that this module depends + * on. + */ + + public Collection<Class<? extends IFloodlightService>> getModuleDependencies(); + + /** + * This is a hook for each module to do its <em>internal</em> initialization, + * e.g., call setService(context.getService("Service")) + * + * All module dependencies are resolved when this is called, but not every module + * is initialized. + * + * @param context + * @throws FloodlightModuleException + */ + + void init(FloodlightModuleContext context) throws FloodlightModuleException; + + /** + * This is a hook for each module to do its <em>external</em> initializations, + * e.g., register for callbacks or query for state in other modules + * + * It is expected that this function will not block and that modules that want + * non-event driven CPU will spawn their own threads. + * + * @param context + * @throws FloodlightModuleException + */ + + void startUp(FloodlightModuleContext context) + throws FloodlightModuleException; } diff --git a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java index 6288cb7898d4f792a583b7a17c22d29f6da4caf2..6dce334f8cd15c529c9aa56e55f07418e0adb705 100644 --- a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java +++ b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java @@ -48,4 +48,12 @@ public interface IFloodlightModuleContext { * @return A key, value map of the configuration options */ public Map<String, String> getConfigParams(IFloodlightModule module); + + /** + * Gets module specific configuration parameters. + * @param clazz The class of the module to get configuration parameters for + * @return A key, value map of the configuration options + */ + public Map<String, String> getConfigParams(Class<? extends + IFloodlightModule> clazz); } diff --git a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java index 45ef6e9978fdebf82e3476dd8dfe6b94552499b4..c4d3419eb20c4822a16746fe44c9c7466d4caeff 100644 --- a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -18,6 +18,9 @@ package net.floodlightcontroller.core.web; import net.floodlightcontroller.core.module.ModuleLoaderResource; +import net.floodlightcontroller.debugcounter.DebugCounterGetResource; +import net.floodlightcontroller.debugcounter.DebugCounterResetResource; +import net.floodlightcontroller.debugcounter.DebugCounterStateResource; import net.floodlightcontroller.restserver.RestletRoutable; import org.restlet.Context; @@ -60,6 +63,9 @@ public class CoreWebRoutable implements RestletRoutable { router.attach("/role/json", ControllerRoleResource.class); router.attach("/health/json", HealthCheckResource.class); router.attach("/system/uptime/json", SystemUptimeResource.class); + router.attach("/debugcounter/{param}/json", DebugCounterGetResource.class); + router.attach("/debugcounter/reset/{param}/json", DebugCounterResetResource.class); + router.attach("/debugcounter/{moduleCounterName}/{state}/json", DebugCounterStateResource.class); return router; } } diff --git a/src/main/java/net/floodlightcontroller/counter/ConcurrentCounter.java b/src/main/java/net/floodlightcontroller/counter/ConcurrentCounter.java deleted file mode 100644 index cdec1e0ed3e2e036af82c96fe35b100510296166..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/counter/ConcurrentCounter.java +++ /dev/null @@ -1,205 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -/** - * - */ -package net.floodlightcontroller.counter; - -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import net.floodlightcontroller.counter.CounterValue.CounterType; - - -/** - * This module needs to be updated with CounterValue. - * - * This is a crumby attempt at a highly concurrent implementation of the Counter interface. - * - * (Help! Help! Someone please re-write me! This will almost certainly break at high loads.) - * - * The gist is that this class, ConcurrentCounter, keeps an internal highly transient buffer that is occasionally flushed - * in to a set of CountBuffers (circular buffers) which store a longer term historical view of the count values at different - * moments in time. - * - * This Counter implementation may be a bit over-engineered... The goal here was to present an implementation that is very - * predictable with respect to memory and CPU time and, at the same time, present a very fast increment() method. The reasoning - * here is that this will be a go-to class when it comes to debugging, particularly in high-load situations where logging - * may introduce so much variability to the system that it foils the results. - * - * @author kyle - * - */ -public class ConcurrentCounter implements ICounter { - - protected static final Map<DateSpan, Integer> MAX_HISTORY = new HashMap<DateSpan, Integer>(); - static { - MAX_HISTORY.put(DateSpan.REALTIME, new Integer(1)); - MAX_HISTORY.put(DateSpan.SECONDS, new Integer(120)); - MAX_HISTORY.put(DateSpan.MINUTES, new Integer(60)); - MAX_HISTORY.put(DateSpan.HOURS, new Integer(48)); - MAX_HISTORY.put(DateSpan.DAYS, new Integer(60)); - MAX_HISTORY.put(DateSpan.WEEKS, new Integer(2)); - } - - protected static Set<ConcurrentCounter> liveCounters; - - static { - liveCounters = Collections.newSetFromMap(new ConcurrentHashMap<ConcurrentCounter, Boolean>()); //nifty way to get concurrent hash set - //Set a background thread to flush any liveCounters every 100 milliseconds - Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { - public void run() { - for(ConcurrentCounter c : liveCounters) { - c.flush(); - } - }}, 100, 100, TimeUnit.MILLISECONDS); - } - - /** - * Very simple data structure to store off a single count entry at a single point in time - * @author kyle - * - */ - protected static final class CountAtom { - protected Date date; - protected Long delta; - - protected CountAtom(Date date, Long delta) { - this.date = date; - this.delta = delta; - } - - public String toString() { - return "[" + this.date + ": " + this.delta + "]"; - } - } - - - protected Queue<CountAtom> unprocessedCountBuffer; - protected Map<DateSpan, CountBuffer> counts; - protected Date startDate; - - /** - * Factory method to create a new counter instance. (Design note - - * use a factory pattern here as it may be necessary to hook in other - * registrations around counter objects as they are created.) - * - * @param startDate - * @return - */ - public static ICounter createCounter(Date startDate) { - ConcurrentCounter cc = new ConcurrentCounter(startDate); - ConcurrentCounter.liveCounters.add(cc); - return cc; - - } - - /** - * Protected constructor - use createCounter factory method instead - * @param startDate - */ - protected ConcurrentCounter(Date startDate) { - init(startDate); - } - - protected void init(Date startDate) { - this.startDate = startDate; - this.unprocessedCountBuffer = new ConcurrentLinkedQueue<CountAtom>(); - this.counts = new HashMap<DateSpan, CountBuffer>(); - - for(DateSpan ds : DateSpan.values()) { - CountBuffer cb = new CountBuffer(startDate, ds, MAX_HISTORY.get(ds)); - counts.put(ds, cb); - } - } - /** - * This is the key method that has to be both fast and very thread-safe. - */ - @Override - public void increment() { - this.increment(new Date(), (long)1); - } - - @Override - public void increment(Date d, long delta) { - this.unprocessedCountBuffer.add(new CountAtom(d, delta)); - } - - @Override - public void setCounter(Date d, CounterValue value) { - // To be done later - } - - /** - * Reset the value. - */ - @Override - public void reset(Date startDate) { - init(startDate); - } - - /** - * Flushes values out of the internal buffer and in to structures - * that can be fetched with a call to snapshot() - */ - public synchronized void flush() { - for(CountAtom c = this.unprocessedCountBuffer.poll(); c != null; c = this.unprocessedCountBuffer.poll()) { - for(DateSpan ds : DateSpan.values()) { - CountBuffer cb = counts.get(ds); - cb.increment(c.date, c.delta); - } - } - } - - @Override - public CounterValue getCounterValue() { - // To be done later - //CountSeries cs = counts.get(DateSpan.REALTIME).snapshot(); - //return cs.getSeries()[0]; - return new CounterValue(CounterType.LONG); - } - - @Override - public Date getCounterDate() { - // To be done later - //CountSeries cs = counts.get(DateSpan.REALTIME).snapshot(); - //return cs.getSeries()[0]; - return new Date(); - } - - @Override - /** - * This method returns a disconnected copy of the underlying CountSeries corresponding to dateSpan. - */ - public CountSeries snapshot(DateSpan dateSpan) { - flush(); - CountSeries cs = counts.get(dateSpan).snapshot(); - return cs; - } - - - -} diff --git a/src/main/java/net/floodlightcontroller/counter/CountBuffer.java b/src/main/java/net/floodlightcontroller/counter/CountBuffer.java deleted file mode 100644 index fa45862e8b938cad6a5b7e57600861986a68f7ff..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/counter/CountBuffer.java +++ /dev/null @@ -1,125 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -package net.floodlightcontroller.counter; - -import java.util.Date; - -import net.floodlightcontroller.counter.ICounter.DateSpan; - - -/** - * Implements a circular buffer to store the last x time-based counter values. This is pretty crumby - * implementation, basically wrapping everything with synchronized blocks, in order to ensure that threads - * which will be updating the series don't result in a thread which is reading the series getting stuck with - * a start date which does not correspond to the count values in getSeries. - * - * This could probably use a re-think... - * - * @author kyle - * - */ -public class CountBuffer { - protected long[] counterValues; - protected Date startDate; - protected DateSpan dateSpan; - protected int currentIndex; - protected int seriesLength; - - - public CountBuffer(Date startDate, DateSpan dateSpan, int seriesLength) { - this.seriesLength = seriesLength; - this.counterValues = new long[seriesLength]; - this.dateSpan = dateSpan; - - this.startDate = startDate; - this.currentIndex = 0; - } - - /** - * Increment the count associated with Date d, forgetting some of the older count values if necessary to ensure - * that the total span of time covered by this series corresponds to DateSpan * seriesLength (circular buffer). - * - * Note - fails silently if the Date falls prior to the start of the tracked count values. - * - * Note - this should be a reasonably fast method, though it will have to block if there is another thread reading the - * series at the same time. - * - * @param d - * @param delta - */ - public synchronized void increment(Date d, long delta) { - - long dsMillis = CountSeries.dateSpanToMilliseconds(this.dateSpan); - Date endDate = new Date(startDate.getTime() + seriesLength * dsMillis - 1); - - if(d.getTime() < startDate.getTime()) { - return; //silently fail rather than insert a count at a time older than the history buffer we're keeping - } - else if (d.getTime() >= startDate.getTime() && d.getTime() <= endDate.getTime()) { - int index = (int) (( d.getTime() - startDate.getTime() ) / dsMillis); // java rounds down on long/long - int modIndex = (index + currentIndex) % seriesLength; - long currentValue = counterValues[modIndex]; - counterValues[modIndex] = currentValue + delta; - } - else if (d.getTime() > endDate.getTime()) { - //Initialize new buckets - int newBuckets = (int)((d.getTime() - endDate.getTime()) / dsMillis) + 1; // java rounds down on long/long - for(int i = 0; i < newBuckets; i++) { - int modIndex = (i + currentIndex) % seriesLength; - counterValues[modIndex] = 0; - } - //Update internal vars - this.startDate = new Date(startDate.getTime() + dsMillis * newBuckets); - this.currentIndex = (currentIndex + newBuckets) % this.seriesLength; - - //Call again (date should be in the range this time) - this.increment(d, delta); - } - } - - /** - * Relatively slow method, expected to be called primarily from UI rather than from in-packet-path. - * - * @return the count values associated with each time interval starting with startDate and demarc'ed by dateSpan - */ - public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment - long[] ret = new long[this.seriesLength]; - for(int i = 0; i < this.seriesLength; i++) { - int modIndex = (currentIndex + i) % this.seriesLength; - ret[i] = this.counterValues[modIndex]; - } - return ret; - } - - - /** - * Returns an immutable count series that represents a snapshot of this - * series at a specific moment in time. - * @return - */ - public synchronized CountSeries snapshot() { - long[] cvs = new long[this.seriesLength]; - for(int i = 0; i < this.seriesLength; i++) { - int modIndex = (this.currentIndex + i) % this.seriesLength; - cvs[i] = this.counterValues[modIndex]; - } - - return new CountSeries(this.startDate, this.dateSpan, cvs); - } - -} diff --git a/src/main/java/net/floodlightcontroller/counter/CountSeries.java b/src/main/java/net/floodlightcontroller/counter/CountSeries.java deleted file mode 100644 index e8a547a516bab83395aa9b73affd425e85b897bd..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/counter/CountSeries.java +++ /dev/null @@ -1,88 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -package net.floodlightcontroller.counter; - -import java.util.Arrays; -import java.util.Date; - -import net.floodlightcontroller.counter.ICounter.DateSpan; - -/** - * Simple immutable class to store a series of historic counter values - * - * This could probably use a re-think... - * - * @author kyle - * - */ -public class CountSeries { - protected long[] counterValues; - protected Date startDate; - protected DateSpan dateSpan; - - public CountSeries(Date startDate, DateSpan dateSpan, long[] counterValues) { - this.counterValues = counterValues.clone(); - this.dateSpan = dateSpan; - this.startDate = startDate; - } - - - public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment - return this.counterValues.clone(); - } - - /** - * Returns the startDate of this series. The first long in getSeries represents the sum of deltas from increment calls with dates - * that correspond to >= startDate and < startDate + DateSpan. - * @return - */ - public Date getStartDate() {//synchronized here should lock on 'this', implying that it shares the lock with increment - return this.startDate; - } - - public String toString() { - String ret = "{start: " + this.startDate + ", span: " + this.dateSpan + ", series: " + Arrays.toString(getSeries()) + "}"; - return ret; - } - - /** - * Return a long that is the number of milliseconds in a ds (second/minute/hour/day/week). (Utility method.) - * - * @param ds - * @return - */ - 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; - default: - break; - } - return delta; - } - -} diff --git a/src/main/java/net/floodlightcontroller/counter/CounterStore.java b/src/main/java/net/floodlightcontroller/counter/CounterStore.java index 16465ba630aa9fd43747d08e9037110200d76052..bfe3b963f82b61bc0529005e4edd5b1f0b4d5164 100644 --- a/src/main/java/net/floodlightcontroller/counter/CounterStore.java +++ b/src/main/java/net/floodlightcontroller/counter/CounterStore.java @@ -1,7 +1,7 @@ /** - * Copyright 2011, Big Switch Networks, Inc. + * Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -15,9 +15,6 @@ * under the License. **/ -/** - * Implements a very simple central store for system counters - */ package net.floodlightcontroller.counter; import java.util.ArrayList; @@ -27,11 +24,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import javax.annotation.PostConstruct; - import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; @@ -46,10 +38,16 @@ import org.openflow.protocol.OFPacketIn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** - * @author kyle + * Implements a central store for system counters. These counters include + * overall packet-in, packet-out, and flow-mod counters. Additional packet-in + * counters are maintained for bcast/unicast/multicast traffic, as well as counters + * for traffic types based on ethertype and ip-proto (maintained on a per switch + * and controller level). These counters are maintained without the involvement of + * any other module in the system. For per-module counters and other detailed + * debug services, consider IDebugCounterService. * + * @authors Kyle, Kanzhe, Mandeep and Saurav */ public class CounterStore implements IFloodlightModule, ICounterStoreService { protected static Logger log = LoggerFactory.getLogger(CounterStore.class); @@ -70,69 +68,102 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { public void set(int val) { value = val; } } - /** - * A map of counterName --> Counter - */ - protected ConcurrentHashMap<String, CounterEntry> nameToCEIndex = - new ConcurrentHashMap<String, CounterEntry>(); + protected class CounterKeyTuple { + byte msgType; + long dpid; + short l3type; + byte l4type; + + public CounterKeyTuple(byte msgType, long dpid, short l3type, byte l4type){ + this.msgType = msgType; + this.dpid = dpid; + this.l3type = l3type; + this.l4type = l4type; + } - protected ICounter heartbeatCounter; - protected ICounter randomCounter; + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof CounterKeyTuple)) return false; + CounterKeyTuple other = (CounterKeyTuple) obj; + if (this.msgType == other.msgType && + this.dpid == other.dpid && + this.l3type == other.l3type && + this.l4type == other.l4type) + return true; + return false; + } - protected ConcurrentHashMap<String, List<ICounter>> - pktinCounters = new ConcurrentHashMap<String, List<ICounter>>(); - protected ConcurrentHashMap<String, List<ICounter>> - pktoutCounters = new ConcurrentHashMap<String, List<ICounter>>(); + @Override + public int hashCode() { + final int prime = 283; + int result = 1; + result = prime * result + msgType; + result = prime * result + (int) (dpid ^ (dpid >>> 32)); + result = prime * result + l3type; + result = prime * result + l4type; + return result; + } + } + + /** + * Counter storage across all threads. These are periodically updated from the + * local per thread counters by the updateFlush method. + */ + protected ConcurrentHashMap<CounterKeyTuple, List<ICounter>> + pktinCounters = new ConcurrentHashMap<CounterKeyTuple, List<ICounter>>(); + protected ConcurrentHashMap<CounterKeyTuple, List<ICounter>> + pktoutCounters = new ConcurrentHashMap<CounterKeyTuple, List<ICounter>>(); - protected final ThreadLocal<Map<String,MutableInt>> pktin_local_buffer = - new ThreadLocal<Map<String,MutableInt>>() { + /** + * Thread local counter stores + */ + protected final ThreadLocal<Map<CounterKeyTuple,MutableInt>> pktin_local_buffer = + new ThreadLocal<Map<CounterKeyTuple,MutableInt>>() { @Override - protected Map<String,MutableInt> initialValue() { - return new HashMap<String,MutableInt>(); + protected Map<CounterKeyTuple,MutableInt> initialValue() { + return new HashMap<CounterKeyTuple,MutableInt>(); } }; - protected final ThreadLocal<Map<String,MutableInt>> pktout_local_buffer = - new ThreadLocal<Map<String,MutableInt>>() { + + protected final ThreadLocal<Map<CounterKeyTuple,MutableInt>> pktout_local_buffer = + new ThreadLocal<Map<CounterKeyTuple,MutableInt>>() { @Override - protected Map<String,MutableInt> initialValue() { - return new HashMap<String,MutableInt>(); + protected Map<CounterKeyTuple,MutableInt> initialValue() { + return new HashMap<CounterKeyTuple,MutableInt>(); } }; + /** + * A cache of counterName --> Counter used to retrieve counters quickly via + * string-counter-keys + */ + protected ConcurrentHashMap<String, CounterEntry> nameToCEIndex = + new ConcurrentHashMap<String, CounterEntry>(); /** * Counter Categories grouped by network layers * NetworkLayer -> CounterToCategories */ - protected static Map<NetworkLayer, Map<String, List<String>>> layeredCategories = + protected static Map<NetworkLayer, Map<String, List<String>>> layeredCategories = new ConcurrentHashMap<NetworkLayer, Map<String, List<String>>> (); - public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) { - if (((OFPacketIn)m).getPacketData().length <= 0) { - return; - } - - List<ICounter> counters = this.getPacketInCounters(sw, m, eth); - if (counters != null) { - for (ICounter c : counters) { - c.increment(); - } - } - return; - } + //******************************* + // ICounterStoreService + //******************************* @Override public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth) { if (((OFPacketIn)m).getPacketData().length <= 0) { return; } - - String countersKey = this.getCountersKey(sw, m, eth); - Map<String, MutableInt> pktin_buffer = this.pktin_local_buffer.get(); + CounterKeyTuple countersKey = this.getCountersKey(sw, m, eth); + Map<CounterKeyTuple, MutableInt> pktin_buffer = this.pktin_local_buffer.get(); MutableInt currval = pktin_buffer.get(countersKey); - if ( currval == null ) { - this.getPacketInCounters(sw, m, eth); // create counters as side effect (if required) + if (currval == null) { + this.createPacketInCounters(sw, m, eth); // create counters as side effect (if required) currval = new MutableInt(); pktin_buffer.put(countersKey, currval); } @@ -140,30 +171,13 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { return; } - /** - * This method can only be used to update packetOut and flowmod counters - * NOTE: flowmod is counted per switch and for controller, not per port/proto - * - * @param sw - * @param m - */ - public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage m) { - List<ICounter> counters = this.getPktOutFMCounters(sw, m); - if (counters != null) { - for (ICounter c : counters) { - c.increment(); - } - } - return; - } - @Override public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage m) { - String countersKey = this.getCountersKey(sw, m, null); - Map<String, MutableInt> pktout_buffer = this.pktout_local_buffer.get(); + CounterKeyTuple countersKey = this.getCountersKey(sw, m, null); + Map<CounterKeyTuple, MutableInt> pktout_buffer = this.pktout_local_buffer.get(); MutableInt currval = pktout_buffer.get(countersKey); - if ( currval == null ) { + if (currval == null) { this.getPktOutFMCounters(sw, m); // create counters as side effect (if required) currval = new MutableInt(); pktout_buffer.put(countersKey, currval); @@ -175,8 +189,8 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { @Override public void updateFlush() { Date date = new Date(); - Map<String, MutableInt> pktin_buffer = this.pktin_local_buffer.get(); - for (String key : pktin_buffer.keySet()) { + Map<CounterKeyTuple, MutableInt> pktin_buffer = this.pktin_local_buffer.get(); + for (CounterKeyTuple key : pktin_buffer.keySet()) { MutableInt currval = pktin_buffer.get(key); int delta = currval.get(); @@ -192,8 +206,8 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { // We could do better "GC" of counters that have not been update "recently" pktin_buffer.clear(); - Map<String, MutableInt> pktout_buffer = this.pktout_local_buffer.get(); - for (String key : pktout_buffer.keySet()) { + Map<CounterKeyTuple, MutableInt> pktout_buffer = this.pktout_local_buffer.get(); + for (CounterKeyTuple key : pktout_buffer.keySet()) { MutableInt currval = pktout_buffer.get(key); int delta = currval.get(); @@ -210,56 +224,102 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { pktout_buffer.clear(); } - protected String getCountersKey(IOFSwitch sw, OFMessage m, Ethernet eth) { + @Override + public ICounter createCounter(String key, CounterValue.CounterType type) { + CounterEntry ce; + ICounter c; + + c = SimpleCounter.createCounter(new Date(), type); + ce = new CounterEntry(); + ce.counter = c; + ce.title = key; + nameToCEIndex.putIfAbsent(key, ce); + + return nameToCEIndex.get(key).counter; + } + + @Override + public ICounter getCounter(String key) { + CounterEntry counter = nameToCEIndex.get(key); + if (counter != null) { + return counter.counter; + } else { + return null; + } + } + + /* (non-Javadoc) + * @see net.floodlightcontroller.counter.ICounterStoreService#getAll() + */ + @Override + public Map<String, ICounter> getAll() { + Map<String, ICounter> ret = new ConcurrentHashMap<String, ICounter>(); + for(Map.Entry<String, CounterEntry> counterEntry : this.nameToCEIndex.entrySet()) { + String key = counterEntry.getKey(); + ICounter counter = counterEntry.getValue().counter; + ret.put(key, counter); + } + return ret; + } + + @Override + public List<String> getAllCategories(String counterName, NetworkLayer layer) { + if (layeredCategories.containsKey(layer)) { + Map<String, List<String>> counterToCategories = layeredCategories.get(layer); + if (counterToCategories.containsKey(counterName)) { + return counterToCategories.get(counterName); + } + } + return null; + } + + /** + * Create a title based on switch ID, portID, vlanID, and counterName + * If portID is -1, the title represents the given switch only + * If portID is a non-negative number, the title represents the port on the given switch + */ + public static String createCounterName(String switchID, int portID, String counterName) { + if (portID < 0) { + return switchID + TitleDelimitor + counterName; + } else { + return switchID + TitleDelimitor + portID + TitleDelimitor + counterName; + } + } + + //******************************* + // Internal Methods + //******************************* + + protected CounterKeyTuple getCountersKey(IOFSwitch sw, OFMessage m, Ethernet eth) { byte mtype = m.getType().getTypeValue(); - //long swid = sw.getId(); - String swsid = sw.getStringId(); - short port = 0; short l3type = 0; byte l4type = 0; - + if (eth != null) { - // Packet in counters - // Need port and protocol level differentiation - OFPacketIn packet = (OFPacketIn)m; - port = packet.getInPort(); l3type = eth.getEtherType(); - if (l3type == (short)0x0800) { + if (eth.getPayload() instanceof IPv4) { IPv4 ipV4 = (IPv4)eth.getPayload(); l4type = ipV4.getProtocol(); } } - - /* If possible, find and return counters for this tuple - * - * NOTE: this can be converted to a tuple for better performance, - * for now we are using a string representation as a the key - */ - String countersKey = - Byte.toString(mtype) + "-" + - swsid + "-" + Short.toString(port) + "-" + - Short.toString(l3type) + "-" + - Byte.toString(l4type); - return countersKey; + return new CounterKeyTuple(mtype, sw.getId(), l3type, l4type); } - protected List<ICounter> getPacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) { + protected List<ICounter> createPacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) { /* If possible, find and return counters for this tuple */ - String countersKey = this.getCountersKey(sw, m, eth); + CounterKeyTuple countersKey = this.getCountersKey(sw, m, eth); List<ICounter> counters = this.pktinCounters.get(countersKey); if (counters != null) { return counters; } - + /* * Create the required counters */ counters = new ArrayList<ICounter>(); - /* values for names */ - short port = ((OFPacketIn)m).getInPort(); - short l3type = eth.getEtherType(); + int l3type = eth.getEtherType() & 0xffff; String switchIdHex = sw.getStringId(); String etherType = String.format("%04x", eth.getEtherType()); String packetName = m.getType().toClass().getName(); @@ -285,14 +345,14 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { if (l3type < 0x0600) { etherType = "0599"; } - if (TypeAliases.l3TypeAliasMap != null && + if (TypeAliases.l3TypeAliasMap != null && TypeAliases.l3TypeAliasMap.containsKey(etherType)) { etherType = TypeAliases.l3TypeAliasMap.get(etherType); } else { etherType = "L3_" + etherType; } - + // overall controller packet counter names String controllerCounterName = CounterStore.createCounterName( @@ -310,14 +370,6 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { counters.add(createCounter(switchCounterName, CounterType.LONG)); - String portCounterName = - CounterStore.createCounterName( - switchIdHex, - port, - packetName); - counters.add(createCounter(portCounterName, - CounterType.LONG)); - // L2 counter names String controllerL2CategoryCounterName = CounterStore.createCounterName( @@ -339,16 +391,6 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { counters.add(createCounter(switchL2CategoryCounterName, CounterType.LONG)); - String portL2CategoryCounterName = - CounterStore.createCounterName( - switchIdHex, - port, - packetName, - l2Type, - NetworkLayer.L2); - counters.add(createCounter(portL2CategoryCounterName, - CounterType.LONG)); - // L3 counter names String controllerL3CategoryCounterName = CounterStore.createCounterName( @@ -370,23 +412,13 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { counters.add(createCounter(switchL3CategoryCounterName, CounterType.LONG)); - String portL3CategoryCounterName = - CounterStore.createCounterName( - switchIdHex, - port, - packetName, - etherType, - NetworkLayer.L3); - counters.add(createCounter(portL3CategoryCounterName, - CounterType.LONG)); - // L4 counters - if (l3type == (short)0x0800) { + if (eth.getPayload() instanceof IPv4) { // resolve protocol alias IPv4 ipV4 = (IPv4)eth.getPayload(); String l4name = String.format("%02x", ipV4.getProtocol()); - if (TypeAliases.l4TypeAliasMap != null && + if (TypeAliases.l4TypeAliasMap != null && TypeAliases.l4TypeAliasMap.containsKey(l4name)) { l4name = TypeAliases.l4TypeAliasMap.get(l4name); } @@ -415,25 +447,16 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { counters.add(createCounter(switchL4CategoryCounterName, CounterType.LONG)); - String portL4CategoryCounterName = - CounterStore.createCounterName( - switchIdHex, - port, - packetName, - l4name, - NetworkLayer.L4); - counters.add(createCounter(portL4CategoryCounterName, - CounterType.LONG)); } /* Add to map and return */ this.pktinCounters.putIfAbsent(countersKey, counters); return this.pktinCounters.get(countersKey); } - + protected List<ICounter> getPktOutFMCounters(IOFSwitch sw, OFMessage m) { /* If possible, find and return counters for this tuple */ - String countersKey = this.getCountersKey(sw, m, null); + CounterKeyTuple countersKey = this.getCountersKey(sw, m, null); List<ICounter> counters = this.pktoutCounters.get(countersKey); if (counters != null) { @@ -472,26 +495,13 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { } - /** - * Create a title based on switch ID, portID, vlanID, and counterName - * If portID is -1, the title represents the given switch only - * If portID is a non-negative number, the title represents the port on the given switch - */ - public static String createCounterName(String switchID, int portID, String counterName) { - if (portID < 0) { - return switchID + TitleDelimitor + counterName; - } else { - return switchID + TitleDelimitor + portID + TitleDelimitor + counterName; - } - } - /** * Create a title based on switch ID, portID, vlanID, counterName, and subCategory * If portID is -1, the title represents the given switch only * If portID is a non-negative number, the title represents the port on the given switch * For example: PacketIns can be further categorized based on L2 etherType or L3 protocol */ - public static String createCounterName(String switchID, int portID, String counterName, + protected static String createCounterName(String switchID, int portID, String counterName, String subCategory, NetworkLayer layer) { String fullCounterName = ""; String groupCounterName = ""; @@ -504,7 +514,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { fullCounterName = groupCounterName + TitleDelimitor + subCategory; } - Map<String, List<String>> counterToCategories; + Map<String, List<String>> counterToCategories; if (layeredCategories.containsKey(layer)) { counterToCategories = layeredCategories.get(layer); } else { @@ -526,69 +536,9 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { return fullCounterName; } - @Override - public List<String> getAllCategories(String counterName, NetworkLayer layer) { - if (layeredCategories.containsKey(layer)) { - Map<String, List<String>> counterToCategories = layeredCategories.get(layer); - if (counterToCategories.containsKey(counterName)) { - return counterToCategories.get(counterName); - } - } - return null; - } - - @Override - public ICounter createCounter(String key, CounterValue.CounterType type) { - CounterEntry ce; - ICounter c; - - c = SimpleCounter.createCounter(new Date(), type); - ce = new CounterEntry(); - ce.counter = c; - ce.title = key; - nameToCEIndex.putIfAbsent(key, ce); - - return nameToCEIndex.get(key).counter; - } - - /** - * Post construction init method to kick off the health check and random (test) counter threads - */ - @PostConstruct - public void startUp() { - this.heartbeatCounter = this.createCounter("CounterStore heartbeat", CounterValue.CounterType.LONG); - this.randomCounter = this.createCounter("CounterStore random", CounterValue.CounterType.LONG); - //Set a background thread to flush any liveCounters every 100 milliseconds - Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { - public void run() { - heartbeatCounter.increment(); - randomCounter.increment(new Date(), (long) (Math.random() * 100)); //TODO - pull this in to random timing - }}, 100, 100, TimeUnit.MILLISECONDS); - } - - @Override - public ICounter getCounter(String key) { - CounterEntry counter = nameToCEIndex.get(key); - if (counter != null) { - return counter.counter; - } else { - return null; - } - } - - /* (non-Javadoc) - * @see net.floodlightcontroller.counter.ICounterStoreService#getAll() - */ - @Override - public Map<String, ICounter> getAll() { - Map<String, ICounter> ret = new ConcurrentHashMap<String, ICounter>(); - for(Map.Entry<String, CounterEntry> counterEntry : this.nameToCEIndex.entrySet()) { - String key = counterEntry.getKey(); - ICounter counter = counterEntry.getValue().counter; - ret.put(key, counter); - } - return ret; - } + //******************************* + // IFloodlightProvider + //******************************* @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { @@ -602,7 +552,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { Map<Class<? extends IFloodlightService>, - IFloodlightService> m = + IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); m.put(ICounterStoreService.class, this); @@ -625,4 +575,5 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { public void startUp(FloodlightModuleContext context) { // no-op for now } + } diff --git a/src/main/java/net/floodlightcontroller/counter/ICounter.java b/src/main/java/net/floodlightcontroller/counter/ICounter.java index b7fb73630de0a85321f575963c9b6224bc2afbe0..0e31fdecd6a75419b9ccab92cd4a94e33f6f8cda 100644 --- a/src/main/java/net/floodlightcontroller/counter/ICounter.java +++ b/src/main/java/net/floodlightcontroller/counter/ICounter.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -28,46 +28,37 @@ import java.util.Date; * */ public interface ICounter { - + /** * Most commonly used method */ public void increment(); - + /** * Used primarily for flushing thread local updates */ public void increment(Date d, long delta); - + /** * Counter value setter */ public void setCounter(Date d, CounterValue value); - + /** * Return the most current value */ public Date getCounterDate(); - + /** * Return the most current value */ public CounterValue getCounterValue(); - + /** * Reset the value */ public void reset(Date d); - - /** - * Returns a CountSeries that is a snapshot of the counter's values for the given dateSpan. (Further changes - * to this counter won't be reflected in the CountSeries that comes back.) - * - * @param dateSpan - * @return - */ - public CountSeries snapshot(DateSpan dateSpan); - + public static enum DateSpan { REALTIME, diff --git a/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java b/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java index c516d8f858e4c7a6efd71dcb1fa5bef97a5cc4f6..c0cbd55916086103bb4df820ca1b33974ed3dcca 100644 --- a/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java +++ b/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java @@ -35,27 +35,25 @@ public interface ICounterStoreService extends IFloodlightService { public final static String BROADCAST = "broadcast"; public final static String MULTICAST = "multicast"; public final static String UNICAST = "unicast"; - + /** L2 EtherType subCategories */ public final static String L3ET_IPV4 = "L3_IPv4"; /** * Update packetIn counters - * + * * @param sw * @param m * @param eth */ - public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth); public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth); - + /** * This method can only be used to update packetOut and flowmod counters - * + * * @param sw * @param ofMsg */ - public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg); public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg); /** @@ -63,7 +61,7 @@ public interface ICounterStoreService extends IFloodlightService { * */ public void updateFlush(); - + /** * Retrieve a list of subCategories by counterName. * null if nothing. @@ -72,9 +70,9 @@ public interface ICounterStoreService extends IFloodlightService { NetworkLayer layer); /** - * Create a new ICounter and set the title. Note that the title must be + * Create a new ICounter and set the title. Note that the title must be * unique, otherwise this will throw an IllegalArgumentException. - * + * * @param key * @param type * @return @@ -88,7 +86,7 @@ public interface ICounterStoreService extends IFloodlightService { /** * Returns an immutable map of title:counter with all of the counters in the store. - * + * * (Note - this method may be slow - primarily for debugging/UI) */ public Map<String, ICounter> getAll(); diff --git a/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java b/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java index 167b4ed54e4f2b1a070c56ba99b4ee35731ee3c3..bdcc690b349c4497d8ca7b645b23a03d3af2ce85 100644 --- a/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java +++ b/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java @@ -47,22 +47,12 @@ public class NullCounterStore implements IFloodlightModule, private ICounter emptyCounter; private List<String> emptyList; private Map<String, ICounter> emptyMap; - - @Override - public void updatePacketInCounters(IOFSwitch sw, OFMessage m, Ethernet eth) { - // no-op - } @Override public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth) { // no-op } - @Override - public void updatePktOutFMCounterStore(IOFSwitch sw, OFMessage ofMsg) { - // no-op - } - @Override public void updatePktOutFMCounterStoreLocal(IOFSwitch sw, OFMessage ofMsg) { // no-op @@ -106,7 +96,7 @@ public class NullCounterStore implements IFloodlightModule, public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { Map<Class<? extends IFloodlightService>, - IFloodlightService> m = + IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); m.put(ICounterStoreService.class, this); diff --git a/src/main/java/net/floodlightcontroller/counter/SimpleCounter.java b/src/main/java/net/floodlightcontroller/counter/SimpleCounter.java index 01a042845f85b1607776c709f7a94726a0ba1796..dd0daf31797ace63ad0d1c8004ec8b016cb19dc9 100644 --- a/src/main/java/net/floodlightcontroller/counter/SimpleCounter.java +++ b/src/main/java/net/floodlightcontroller/counter/SimpleCounter.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -16,7 +16,7 @@ **/ /** - * + * */ package net.floodlightcontroller.counter; @@ -28,7 +28,7 @@ import java.util.Date; * This is a simple counter implementation that doesn't support data series. * The idea is that floodlight only keeps the realtime value for each counter, * statd, a statistics collection daemon, samples counters at a user-defined interval - * and pushes the values to a database, which keeps time-based data series. + * and pushes the values to a database, which keeps time-based data series. * @author Kanzhe * */ @@ -37,10 +37,10 @@ public class SimpleCounter implements ICounter { protected CounterValue counter; protected Date samplingTime; protected Date startDate; - + /** - * Factory method to create a new counter instance. - * + * Factory method to create a new counter instance. + * * @param startDate * @return */ @@ -48,10 +48,10 @@ public class SimpleCounter implements ICounter { SimpleCounter cc = new SimpleCounter(startDate, type); return cc; } - + /** - * Factory method to create a copy of a counter instance. - * + * Factory method to create a copy of a counter instance. + * * @param startDate * @return */ @@ -67,7 +67,7 @@ public class SimpleCounter implements ICounter { cc.setCounter(copy.getCounterDate(), copy.getCounterValue()); return cc; } - + /** * Protected constructor - use createCounter factory method instead * @param startDate @@ -75,32 +75,33 @@ public class SimpleCounter implements ICounter { protected SimpleCounter(Date startDate, CounterValue.CounterType type) { init(startDate, type); } - + protected void init(Date startDate, CounterValue.CounterType type) { this.startDate = startDate; this.samplingTime = new Date(); this.counter = new CounterValue(type); } - + /** * This is the key method that has to be both fast and very thread-safe. */ @Override synchronized public void increment() { - this.increment(new Date(), (long)1); + this.increment(new Date(), 1); } - + @Override synchronized public void increment(Date d, long delta) { this.samplingTime = d; this.counter.increment(delta); } - - synchronized public void setCounter(Date d, CounterValue value) { + + @Override +synchronized public void setCounter(Date d, CounterValue value) { this.samplingTime = d; this.counter = value; } - + /** * This is the method to retrieve the current value. */ @@ -116,7 +117,7 @@ public class SimpleCounter implements ICounter { synchronized public Date getCounterDate() { return this.samplingTime; } - + /** * Reset value. */ @@ -124,14 +125,5 @@ public class SimpleCounter implements ICounter { synchronized public void reset(Date startDate) { init(startDate, this.counter.getType()); } - - @Override - /** - * This method only returns the real-time value. - */ - synchronized public CountSeries snapshot(DateSpan dateSpan) { - long[] values = new long[1]; - values[0] = this.counter.getLong(); - return new CountSeries(this.samplingTime, DateSpan.DAYS, values); - } + } diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..7aed59a8f30fa7385b3d8ab7e766392b921b2c85 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java @@ -0,0 +1,380 @@ +package net.floodlightcontroller.debugcounter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; + +/** + * This class implements a central store for all counters used for debugging the + * system. For counters based on traffic-type, see ICounterStoreService. + * + * @author Saurav + */ +public class DebugCounter implements IFloodlightModule, IDebugCounterService { + protected static Logger log = LoggerFactory.getLogger(DebugCounter.class); + + /** + * The counter value + */ + protected class MutableLong { + long value = 0; + public void increment() { value += 1; } + public long get() { return value; } + public void set(long val) { value = val; } + } + + /** + * Global debug-counter storage across all threads. These are + * updated from the local per thread counters by the flush counters method. + */ + protected ConcurrentHashMap<String, AtomicLong> debugCounters = + new ConcurrentHashMap<String, AtomicLong>(); + + /** + * Thread local debug counters used for maintaining counters local to a thread. + */ + protected final ThreadLocal<Map<String, MutableLong>> threadlocalCounters = + new ThreadLocal<Map<String, MutableLong>>() { + @Override + protected Map<String, MutableLong> initialValue() { + return new HashMap<String, MutableLong>(); + } + }; + + /** + * protected class to store counter information + */ + protected class CounterInfo { + String moduleCounterName; + String counterDesc; + CounterType ctype; + String moduleName; + String counterName; + + public CounterInfo(String name, String desc, CounterType ctype) { + this.moduleCounterName = name; + String[] temp = name.split("-"); + this.moduleName = temp[0]; + this.counterName = temp[1]; + this.counterDesc = desc; + this.ctype = ctype; + } + + public String getModuleCounterName() { return moduleCounterName; } + public String getCounterDesc() { return counterDesc; } + public CounterType getCtype() { return ctype; } + public String getModuleName() { return moduleName; } + public String getCounterName() { return counterName; } + } + + /** + * per module counters, indexed by the module name and storing Counter information. + */ + protected ConcurrentHashMap<String, List<CounterInfo>> moduleCounters = + new ConcurrentHashMap<String, List<CounterInfo>>(); + + /** + * fast global cache for counter names that are currently active + */ + Set<String> currentCounters = Collections.newSetFromMap( + new ConcurrentHashMap<String,Boolean>()); + + /** + * Thread local cache for counter names that are currently active. + */ + protected final ThreadLocal<Set<String>> threadlocalCurrentCounters = + new ThreadLocal<Set<String>>() { + @Override + protected Set<String> initialValue() { + return new HashSet<String>(); + } + }; + + //******************************* + // IDebugCounterService + //******************************* + + @Override + public boolean registerCounter(String moduleCounterName, String counterDescription, + CounterType counterType) { + if (debugCounters.containsKey(moduleCounterName)) { + log.error("Cannot register counter: {}. Counter already exists", + moduleCounterName); + return false; + } + String[] temp = moduleCounterName.split("-"); + if (temp.length < 2) { + log.error("Cannot register counter: {}. Name not of type " + + " <module name>-<counter name>", moduleCounterName); + return false; + } + + // store counter information on a per module basis + String moduleName = temp[0]; + List<CounterInfo> a; + if (moduleCounters.containsKey(moduleName)) { + a = moduleCounters.get(moduleName); + } else { + a = new ArrayList<CounterInfo>(); + moduleCounters.put(moduleName, a); + } + a.add(new CounterInfo(moduleCounterName, counterDescription, counterType)); + + // create counter in global map + // and add to counter name cache if it is meant to be always counted + if (counterType == CounterType.ALWAYS_COUNT) { + currentCounters.add(moduleCounterName); + debugCounters.put(moduleCounterName, new AtomicLong()); + } + return true; + } + + @Override + public void updateCounter(String moduleCounterName) { + Map<String, MutableLong> thismap = this.threadlocalCounters.get(); + MutableLong ml = thismap.get(moduleCounterName); + if (ml == null) { + // check locally to see if this counter should be created or not + Set<String> thisset = this.threadlocalCurrentCounters.get(); + if (thisset.contains(moduleCounterName)) { + ml = new MutableLong(); + ml.increment(); + thismap.put(moduleCounterName, ml); + } + } else { + ml.increment(); + } + } + + @Override + public void flushCounters() { + Map<String, MutableLong> thismap = this.threadlocalCounters.get(); + ArrayList<String> deleteKeys = new ArrayList<String>(); + for (String key : thismap.keySet()) { + MutableLong curval = thismap.get(key); + long delta = curval.get(); + if (delta > 0) { + AtomicLong ctr = debugCounters.get(key); + if (ctr == null) { + // The global counter does not exist possibly because it has been + // disabled. It should thus be removed from the thread-local + // map (the counter) and set (the counter name). Removing it + // from the threadlocal set ensures that the counter will not be + // recreated (see updateCounter) + Set<String> thisset = this.threadlocalCurrentCounters.get(); + thisset.remove(key); + deleteKeys.add(key); + } else { + ctr.addAndGet(delta); + curval.set(0); + } + } + } + for (String dkey : deleteKeys) + thismap.remove(dkey); + + // At this point it is also possible that the threadlocal map/set does not + // include a counter that has been enabled and is present in the global + // currentCounters set. If so we need to sync such state so that the + // thread local counter can be created (in the updateCounter method) + Set<String> thisset = this.threadlocalCurrentCounters.get(); + if (thisset.size() != currentCounters.size()) { + thisset.addAll(currentCounters); + } + } + + @Override + public void resetCounter(String moduleCounterName) { + if (debugCounters.containsKey(moduleCounterName)) { + debugCounters.get(moduleCounterName).set(0); + } + } + + @Override + public void resetAllCounters() { + for (AtomicLong v : debugCounters.values()) { + v.set(0); + } + } + + @Override + public void resetAllModuleCounters(String moduleName) { + List<CounterInfo> cil = moduleCounters.get(moduleName); + if (cil != null) { + for (CounterInfo ci : cil) { + if (debugCounters.containsKey(ci.moduleCounterName)) { + debugCounters.get(ci.moduleCounterName).set(0); + } + } + } else { + if (log.isDebugEnabled()) + log.debug("No module found with name {}", moduleName); + } + } + + @Override + public void enableCtrOnDemand(String moduleCounterName) { + currentCounters.add(moduleCounterName); + debugCounters.putIfAbsent(moduleCounterName, new AtomicLong()); + } + + @Override + public void disableCtrOnDemand(String moduleCounterName) { + String[] temp = moduleCounterName.split("-"); + if (temp.length < 2) { + log.error("moduleCounterName {} not recognized", moduleCounterName); + return; + } + String moduleName = temp[0]; + List<CounterInfo> cil = moduleCounters.get(moduleName); + for (CounterInfo ci : cil) { + if (ci.moduleCounterName.equals(moduleCounterName) && + ci.ctype == CounterType.COUNT_ON_DEMAND) { + currentCounters.remove(moduleCounterName); + debugCounters.remove(moduleCounterName); + return; + } + } + } + + @Override + public DebugCounterInfo getCounterValue(String moduleCounterName) { + if (!debugCounters.containsKey(moduleCounterName)) return null; + long counterValue = debugCounters.get(moduleCounterName).longValue(); + + String[] temp = moduleCounterName.split("-"); + if (temp.length < 2) { + log.error("moduleCounterName {} not recognized", moduleCounterName); + return null; + } + String moduleName = temp[0]; + List<CounterInfo> cil = moduleCounters.get(moduleName); + for (CounterInfo ci : cil) { + if (ci.moduleCounterName.equals(moduleCounterName)) { + DebugCounterInfo dci = new DebugCounterInfo(); + dci.counterInfo = ci; + dci.counterValue = counterValue; + return dci; + } + } + return null; + } + + @Override + public List<DebugCounterInfo> getAllCounterValues() { + List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>(); + for (List<CounterInfo> cil : moduleCounters.values()) { + for (CounterInfo ci : cil) { + AtomicLong ctr = debugCounters.get(ci.moduleCounterName); + if (ctr != null) { + DebugCounterInfo dci = new DebugCounterInfo(); + dci.counterInfo = ci; + dci.counterValue = ctr.longValue(); + dcilist.add(dci); + } + } + } + return dcilist; + } + + @Override + public List<DebugCounterInfo> getModuleCounterValues(String moduleName) { + List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>(); + if (moduleCounters.containsKey(moduleName)) { + List<CounterInfo> cil = moduleCounters.get(moduleName); + for (CounterInfo ci : cil) { + AtomicLong ctr = debugCounters.get(ci.moduleCounterName); + if (ctr != null) { + DebugCounterInfo dci = new DebugCounterInfo(); + dci.counterInfo = ci; + dci.counterValue = ctr.longValue(); + dcilist.add(dci); + } + } + } + return dcilist; + } + + @Override + public boolean containsMCName(String moduleCounterName) { + if (debugCounters.containsKey(moduleCounterName)) return true; + // it is possible that the counter may be disabled + for (List<CounterInfo> cil : moduleCounters.values()) { + for (CounterInfo ci : cil) { + if (ci.moduleCounterName.equals(moduleCounterName)) + return true; + } + } + return false; + } + + @Override + public boolean containsModName(String moduleName) { + return (moduleCounters.containsKey(moduleName)) ? true : false; + } + + //******************************* + // Internal Methods + //******************************* + + protected void printAllCounters() { + for (List<CounterInfo> cilist : moduleCounters.values()) { + for (CounterInfo ci : cilist) { + log.info("Countername {} Countervalue {}", new Object[] { + ci.moduleCounterName, debugCounters.get(ci.moduleCounterName) + }); + } + } + } + + //******************************* + // IFloodlightModule + //******************************* + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IDebugCounterService.class); + return l; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { + Map<Class<? extends IFloodlightService>, IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); + m.put(IDebugCounterService.class, this); + return m; + } + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { + return null; + } + + @Override + public void init(FloodlightModuleContext context) throws FloodlightModuleException { + + } + + @Override + public void startUp(FloodlightModuleContext context) { + + } + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterGetResource.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterGetResource.java new file mode 100644 index 0000000000000000000000000000000000000000..395e00c102201f4eb49f8ddea69c76a3297cd2a3 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterGetResource.java @@ -0,0 +1,130 @@ +package net.floodlightcontroller.debugcounter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.floodlightcontroller.debugcounter.IDebugCounterService.DebugCounterInfo; + +import org.restlet.resource.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Return the debug counter data for the get rest-api call + * + * URI must be in one of the following forms: + * "http://{controller-hostname}:8080/wm/core/debugcounter/{param}/json + * + * where {param} must be one of (no quotes): + * "all" returns value/info on all active counters. + * "{moduleName}" returns value/info on all active counters for the specified module. + * "{moduleCounterName}" returns value/info for specific counter if it is enabled. + * + * @author Saurav + */ +public class DebugCounterGetResource extends DebugCounterResourceBase { + protected static Logger logger = + LoggerFactory.getLogger(DebugCounterGetResource.class); + + /** + * The output JSON model that contains the counter information + */ + public static class DebugCounterInfoOutput { + public Map<String, DebugCounterInfo> counterMap; + public String error; + + DebugCounterInfoOutput() { + counterMap = new HashMap<String, DebugCounterInfo>(); + error = null; + } + public Map<String, DebugCounterInfo> getCounterMap() { + return counterMap; + } + + public String getError() { + return error; + } + + } + + public enum Option { + ALL, ONE_MODULE, ONE_MODULE_COUNTER, ERROR_BAD_MODULE_NAME, ERROR_BAD_PARAM, + ERROR_BAD_MODULE_COUNTER_NAME + } + + @Get("json") + public DebugCounterInfoOutput handleCounterInfoQuery() { + DebugCounterInfoOutput output = new DebugCounterInfoOutput(); + Option choice = Option.ERROR_BAD_PARAM; + + String param = (String)getRequestAttributes().get("param"); + if (param == null) { + param = "all"; + choice = Option.ALL; + } else if (param.equals("all")) { + choice = Option.ALL; + } else if (param.contains("-")) { + // differentiate between disabled and non-existing counters + boolean isRegistered = debugCounter.containsMCName(param); + if (isRegistered) { + choice = Option.ONE_MODULE_COUNTER; + } else { + choice = Option.ERROR_BAD_MODULE_COUNTER_NAME; + } + } else { + boolean isRegistered = debugCounter.containsModName(param); + if (isRegistered) { + choice = Option.ONE_MODULE; + } else { + choice = Option.ERROR_BAD_MODULE_NAME; + } + } + + switch (choice) { + case ALL: + poplulateAllCounters(debugCounter.getAllCounterValues(), output); + break; + case ONE_MODULE: + populateModuleCounters(debugCounter.getModuleCounterValues(param), output); + break; + case ONE_MODULE_COUNTER: + populateSingleCounter(debugCounter.getCounterValue(param), output); + break; + case ERROR_BAD_MODULE_NAME: + output.error = "Module name has no corresponding registered counters"; + break; + case ERROR_BAD_MODULE_COUNTER_NAME: + output.error = "Counter not registered"; + break; + case ERROR_BAD_PARAM: + output.error = "Bad param"; + } + + return output; + } + + private void populateSingleCounter(DebugCounterInfo debugCounterInfo, + DebugCounterInfoOutput output) { + if (debugCounterInfo != null) + output.counterMap.put(debugCounterInfo.counterInfo.moduleCounterName, + debugCounterInfo); + } + + private void populateModuleCounters(List<DebugCounterInfo> moduleCounterValues, + DebugCounterInfoOutput output) { + for (DebugCounterInfo dci : moduleCounterValues) { + populateSingleCounter(dci, output); + } + } + + private void poplulateAllCounters(List<DebugCounterInfo> allCounterValues, + DebugCounterInfoOutput output) { + for (DebugCounterInfo dci : allCounterValues) { + populateSingleCounter(dci, output); + } + } + + + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResetResource.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResetResource.java new file mode 100644 index 0000000000000000000000000000000000000000..2833f487766683eb3cbb7dbdf3ccdc5ed05ade4e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResetResource.java @@ -0,0 +1,98 @@ +package net.floodlightcontroller.debugcounter; + +import org.restlet.resource.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Reset debug counter values + * + * URI must be in one of the following forms: + * "http://{controller-hostname}:8080/wm/core/debugcounter/reset/{param}/json + * + * where {param} must be one of (no quotes): + * "all" resets all active counters. + * "{moduleName}" resets all active counters for the specified module. + * "{moduleCounterName}" resets specific counter if it is enabled. + * + * @author Saurav + */ +public class DebugCounterResetResource extends DebugCounterResourceBase { + protected static Logger logger = + LoggerFactory.getLogger(DebugCounterGetResource.class); + + public enum Option { + ALL, ONE_MODULE, ONE_MODULE_COUNTER, ERROR_BAD_MODULE_NAME, ERROR_BAD_PARAM, + ERROR_BAD_MODULE_COUNTER_NAME + } + + public static class ResetOutput { + String error; + + public ResetOutput() { + error = null; + } + + public String getError() { + return error; + } + + } + + @Get("json") + public ResetOutput handleCounterResetCmd() { + Option choice = Option.ERROR_BAD_PARAM; + ResetOutput output = new ResetOutput(); + + String param = (String)getRequestAttributes().get("param"); + if (param == null) { + param = "all"; + choice = Option.ALL; + } else if (param.equals("all")) { + choice = Option.ALL; + } else if (param.contains("-")) { + // differentiate between disabled and non-existing counters + boolean isRegistered = debugCounter.containsMCName(param); + if (isRegistered) { + choice = Option.ONE_MODULE_COUNTER; + } else { + choice = Option.ERROR_BAD_MODULE_COUNTER_NAME; + } + } else { + boolean isRegistered = debugCounter.containsModName(param); + if (isRegistered) { + choice = Option.ONE_MODULE; + } else { + choice = Option.ERROR_BAD_MODULE_NAME; + } + } + + switch (choice) { + case ALL: + debugCounter.resetAllCounters(); + output.error = "None"; + break; + case ONE_MODULE: + debugCounter.resetAllModuleCounters(param); + output.error = "None"; + break; + case ONE_MODULE_COUNTER: + debugCounter.resetCounter(param); + output.error = "None"; + break; + case ERROR_BAD_MODULE_NAME: + output.error = "Module name has no corresponding registered counters"; + break; + case ERROR_BAD_MODULE_COUNTER_NAME: + output.error = "Counter not registered"; + break; + case ERROR_BAD_PARAM: + output.error = "Bad param"; + } + + return output; + } + + + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java new file mode 100644 index 0000000000000000000000000000000000000000..9edd47473a75564334c217b240002612d31e6219 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java @@ -0,0 +1,16 @@ +package net.floodlightcontroller.debugcounter; + +import org.restlet.resource.ResourceException; +import org.restlet.resource.ServerResource; + +public class DebugCounterResourceBase extends ServerResource { + + protected IDebugCounterService debugCounter; + + @Override + protected void doInit() throws ResourceException { + super.doInit(); + debugCounter = (IDebugCounterService)getContext().getAttributes(). + get(IDebugCounterService.class.getCanonicalName()); + } +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterStateResource.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterStateResource.java new file mode 100644 index 0000000000000000000000000000000000000000..40f56d1f2d14bcf94ff2b7ec1fa30d825beea1cf --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterStateResource.java @@ -0,0 +1,63 @@ +package net.floodlightcontroller.debugcounter; + +import org.restlet.resource.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Enable/disable on-demand counters + * + * URI must be in one of the following forms: + * "http://{controller-hostname}:8080/wm/core/debugcounter/{moduleCounterName}/{state}/json + * + * where {state} must be one of (no quotes): + * "enable" enables counter {moduleCounterName} if it is an on-demand counter. + * "disable" disables counter {moduleCounterName} if it is an on-demand counter. + * + * @author Saurav + */ +public class DebugCounterStateResource extends DebugCounterResourceBase { + + protected static Logger logger = + LoggerFactory.getLogger(DebugCounterStateResource.class); + + public static class StateOutput { + String error; + + public StateOutput() { + error = null; + } + + public String getError() { + return error; + } + + } + + @Get("json") + public StateOutput handleCounterStateCmd() { + StateOutput output = new StateOutput(); + + String state = (String)getRequestAttributes().get("state"); + String moduleCounterName = (String)getRequestAttributes().get("moduleCounterName"); + + if (!moduleCounterName.contains("-")) { + output.error = "Specified moduleCounterName is not of type " + + "<moduleName>-<counterName>."; + return output; + } + + if ( !(state.equals("enable") || state.equals("disable")) ) { + output.error = "State must be either enable or disable"; + return output; + } + + if (state.equals("enable")) { + debugCounter.enableCtrOnDemand(moduleCounterName); + } else { + debugCounter.disableCtrOnDemand(moduleCounterName); + } + output.error = "None"; + return output; + } +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java new file mode 100644 index 0000000000000000000000000000000000000000..18fab72cbb7bfe43035377d720b0718b7d4e1331 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java @@ -0,0 +1,159 @@ +package net.floodlightcontroller.debugcounter; + +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugcounter.DebugCounter.CounterInfo; +import java.util.List; + +public interface IDebugCounterService extends IFloodlightService { + + /** + * Different counter types. Counters that are meant to be counted on demand + * need to be separately enabled/disabled. + */ + public enum CounterType { + ALWAYS_COUNT, + COUNT_ON_DEMAND + } + + public class DebugCounterInfo { + CounterInfo counterInfo; + Long counterValue; + + public CounterInfo getCounterInfo() { + return counterInfo; + } + public Long getCounterValue() { + return counterValue; + } + } + + /** + * All modules that wish to have the DebugCounterService count for them, must + * register their counters by making this call (typically from that module's + * 'startUp' method). The counter can then be updated, displayed, reset etc. + * using the registered moduleCounterName. + * + * @param moduleCounterName the counter name which MUST be have the following + * syntax: <module name>-<counter name> + * eg.: linkdiscovery-incoming + * There should be only a single '-' in the name + * @param counterDescription a descriptive string that gives more information + * of what the counter is measuring. For example, + * "Measures the number of incoming packets seen by + * this module". + * @param counterType One of CounterType. COUNT_ON_DEMAND counter types + * need to be explicitly enabled/disabled using other + * methods in this API -- i.e. registering them is + * not enough to start counting. + * @return false if the counter has already been registered + * or if the moduleCounterName is not as expected. + */ + public boolean registerCounter(String moduleCounterName, String counterDescription, + CounterType counterType); + + /** + * Increments the counter by 1, if the counter is meant to be always counted, + * or if the counter has been enabled for counting. + * @param moduleCounterName the registered counter name. + */ + public void updateCounter(String moduleCounterName); + + /** + * Update the global counter map with values from the thread local maps. This + * method is not intended for use by any module. It's typical usage is from + * floodlight core. As far as the modules are concerned, this should happen + * automatically for their counters. + */ + public void flushCounters(); + + /** + * Resets the value of the counter to zero if it is currently enabled. Note + * that with live traffic, it is not necessary that the counter will display + * zero with a get call as it may get updated between the reset and get calls. + * @param moduleCounterName the registered counter name. + */ + public void resetCounter(String moduleCounterName); + + /** + * Resets the values of all counters that are currently enabled to zero. + */ + public void resetAllCounters(); + + /** + * Resets the values of all counters that are currently active and belong + * to a module with the given 'moduleName'. The moduleName MUST be the + * part of the moduleCounterName with which the counters were registered. + * eg. if 'linkdiscovery-incoming' and 'linkdiscovery-lldpeol' are two counters + * the module name is 'linkdiscovery' + * @param moduleName + */ + public void resetAllModuleCounters(String moduleName); + + /** + * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to + * enable counting on the counter. Note that this step is necessary to start + * counting for these counter types - merely registering the counter is not + * enough (as is the case for CounterType.ALWAYS_COUNT). Note that newly + * enabled counter starts from an initial value of zero. + * + * @param moduleCounterName the registered counter name. + */ + public void enableCtrOnDemand(String moduleCounterName); + + /** + * This method applies only to CounterType.ALWAYS_COUNT. It is used to disable + * counting on this counter. Note that disabling a counter results in a loss + * of the counter value. When re-enabled the counter will restart from zero. + * + * @param moduleCounterName the registered counter name. + */ + public void disableCtrOnDemand(String moduleCounterName); + + /** + * Get counter value and associated information for a specific counter if it + * is active. + * + * @param moduleCounterName + * @return DebugCounterInfo or null if the counter could not be found + */ + public DebugCounterInfo getCounterValue(String moduleCounterName); + + /** + * Get counter values and associated information for all active counters + * + * @return the list of values/info or an empty list + */ + public List<DebugCounterInfo> getAllCounterValues(); + + /** + * Get counter values and associated information for all active counters associated + * with a module. + * + * @param moduleName + * @return the list of values/info or an empty list + */ + public List<DebugCounterInfo> getModuleCounterValues(String moduleName); + + /** + * Convenience method to figure out if the the given 'moduleCounterName' corresponds + * to a registered moduleCounterName or not. Note that the counter may or + * may not be enabled for counting, but if it is registered the method will + * return true. + * + * @param param + * @return false if moduleCounterName is not a registered counter + */ + public boolean containsMCName(String moduleCounterName); + + /** + * Convenience method to figure out if the the given 'moduleName' corresponds + * to a registered moduleName or not. Note that the module may or may not have + * a counter enabled for counting, but if it is registered the method will + * return true. + * + * @param param + * @return false if moduleName is not a registered counter + */ + public boolean containsModName(String moduleName); + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..5e5ffbba2f3de68af207d63c08909451bd032186 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java @@ -0,0 +1,112 @@ +package net.floodlightcontroller.debugcounter; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; + +public class NullDebugCounter implements IFloodlightModule, IDebugCounterService { + + @Override + public boolean registerCounter(String moduleCounterName, + String counterDescription, + CounterType counterType) { + return false; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleServices() { + return null; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + return null; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + return null; + } + + @Override + public + void + init(FloodlightModuleContext context) + throws FloodlightModuleException { + + } + + @Override + public void startUp(FloodlightModuleContext context) { + + } + + @Override + public void updateCounter(String moduleCounterName) { + + } + + @Override + public void flushCounters() { + + } + + @Override + public void resetCounter(String moduleCounterName) { + + } + + @Override + public void resetAllCounters() { + + } + + @Override + public void resetAllModuleCounters(String moduleName) { + + } + + @Override + public void enableCtrOnDemand(String moduleCounterName) { + + } + + @Override + public void disableCtrOnDemand(String moduleCounterName) { + + } + + @Override + public DebugCounterInfo getCounterValue(String moduleCounterName) { + return null; + } + + @Override + public List<DebugCounterInfo> getAllCounterValues() { + return null; + } + + @Override + public List<DebugCounterInfo> getModuleCounterValues(String moduleName) { + return null; + } + + @Override + public boolean containsMCName(String moduleCounterName) { + return false; + } + + @Override + public boolean containsModName(String moduleName) { + return false; + } + +} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 69fac694665f14e6df08ada226d0a5496e2b9884..5e8f221a9829bdcf25d0f37a41fd33047db3affe 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -51,6 +51,12 @@ public interface IDeviceService extends IFloodlightService { public static final String CONTEXT_DST_DEVICE = "net.floodlightcontroller.devicemanager.dstDevice"; + /** + * The original destination device for the current packet-in + */ + public static final String CONTEXT_ORIG_DST_DEVICE = + "net.floodlightcontroller.devicemanager.origDstDevice"; + /** * A FloodlightContextStore object that can be used to interact with the * FloodlightContext information created by BVS manager. diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java index 22402ced28f3bb79f57629220a40ed8576c790a2..1b53550f6fe5f047db8160e4399e005c85e08dda 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java @@ -20,7 +20,7 @@ package net.floodlightcontroller.devicemanager; import java.util.Set; /** - * Implementors of this interface can receive updates from DeviceManager about + * Implementors of this interface can receive updates from the Entity Classifier about * the changes to entity Classes. * * @author Ananth Suryanarayana (Ananth.Suryanarayana@bigswitch.com) diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 9fea72297ea0f4c779c45305d435f27ea6af811e..bbbccee7831c20d868ee12ba2678beec9c758486 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -417,9 +417,11 @@ public class Device implements IDevice { if (apMap == null || apMap.isEmpty()) { apList.add(newAP); attachmentPoints = apList; - // there are no old attachment points - we should not treat this - // as a device moved. - return false; + // there are no old attachment points - since the device exists, this + // may be because the host really moved (so the old AP port went down); + // or it may be because the switch restarted (so old APs were nullified). + // For now we will treat both cases as host moved. + return true; } long id = topology.getL2DomainId(sw); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 09a46152b41ad25dbc51d057c4ab1c3c23e8a2ef..7a8b5155d3a6ec5c45fef3101f75c0f06b7ac04b 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -49,6 +49,9 @@ import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.ListenerDispatcher; import net.floodlightcontroller.core.util.SingletonTask; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.NullDebugCounter; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClass; @@ -104,6 +107,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { protected IRestApiService restApi; protected IThreadPoolService threadPool; protected IFlowReconcileService flowReconcileMgr; + protected IDebugCounterService debugCounters; /** * Time in milliseconds before entities will expire @@ -595,6 +599,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: + debugCounters.updateCounter("devicemanager-incoming"); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: @@ -716,6 +721,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { this.threadPool = fmc.getServiceImpl(IThreadPoolService.class); this.flowReconcileMgr = fmc.getServiceImpl(IFlowReconcileService.class); this.entityClassifier = fmc.getServiceImpl(IEntityClassifierService.class); + this.debugCounters = fmc.getServiceImpl(IDebugCounterService.class); } @Override @@ -753,6 +759,18 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } else { logger.debug("Could not instantiate REST API"); } + + registerDeviceManagerDebugCounters(); + } + + private void registerDeviceManagerDebugCounters() { + if (debugCounters == null) { + logger.error("Debug Counter Service not found."); + debugCounters = new NullDebugCounter(); + return; + } + debugCounters.registerCounter(getName() + "-" + "incoming", + "All incoming packets seen by this module", CounterType.ALWAYS_COUNT); } // *************** @@ -840,10 +858,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { * @param srcDevice */ private void snoopDHCPClientName(Ethernet eth, Device srcDevice) { - if (eth.getEtherType() != Ethernet.TYPE_IPv4) + if (! (eth.getPayload() instanceof IPv4) ) return; IPv4 ipv4 = (IPv4) eth.getPayload(); - if (ipv4.getProtocol() != IPv4.PROTOCOL_UDP) + if (! (ipv4.getPayload() instanceof UDP) ) return; UDP udp = (UDP) ipv4.getPayload(); if (!(udp.getPayload() instanceof DHCP)) @@ -1243,9 +1261,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener { device.getEntityClass().getName(), entity); return null; } - // If this is an internal port we don't learn the new entity - // and don't update indexes. We only learn on attachment point - // ports. + // If this is not an attachment point port we don't learn the new entity + // and don't update indexes. But we do allow the device to continue up + // the chain. if (entity.hasSwitchPort() && !topology.isAttachmentPointPort(entity.getSwitchDPID(), entity.getSwitchPort().shortValue())) { diff --git a/src/main/java/net/floodlightcontroller/firewall/Firewall.java b/src/main/java/net/floodlightcontroller/firewall/Firewall.java index c6a4fd763df1df46babb4b92fef411070365d7c8..21dc8aed6ca937f3123e9a00971ee8b0fc13fd80 100644 --- a/src/main/java/net/floodlightcontroller/firewall/Firewall.java +++ b/src/main/java/net/floodlightcontroller/firewall/Firewall.java @@ -595,7 +595,7 @@ public class Firewall implements IFirewallService, IOFMessageListener, // the case to determine if we have L2 broadcast + L3 unicast // don't allow this broadcast packet if such is the case (malformed // packet) - if (eth.getEtherType() == Ethernet.TYPE_IPv4 + if ((eth.getPayload() instanceof IPv4) && this.IPIsBroadcast(((IPv4) eth.getPayload()) .getDestinationAddress()) == false) { allowBroadcast = false; diff --git a/src/main/java/net/floodlightcontroller/hub/Hub.java b/src/main/java/net/floodlightcontroller/hub/Hub.java index 3618351d75cf7fe899400fad41280452600f8a09..70b920345051bb51669ba15df09e787096b3fc3f 100644 --- a/src/main/java/net/floodlightcontroller/hub/Hub.java +++ b/src/main/java/net/floodlightcontroller/hub/Hub.java @@ -78,7 +78,7 @@ public class Hub implements IFloodlightModule, IOFMessageListener { po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); // set data if is is included in the packetin - if (pi.getBufferId() == 0xffffffff) { + if (pi.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { byte[] packetData = pi.getPacketData(); po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length)); diff --git a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java index 4a9ffa35c4e41980e3b7651094b00182156cd87d..2e8ce6f1baf678771543273e80e0f3dad0277054 100644 --- a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java +++ b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -52,7 +52,6 @@ import net.floodlightcontroller.counter.ICounterStoreService; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.restserver.IRestApiService; -import org.openflow.protocol.OFError; import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFFlowRemoved; import org.openflow.protocol.OFMatch; @@ -68,15 +67,15 @@ import org.openflow.util.LRULinkedHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class LearningSwitch +public class LearningSwitch implements IFloodlightModule, ILearningSwitchService, IOFMessageListener { protected static Logger log = LoggerFactory.getLogger(LearningSwitch.class); - + // Module dependencies protected IFloodlightProviderService floodlightProvider; protected ICounterStoreService counterStore; protected IRestApiService restApi; - + // Stores the learned state for each switch protected Map<IOFSwitch, Map<MacVlanPair,Short>> macVlanToSwitchPortMap; @@ -87,25 +86,25 @@ public class LearningSwitch public static final int APP_ID_BITS = 12; public static final int APP_ID_SHIFT = (64 - APP_ID_BITS); public static final long LEARNING_SWITCH_COOKIE = (long) (LEARNING_SWITCH_APP_ID & ((1 << APP_ID_BITS) - 1)) << APP_ID_SHIFT; - - // more flow-mod defaults + + // more flow-mod defaults protected static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds protected static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite protected static short FLOWMOD_PRIORITY = 100; - + // for managing our map sizes - protected static final int MAX_MACS_PER_SWITCH = 1000; + protected static final int MAX_MACS_PER_SWITCH = 1000; // normally, setup reverse flow as well. Disable only for using cbench for comparison with NOX etc. protected static final boolean LEARNING_SWITCH_REVERSE_FLOW = true; - + /** * @param floodlightProvider the floodlightProvider to set */ public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) { this.floodlightProvider = floodlightProvider; } - + @Override public String getName() { return "learningswitch"; @@ -120,13 +119,13 @@ public class LearningSwitch */ protected void addToPortMap(IOFSwitch sw, long mac, short vlan, short portVal) { Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw); - + if (vlan == (short) 0xffff) { // OFMatch.loadFromPacket sets VLAN ID to 0xffff if the packet contains no VLAN tag; // for our purposes that is equivalent to the default VLAN ID 0 vlan = 0; } - + if (swMap == null) { // May be accessed by REST API so we need to make it thread safe swMap = Collections.synchronizedMap(new LRULinkedHashMap<MacVlanPair,Short>(MAX_MACS_PER_SWITCH)); @@ -134,7 +133,7 @@ public class LearningSwitch } swMap.put(new MacVlanPair(mac, vlan), portVal); } - + /** * Removes a host from the MAC/VLAN->SwitchPort mapping * @param sw The switch to remove the mapping from @@ -164,18 +163,18 @@ public class LearningSwitch Map<MacVlanPair,Short> swMap = macVlanToSwitchPortMap.get(sw); if (swMap != null) return swMap.get(new MacVlanPair(mac, vlan)); - + // if none found return null; } - + /** * Clears the MAC/VLAN -> SwitchPort map for all switches */ public void clearLearnedTable() { macVlanToSwitchPortMap.clear(); } - + /** * Clears the MAC/VLAN -> SwitchPort map for a single switch * @param sw The switch to clear the mapping for @@ -185,12 +184,12 @@ public class LearningSwitch if (swMap != null) swMap.clear(); } - + @Override public synchronized Map<IOFSwitch, Map<MacVlanPair,Short>> getTable() { return macVlanToSwitchPortMap; } - + /** * Writes a OFFlowMod to a switch. * @param sw The switch tow rite the flowmod to. @@ -223,7 +222,7 @@ public class LearningSwitch // from the length field in the // header. */ // }; - + OFFlowMod flowMod = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD); flowMod.setMatch(match); flowMod.setCookie(LearningSwitch.LEARNING_SWITCH_COOKIE); @@ -247,12 +246,12 @@ public class LearningSwitch flowMod.setLength((short) (OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH)); if (log.isTraceEnabled()) { - log.trace("{} {} flow mod {}", + log.trace("{} {} flow mod {}", new Object[]{ sw, (command == OFFlowMod.OFPFC_DELETE) ? "deleting" : "adding", flowMod }); } - counterStore.updatePktOutFMCounterStore(sw, flowMod); - + counterStore.updatePktOutFMCounterStoreLocal(sw, flowMod); + // and write it out try { sw.write(flowMod, null); @@ -260,11 +259,11 @@ public class LearningSwitch log.error("Failed to write {} to switch {}", new Object[]{ flowMod, sw }, e); } } - + /** * Pushes a packet-out to a switch. The assumption here is that * the packet-in was also generated from the same switch. Thus, if the input - * port of the packet-in and the outport are the same, the function will not + * port of the packet-in and the outport are the same, the function will not * push the packet-out. * @param sw switch that generated the packet-in, and from which packet-out is sent * @param match OFmatch @@ -276,21 +275,21 @@ public class LearningSwitch return; } - // The assumption here is (sw) is the switch that generated the + // The assumption here is (sw) is the switch that generated the // packet-in. If the input port is the same as output port, then // the packet-out should be ignored. if (pi.getInPort() == outport) { if (log.isDebugEnabled()) { - log.debug("Attempting to do packet-out to the same " + - "interface as packet-in. Dropping packet. " + - " SrcSwitch={}, match = {}, pi={}", + log.debug("Attempting to do packet-out to the same " + + "interface as packet-in. Dropping packet. " + + " SrcSwitch={}, match = {}, pi={}", new Object[]{sw, match, pi}); return; } } if (log.isTraceEnabled()) { - log.trace("PacketOut srcSwitch={} match={} pi={}", + log.trace("PacketOut srcSwitch={} match={} pi={}", new Object[] {sw, match, pi}); } @@ -336,15 +335,15 @@ public class LearningSwitch log.error("Failure writing packet out", e); } } - + /** * Writes an OFPacketOut message to a switch. * @param sw The switch to write the PacketOut to. * @param packetInMessage The corresponding PacketIn. * @param egressPort The switchport to output the PacketOut. */ - private void writePacketOutForPacketIn(IOFSwitch sw, - OFPacketIn packetInMessage, + private void writePacketOutForPacketIn(IOFSwitch sw, + OFPacketIn packetInMessage, short egressPort) { // from openflow 1.0 spec - need to set these on a struct ofp_packet_out: // uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */ @@ -354,7 +353,7 @@ public class LearningSwitch /* uint8_t data[0]; */ /* Packet data. The length is inferred from the length field in the header. (Only meaningful if buffer_id == -1.) */ - + OFPacketOut packetOutMessage = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); short packetOutLength = (short)OFPacketOut.MINIMUM_LENGTH; // starting length @@ -363,34 +362,34 @@ public class LearningSwitch packetOutMessage.setInPort(packetInMessage.getInPort()); packetOutMessage.setActionsLength((short)OFActionOutput.MINIMUM_LENGTH); packetOutLength += OFActionOutput.MINIMUM_LENGTH; - + // set actions - List<OFAction> actions = new ArrayList<OFAction>(1); + List<OFAction> actions = new ArrayList<OFAction>(1); actions.add(new OFActionOutput(egressPort, (short) 0)); packetOutMessage.setActions(actions); // set data - only if buffer_id == -1 if (packetInMessage.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { byte[] packetData = packetInMessage.getPacketData(); - packetOutMessage.setPacketData(packetData); + packetOutMessage.setPacketData(packetData); packetOutLength += (short)packetData.length; } - + // finally, set the total length - packetOutMessage.setLength(packetOutLength); - + packetOutMessage.setLength(packetOutLength); + // and write it out try { - counterStore.updatePktOutFMCounterStore(sw, packetOutMessage); + counterStore.updatePktOutFMCounterStoreLocal(sw, packetOutMessage); sw.write(packetOutMessage, null); } catch (IOException e) { log.error("Failed to write {} to switch {}: {}", new Object[]{ packetOutMessage, sw, e }); } } - + /** * Processes a OFPacketIn message. If the switch has learned the MAC/VLAN to port mapping - * for the pair it will write a FlowMod for. If the mapping has not been learned the + * for the pair it will write a FlowMod for. If the mapping has not been learned the * we will flood the packet. * @param sw * @param pi @@ -415,7 +414,7 @@ public class LearningSwitch // If source MAC is a unicast address, learn the port for this MAC/VLAN this.addToPortMap(sw, sourceMac, vlan, pi.getInPort()); } - + // Now output flow-mod and/or packet Short outPort = getFromPortMap(sw, destMac, vlan); if (outPort == null) { @@ -483,7 +482,7 @@ public class LearningSwitch // it from the macVlanToPortMap to revert to flooding packets to this device. this.removeFromPortMap(sw, Ethernet.toLong(match.getDataLayerSource()), match.getDataLayerVirtualLan()); - + // Also, if packets keep coming from another device (e.g. from ping), the // corresponding reverse flow entry will never expire on its own and will // send the packets to the wrong port (the matching input port of the @@ -501,9 +500,9 @@ public class LearningSwitch match.getInputPort()); return Command.CONTINUE; } - + // IOFMessageListener - + @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { @@ -512,10 +511,10 @@ public class LearningSwitch case FLOW_REMOVED: return this.processFlowRemovedMessage(sw, (OFFlowRemoved) msg); case ERROR: - log.info("received an error {} from switch {}", (OFError) msg, sw); + log.info("received an error {} from switch {}", msg, sw); return Command.CONTINUE; default: - break; + break; } log.error("received an unexpected message {} from switch {}", msg, sw); return Command.CONTINUE; @@ -532,10 +531,10 @@ public class LearningSwitch } // IFloodlightModule - + @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = + Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(ILearningSwitchService.class); return l; @@ -545,7 +544,7 @@ public class LearningSwitch public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { Map<Class<? extends IFloodlightService>, - IFloodlightService> m = + IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); m.put(ILearningSwitchService.class, this); @@ -555,7 +554,7 @@ public class LearningSwitch @Override public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = + Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(IFloodlightProviderService.class); l.add(ICounterStoreService.class); @@ -566,7 +565,7 @@ public class LearningSwitch @Override public void init(FloodlightModuleContext context) throws FloodlightModuleException { - macVlanToSwitchPortMap = + macVlanToSwitchPortMap = new ConcurrentHashMap<IOFSwitch, Map<MacVlanPair,Short>>(); floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); @@ -582,7 +581,7 @@ public class LearningSwitch floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this); floodlightProvider.addOFMessageListener(OFType.ERROR, this); restApi.addRestletRoutable(new LearningSwitchWebRoutable()); - + // read our config options Map<String, String> configOptions = context.getConfigParams(this); try { @@ -615,11 +614,11 @@ public class LearningSwitch "using default of {}", FLOWMOD_PRIORITY); } - log.debug("FlowMod idle timeout set to {} seconds", + log.debug("FlowMod idle timeout set to {} seconds", FLOWMOD_DEFAULT_IDLE_TIMEOUT); - log.debug("FlowMod hard timeout set to {} seconds", + log.debug("FlowMod hard timeout set to {} seconds", FLOWMOD_DEFAULT_HARD_TIMEOUT); - log.debug("FlowMod priority set to {}", + log.debug("FlowMod priority set to {}", FLOWMOD_PRIORITY); } } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 53cbb3ac89b95e7cad3457c3b3fed67622afcaf5..3e991c514227aa46cce1d2135a8b8edb27ac55f5 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -57,6 +57,9 @@ import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.SingletonTask; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; +import net.floodlightcontroller.debugcounter.NullDebugCounter; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.SwitchType; @@ -118,7 +121,7 @@ import org.slf4j.LoggerFactory; public class LinkDiscoveryManager implements IOFMessageListener, IOFSwitchListener, IStorageSourceListener, ILinkDiscoveryService, IFloodlightModule, IInfoProvider, IHAListener { - protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class); + protected static final Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class); // Names of table/fields for links in the storage API private static final String TOPOLOGY_TABLE_NAME = "controller_topologyconfig"; @@ -142,6 +145,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, protected IStorageSourceService storageSource; protected IThreadPoolService threadPool; protected IRestApiService restApi; + protected IDebugCounterService debugCounters; // LLDP and BDDP fields private static final byte[] LLDP_STANDARD_DST_MAC_STRING = @@ -510,6 +514,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: + debugCounters.updateCounter("linkdiscovery-incoming"); return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx); case PORT_STATUS: @@ -539,7 +544,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - if (eth.getEtherType() == Ethernet.TYPE_BSN) { + if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; @@ -548,7 +553,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx); - } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) { + } else if (eth.getPayload() instanceof LLDP) { return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx); } else if (eth.getEtherType() < 1500) { long destMac = eth.getDestinationMAC().toLong(); @@ -639,7 +644,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, // broadcast the packet as a regular packet (after checking IDs) if (isStandard) { if (log.isTraceEnabled()) { - log.trace("Got a standard LLDP=[{}]. Not fowarding it.", lldp.toString()); + log.trace("Got a standard LLDP=[{}] that was not sent by" + + " this controller. Not fowarding it.", lldp.toString()); } return Command.STOP; } else if (myId < otherId) { @@ -764,6 +770,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, removeFromMaintenanceQueue(nptDst); // Consume this message + debugCounters.updateCounter("linkdiscovery-lldpeol"); return Command.STOP; } @@ -990,7 +997,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL; if (lldpClock == 0) { - log.debug("Sending LLDP out on all ports."); + if (log.isTraceEnabled()) + log.trace("Sending LLDP out on all ports."); discoverOnAllPorts(); } } @@ -1344,6 +1352,14 @@ public class LinkDiscoveryManager implements IOFMessageListener, long dst, short dstPort) { return true; } + @LogMessageDocs({ + @LogMessageDoc(message="Inter-switch link detected:", + explanation="Detected a new link between two openflow switches," + + "use show link to find current status"), + @LogMessageDoc(message="Inter-switch link updated:", + explanation="Detected a link change between two openflow switches, " + + "use show link to find current status") + }) protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) { NodePortTuple srcNpt, dstNpt; @@ -1398,11 +1414,16 @@ public class LinkDiscoveryManager implements IOFMessageListener, updateOperation = UpdateOperation.LINK_UPDATED; linkChanged = true; - // Add to event history + // Log direct links only. Multi-hop links may be numerous + // Add all to event history + LinkType linkType = getLinkType(lt, newInfo); + if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) { + log.info("Inter-switch link detected: {}", lt); + } evHistTopoLink(lt.getSrc(), lt.getDst(), lt.getSrcPort(), lt.getDstPort(), newInfo.getSrcPortState(), newInfo.getDstPortState(), - getLinkType(lt, newInfo), + linkType, EvAction.LINK_ADDED, "LLDP Recvd"); } else { // Since the link info is already there, we need to @@ -1453,15 +1474,16 @@ public class LinkDiscoveryManager implements IOFMessageListener, if (linkChanged) { updateOperation = getUpdateOperation(newInfo.getSrcPortState(), newInfo.getDstPortState()); - if (log.isTraceEnabled()) { - log.trace("Updated link {}", lt); + LinkType linkType = getLinkType(lt, newInfo); + if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) { + log.info("Inter-switch link updated: {}", lt); } // Add to event history evHistTopoLink(lt.getSrc(), lt.getDst(), lt.getSrcPort(), lt.getDstPort(), newInfo.getSrcPortState(), newInfo.getDstPortState(), - getLinkType(lt, newInfo), + linkType, EvAction.LINK_PORT_STATE_UPDATED, "LLDP Recvd"); } @@ -1497,6 +1519,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, * @param links * The List of @LinkTuple to delete. */ + @LogMessageDoc(message="Inter-switch link removed:", + explanation="A previously detected link between two openflow switches no longer exists, " + + "use show link to find current status") protected void deleteLinks(List<Link> links, String reason, List<LDUpdate> updateList) { @@ -1529,11 +1554,12 @@ public class LinkDiscoveryManager implements IOFMessageListener, } LinkInfo info = this.links.remove(lt); + LinkType linkType = getLinkType(lt, info); linkUpdateList.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(), lt.getDst(), lt.getDstPort(), - getLinkType(lt, info), + linkType, UpdateOperation.LINK_REMOVED)); // Update Event History @@ -1549,7 +1575,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, // TODO Whenever link is removed, it has to checked if // the switchports must be added to quarantine. - if (log.isTraceEnabled()) { + if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) { + log.info("Inter-switch link removed: {}", lt); + } else if (log.isTraceEnabled()) { log.trace("Deleted link {}", lt); } } @@ -2094,6 +2122,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, storageSource = context.getServiceImpl(IStorageSourceService.class); threadPool = context.getServiceImpl(IThreadPoolService.class); restApi = context.getServiceImpl(IRestApiService.class); + debugCounters = context.getServiceImpl(IDebugCounterService.class); // read our config options Map<String, String> configOptions = context.getConfigParams(this); @@ -2176,6 +2205,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, + "switch table {}", SWITCH_CONFIG_TABLE_NAME); } + registerLinkDiscoveryDebugCounters(); + ScheduledExecutorService ses = threadPool.getScheduledExecutor(); // To be started by the first switch connection @@ -2249,6 +2280,18 @@ public class LinkDiscoveryManager implements IOFMessageListener, setControllerTLV(); } + private void registerLinkDiscoveryDebugCounters() { + if (debugCounters == null) { + log.error("Debug Counter Service not found."); + debugCounters = new NullDebugCounter(); + return; + } + debugCounters.registerCounter(getName() + "-" + "incoming", + "All incoming packets seen by this module", CounterType.ALWAYS_COUNT); + debugCounters.registerCounter(getName() + "-" + "lldpeol", + "End of Life for LLDP packets", CounterType.COUNT_ON_DEMAND); + } + // **************************************************** // Topology Manager's Event History members and methods // **************************************************** diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java index 83b9eaf573ea92e41603eb8bacd706bcf16e32eb..b2a35c2b7ae4a2a3339aa0358510309f8cfbeaa3 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java @@ -69,6 +69,7 @@ import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.ARP; import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.ICMP; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.packet.TCP; @@ -194,7 +195,7 @@ public class LoadBalancer implements IFloodlightModule, if (eth.isBroadcast() || eth.isMulticast()) { // handle ARP for VIP - if (eth.getEtherType() == Ethernet.TYPE_ARP) { + if (pkt instanceof ARP) { // retrieve arp to determine target IP address ARP arpRequest = (ARP) eth.getPayload(); @@ -219,17 +220,17 @@ public class LoadBalancer implements IFloodlightModule, IPClient client = new IPClient(); client.ipAddress = ip_pkt.getSourceAddress(); client.nw_proto = ip_pkt.getProtocol(); - if (client.nw_proto == IPv4.PROTOCOL_TCP) { + if (ip_pkt.getPayload() instanceof TCP) { TCP tcp_pkt = (TCP) ip_pkt.getPayload(); client.srcPort = tcp_pkt.getSourcePort(); client.targetPort = tcp_pkt.getDestinationPort(); } - if (client.nw_proto == IPv4.PROTOCOL_UDP) { + if (ip_pkt.getPayload() instanceof UDP) { UDP udp_pkt = (UDP) ip_pkt.getPayload(); client.srcPort = udp_pkt.getSourcePort(); client.targetPort = udp_pkt.getDestinationPort(); } - if (client.nw_proto == IPv4.PROTOCOL_ICMP) { + if (ip_pkt.getPayload() instanceof ICMP) { client.srcPort = 8; client.targetPort = 0; } @@ -268,6 +269,8 @@ public class LoadBalancer implements IFloodlightModule, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); // retrieve original arp to determine host configured gw IP address + if (! (eth.getPayload() instanceof ARP)) + return; ARP arpRequest = (ARP) eth.getPayload(); // have to do proxy arp reply since at this point we cannot determine the requesting application type diff --git a/src/main/java/net/floodlightcontroller/packet/BasePacket.java b/src/main/java/net/floodlightcontroller/packet/BasePacket.java index 4ecfdedddd813b1fbd42a3ad1a2dbc48eafc45ed..eed530c03d2e8047774697bc70032d58eee63981 100644 --- a/src/main/java/net/floodlightcontroller/packet/BasePacket.java +++ b/src/main/java/net/floodlightcontroller/packet/BasePacket.java @@ -17,12 +17,16 @@ package net.floodlightcontroller.packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * @author David Erickson (daviderickson@cs.stanford.edu) */ public abstract class BasePacket implements IPacket { + public static final Logger log = LoggerFactory.getLogger(BasePacket.class); protected IPacket parent; protected IPacket payload; @@ -113,4 +117,4 @@ public abstract class BasePacket implements IPacket { pkt.setParent(this.parent); return pkt; } -} \ No newline at end of file +} diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index 44a908118d9e649ec0421f5620684a9d967f6519..4fc2380762a5c71e019886cb6c0da82e6b84fc19 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -230,7 +230,7 @@ public class Ethernet extends BasePacket { @Override public IPacket deserialize(byte[] data, int offset, int length) { - if (length <= 0) + if (length <= 16) // Ethernet packet minium should be 60, this is reasonable return null; ByteBuffer bb = ByteBuffer.wrap(data, offset, length); if (this.destinationMACAddress == null) @@ -261,13 +261,23 @@ public class Ethernet extends BasePacket { Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get(this.etherType); try { payload = clazz.newInstance(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } catch (Exception e) { - throw new RuntimeException("Error parsing payload for Ethernet packet", e); + if (log.isTraceEnabled()) { + log.trace("Failed to parse ethernet packet {}->{}" + + " payload as {}, treat as plain ethernet packet", + new Object[] {this.sourceMACAddress, + this.destinationMACAddress, + clazz.getClass().getName()}); + log.trace("Exception from parsing {}", e); + } + payload = new Data(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } } else { payload = new Data(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } - this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); this.payload.setParent(this); return this; } diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java index 76a140ed002bad4ab93db3488a771b2141223376..afc31425dc5106b9587d03088bfe0135e0b2331d 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPv4.java +++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java @@ -43,6 +43,12 @@ public class IPv4 extends BasePacket { protocolClassMap.put(PROTOCOL_UDP, UDP.class); } + public static final byte IPV4_FLAGS_MOREFRAG = 0x1; + public static final byte IPV4_FLAGS_DONTFRAG = 0x2; + public static final byte IPV4_FLAGS_MASK = 0x7; + public static final byte IPV4_FLAGS_SHIFT = 13; + public static final short IPV4_OFFSET_MASK = (1 << IPV4_FLAGS_SHIFT) - 1; + protected byte version; protected byte headerLength; protected byte diffServ; @@ -58,6 +64,7 @@ public class IPv4 extends BasePacket { protected byte[] options; protected boolean isTruncated; + protected boolean isFragment; /** * Default constructor that sets the version to 4. @@ -66,6 +73,7 @@ public class IPv4 extends BasePacket { super(); this.version = 4; isTruncated = false; + isFragment = false; } /** @@ -127,6 +135,14 @@ public class IPv4 extends BasePacket { this.isTruncated = isTruncated; } + public boolean isFragment() { + return isFragment; + } + + public void setFragment(boolean isFrag) { + this.isFragment = isFrag; + } + /** * @param identification the identification to set */ @@ -308,7 +324,8 @@ public class IPv4 extends BasePacket { bb.put(this.diffServ); bb.putShort(this.totalLength); bb.putShort(this.identification); - bb.putShort((short) (((this.flags & 0x7) << 13) | (this.fragmentOffset & 0x1fff))); + bb.putShort((short)(((this.flags & IPV4_FLAGS_MASK) << IPV4_FLAGS_SHIFT) + | (this.fragmentOffset & IPV4_OFFSET_MASK))); bb.put(this.ttl); bb.put(this.protocol); bb.putShort(this.checksum); @@ -346,8 +363,8 @@ public class IPv4 extends BasePacket { this.totalLength = bb.getShort(); this.identification = bb.getShort(); sscratch = bb.getShort(); - this.flags = (byte) ((sscratch >> 13) & 0x7); - this.fragmentOffset = (short) (sscratch & 0x1fff); + this.flags = (byte) ((sscratch >> IPV4_FLAGS_SHIFT) & IPV4_FLAGS_MASK); + this.fragmentOffset = (short) (sscratch & IPV4_OFFSET_MASK); this.ttl = bb.get(); this.protocol = bb.get(); this.checksum = bb.getShort(); @@ -361,7 +378,9 @@ public class IPv4 extends BasePacket { } IPacket payload; - if (IPv4.protocolClassMap.containsKey(this.protocol)) { + isFragment = ((this.flags & IPV4_FLAGS_MOREFRAG) != 0) || + (this.fragmentOffset != 0); + if (!isFragment && IPv4.protocolClassMap.containsKey(this.protocol)) { Class<? extends IPacket> clazz = IPv4.protocolClassMap.get(this.protocol); try { payload = clazz.newInstance(); @@ -369,6 +388,11 @@ public class IPv4 extends BasePacket { throw new RuntimeException("Error parsing payload for IPv4 packet", e); } } else { + if (log.isTraceEnabled() && isFragment) { + log.trace("IPv4 fragment detected {}->{}, forward using IP header only", + fromIPv4Address(this.sourceAddress), + fromIPv4Address(this.destinationAddress)); + } payload = new Data(); } int payloadLength = this.totalLength - this.headerLength * 4; @@ -493,7 +517,7 @@ public class IPv4 extends BasePacket { * @return The IP address separated into bytes. */ public static byte[] toIPv4AddressBytes(int ipAddress) { - return new byte[] { + return new byte[] { (byte)(ipAddress >>> 24), (byte)(ipAddress >>> 16), (byte)(ipAddress >>> 8), diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java index 81c3c138f60a59d4eedce92173f2595682347a76..ab09375486b9f61b2cfa98b83a5e9eeb483bd20b 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -29,18 +29,23 @@ public interface IRoutingDecision { /* * NONE: NO-OP, continue with the packet processing chain * DROP: Drop this packet and this flow - * FORWARD: Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(), - * if the destination is not known at this time, initiate a discovery action for it (e.g. ARP) - * FORWARD_OR_FLOOD: Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(), - * if the destination is not known at this time, flood this packet on the source switch - * MULTICAST: Multicast this packet to all the interfaces and devices attached + * FORWARD: Forward this packet, and this flow, to the first + * (and only device) in getDestinationDevices(), + * if the destination is not known at this time, + * initiate a discovery action for it (e.g. ARP) + * FORWARD_OR_FLOOD: Forward this packet, and this flow, to the first + * (and only device) in getDestinationDevices(), + * if the destination is not known at this time, + * flood this packet on the source switch + * MULTICAST: Multicast this packet to all the interfaces + * and devices attached */ NONE, DROP, FORWARD, FORWARD_OR_FLOOD, MULTICAST } - + public static final FloodlightContextStore<IRoutingDecision> rtStore = new FloodlightContextStore<IRoutingDecision>(); - public static final String CONTEXT_DECISION = + public static final String CONTEXT_DECISION = "net.floodlightcontroller.routing.decision"; public void addToContext(FloodlightContext cntx); diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index 84052dbee2baf1136a15c981a2ca839cad80a958..af49de70bbb6ca8f29426bc370d5b343fdc30442 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -36,7 +36,7 @@ import net.floodlightcontroller.routing.BroadcastTree; import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.routing.RouteId; -import net.floodlightcontroller.util.LRUHashMap; +import com.google.common.cache.*; /** * A representation of a network topology. Used internally by @@ -76,7 +76,23 @@ public class TopologyInstance { protected Map<Long, BroadcastTree> destinationRootedTrees; protected Map<Long, Set<NodePortTuple>> clusterBroadcastNodePorts; protected Map<Long, BroadcastTree> clusterBroadcastTrees; - protected LRUHashMap<RouteId, Route> pathcache; + + protected class PathCacheLoader extends CacheLoader<RouteId, Route> { + TopologyInstance ti; + PathCacheLoader(TopologyInstance ti) { + this.ti = ti; + } + + @Override + public Route load(RouteId rid) { + return ti.buildroute(rid); + } + } + + // Path cache loader is defined for loading a path when it not present + // in the cache. + private final PathCacheLoader pathCacheLoader = new PathCacheLoader(this); + protected LoadingCache<RouteId, Route> pathcache; public TopologyInstance() { this.switches = new HashSet<Long>(); @@ -132,7 +148,15 @@ public class TopologyInstance { destinationRootedTrees = new HashMap<Long, BroadcastTree>(); clusterBroadcastTrees = new HashMap<Long, BroadcastTree>(); clusterBroadcastNodePorts = new HashMap<Long, Set<NodePortTuple>>(); - pathcache = new LRUHashMap<RouteId, Route>(PATH_CACHE_SIZE); + + pathcache = CacheBuilder.newBuilder().concurrencyLevel(4) + .maximumSize(1000L) + .build( + new CacheLoader<RouteId, Route>() { + public Route load(RouteId rid) { + return pathCacheLoader.load(rid); + } + }); } public void compute() { @@ -511,7 +535,7 @@ public class TopologyInstance { } protected void calculateShortestPathTreeInClusters() { - pathcache.clear(); + pathcache.invalidateAll(); destinationRootedTrees.clear(); Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); @@ -664,15 +688,23 @@ public class TopologyInstance { return r; } + // NOTE: Return a null route if srcId equals dstId. The null route + // need not be stored in the cache. Moreover, the LoadingCache will + // throw an exception if null route is returned. protected Route getRoute(long srcId, long dstId, long cookie) { + // Return null route if srcId equals dstId + if (srcId == dstId) return null; + + RouteId id = new RouteId(srcId, dstId); Route result = null; - if (pathcache.containsKey(id)) { + + try { result = pathcache.get(id); - } else { - result = buildroute(id); - pathcache.put(id, result); + } catch (Exception e) { + log.error("{}", e); } + if (log.isTraceEnabled()) { log.trace("getRoute: {} -> {}", id, result); } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index cd445b9cec59234775a48d1e32e1376c131aa3c8..e0e39729874fa23024b89050c670ed7b68ef8825 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -44,6 +44,9 @@ import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.SingletonTask; import net.floodlightcontroller.counter.ICounterStoreService; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.NullDebugCounter; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.packet.BSN; @@ -111,6 +114,7 @@ public class TopologyManager implements protected IThreadPoolService threadPool; protected IFloodlightProviderService floodlightProvider; protected IRestApiService restApi; + protected IDebugCounterService debugCounters; // Modules that listen to our updates protected ArrayList<ITopologyListener> topologyAware; @@ -250,7 +254,7 @@ public class TopologyManager implements public boolean isAttachmentPointPort(long switchid, short port, boolean tunnelEnabled) { - // If the switch port is a tunnel endpoint, it is not + // If the switch port is 'tun-bsn' port, it is not // an attachment point port, irrespective of whether // a link is found through it or not. if (linkDiscovery.isTunnelPort(switchid, port)) @@ -645,6 +649,7 @@ public class TopologyManager implements FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: + debugCounters.updateCounter("topology-incoming"); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: @@ -735,6 +740,7 @@ public class TopologyManager implements floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); restApi = context.getServiceImpl(IRestApiService.class); + debugCounters = context.getServiceImpl(IDebugCounterService.class); switchPorts = new HashMap<Long,Set<Short>>(); switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); @@ -760,6 +766,17 @@ public class TopologyManager implements floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); floodlightProvider.addHAListener(this); addRestletRoutable(); + registerTopologyDebugCounters(); + } + + private void registerTopologyDebugCounters() { + if (debugCounters == null) { + log.error("Debug Counter Service not found."); + debugCounters = new NullDebugCounter(); + return; + } + debugCounters.registerCounter(getName() + "-" + "incoming", + "All incoming packets seen by this module", CounterType.ALWAYS_COUNT); } protected void addRestletRoutable() { @@ -951,7 +968,7 @@ public class TopologyManager implements IFloodlightProviderService.bcStore. get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - if (eth.getEtherType() == Ethernet.TYPE_BSN) { + if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java index 5de554f273b5badb6137fe8e5221326e37b2090a..75b0b00d3a9fb082e14a18e3da34712fcdee4e36 100644 --- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java +++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java @@ -134,7 +134,7 @@ public class OFMessageDamper { FloodlightContext cntx, boolean flush) throws IOException { if (! msgTypesToCache.contains(msg.getType())) { - sw.write(msg, cntx); + sw.writeThrottled(msg, cntx); if (flush) { sw.flush(); } @@ -146,7 +146,7 @@ public class OFMessageDamper { // entry exists in cache. Dampening. return false; } else { - sw.write(msg, cntx); + sw.writeThrottled(msg, cntx); if (flush) { sw.flush(); } diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java b/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java index 4c5ddaf6b14aa2eb8456299674930c0953283ef9..746490bc6b2897a560c8d654f20c4cd8e93ce115 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java @@ -70,6 +70,7 @@ public class NetworkResource extends ServerResource { else if (n.equals("network")) { while (jp.nextToken() != JsonToken.END_OBJECT) { String field = jp.getCurrentName(); + if (field == null) continue; if (field.equals("name")) { network.name = jp.getText(); } else if (field.equals("gateway")) { diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java b/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java index 66d560e7f9738d1a89943578bd99bcfe60fb5112..9533056297b1b5ac0d10120040b27356b3808283 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java @@ -17,21 +17,24 @@ package net.floodlightcontroller.virtualnetwork; import org.restlet.data.Status; +import org.restlet.resource.Delete; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.Put; import org.restlet.resource.ServerResource; public class NoOp extends ServerResource { - /** - * Does nothing and returns 200 OK with a status message - * @return status: ok - */ - @Get - @Put - @Post - public String noOp(String postdata) { - setStatus(Status.SUCCESS_OK); + /** + * Does nothing and returns 200 OK with a status message + * + * @return status: ok + */ + @Get + @Put + @Post + @Delete + public String noOp(String postdata) { + setStatus(Status.SUCCESS_OK); return "{\"status\":\"ok\"}"; - } + } } diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java index 0d5d6a5ba5e03e9ee4ecc18308c29e04cb85090e..8e857095004242997ff5ba12b33f1bdb7446f808 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java @@ -37,6 +37,6 @@ public class VirtualNetworkWebRoutable implements RestletRoutable { @Override public String basePath() { - return "/quantum/v1.0"; + return "/networkService/v1.1"; } } \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/OFFlowRemoved.java b/src/main/java/org/openflow/protocol/OFFlowRemoved.java index 4f4e0c7ff92acbd9a716c5301bdcd93a06335ae4..33570063c73850cf995606890ac0c3e9e97f67f5 100644 --- a/src/main/java/org/openflow/protocol/OFFlowRemoved.java +++ b/src/main/java/org/openflow/protocol/OFFlowRemoved.java @@ -194,7 +194,11 @@ public class OFFlowRemoved extends OFMessage { this.match.readFrom(data); this.cookie = data.readLong(); this.priority = data.readShort(); - this.reason = OFFlowRemovedReason.values()[(0xff & data.readByte())]; + int reasonIndex = (int)(0xff & data.readByte()); + if (reasonIndex >= OFFlowRemovedReason.values().length) { + reasonIndex = OFFlowRemovedReason.values().length - 1; + } + this.reason = OFFlowRemovedReason.values()[reasonIndex]; data.readByte(); // pad this.durationSeconds = data.readInt(); this.durationNanoseconds = data.readInt(); diff --git a/src/main/java/org/openflow/protocol/OFPhysicalPort.java b/src/main/java/org/openflow/protocol/OFPhysicalPort.java index 58fdae59f56de9df28358e65f1b9cab038ab676e..c8c9a6b9e3a05801b14995b566bc618977e4716d 100644 --- a/src/main/java/org/openflow/protocol/OFPhysicalPort.java +++ b/src/main/java/org/openflow/protocol/OFPhysicalPort.java @@ -26,14 +26,15 @@ import net.floodlightcontroller.core.web.serializers.UShortSerializer; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.HexString; /** * Represents ofp_phy_port * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 25, 2010 */ public class OFPhysicalPort { - public static int MINIMUM_LENGTH = 48; - public static int OFP_ETH_ALEN = 6; + public final static int MINIMUM_LENGTH = 48; + public final static int OFP_ETH_ALEN = 6; public enum OFPortConfig { OFPPC_PORT_DOWN (1 << 0) { @@ -467,4 +468,35 @@ public class OFPhysicalPort { } return true; } + + @Override + public String toString() { + String linkState, linkSpeed; + if ((state & OFPortState.OFPPS_LINK_DOWN.getValue()) != 0) { + linkState = "down"; + } else { + linkState = "up"; + } + if ((currentFeatures & OFPortFeatures.OFPPF_10GB_FD.getValue()) != 0) { + linkSpeed = "10G"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_1GB_FD.getValue()) != 0) { + linkSpeed = "1G"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_1GB_HD.getValue()) != 0) { + linkSpeed = "1G(half-duplex)"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_100MB_FD.getValue()) != 0) { + linkSpeed = "100M"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_100MB_HD.getValue()) != 0) { + linkSpeed = "100M(half-duplex)"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_10MB_FD.getValue()) != 0) { + linkSpeed = "10M"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_10MB_HD.getValue()) != 0) { + linkSpeed = "10M(half-duplex)"; + } else { + linkSpeed = "unknown"; + } + return "port " + name + " (" + portNumber + ")" + + ", mac " + HexString.toHexString(hardwareAddress) + + ", state " + linkState + + ", speed " + linkSpeed; + } } 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 935fe105c9f6a40b0415d21ef18816bc084042bf..dc38c05b97eed015c76e1f6ea56ae3b1099130be 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 @@ -16,6 +16,8 @@ net.floodlightcontroller.hub.Hub net.floodlightcontroller.jython.JythonDebugInterface net.floodlightcontroller.counter.CounterStore net.floodlightcontroller.counter.NullCounterStore +net.floodlightcontroller.debugcounter.DebugCounter +net.floodlightcontroller.debugcounter.NullDebugCounter net.floodlightcontroller.threadpool.ThreadPool net.floodlightcontroller.ui.web.StaticWebRoutable net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 30dfdcaaa06d8e73d98a80a0fc9cb43b827ef347..2d384d3a1b6e39accfcd56839588cbfc1fb9bdbe 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -12,6 +12,7 @@ net.floodlightcontroller.flowcache.FlowCache, \ net.floodlightcontroller.flowcache.FlowReconcileManager, \ net.floodlightcontroller.jython.JythonDebugInterface,\ net.floodlightcontroller.counter.CounterStore,\ +net.floodlightcontroller.debugcounter.DebugCounter,\ net.floodlightcontroller.perfmon.PktInProcessingTime,\ net.floodlightcontroller.ui.web.StaticWebRoutable,\ net.floodlightcontroller.loadbalancer.LoadBalancer diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index 5daf3461bb72bfdc4119754ccd72efb7b71db79a..e21239ab92dfe01a36bcf349a620ae10a97cec56 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -18,9 +18,6 @@ package net.floodlightcontroller.core.internal; import static org.easymock.EasyMock.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -29,6 +26,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.DelayQueue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -50,6 +48,7 @@ import net.floodlightcontroller.core.internal.Controller.SwitchUpdate; import net.floodlightcontroller.core.internal.Controller.SwitchUpdateType; import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState; import net.floodlightcontroller.core.internal.RoleChanger.PendingRoleRequestEntry; +import net.floodlightcontroller.core.internal.RoleChanger.RoleChangeTask; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.core.test.MockThreadPoolService; @@ -95,11 +94,9 @@ import org.openflow.protocol.statistics.OFDescriptionStatistics; import org.openflow.protocol.statistics.OFFlowStatisticsReply; import org.openflow.protocol.statistics.OFStatistics; import org.openflow.protocol.statistics.OFStatisticsType; -import org.openflow.protocol.vendor.OFVendorData; import org.openflow.util.HexString; import org.openflow.vendor.nicira.OFNiciraVendorData; import org.openflow.vendor.nicira.OFRoleReplyVendorData; -import org.openflow.vendor.nicira.OFRoleRequestVendorData; /** * @@ -107,7 +104,7 @@ import org.openflow.vendor.nicira.OFRoleRequestVendorData; */ public class ControllerTest extends FloodlightTestCase implements IOFSwitchDriver { - + private Controller controller; private MockThreadPoolService tp; private boolean test_bind_order = false; @@ -117,26 +114,26 @@ public class ControllerTest extends FloodlightTestCase public void setUp() throws Exception { super.setUp(); FloodlightModuleContext fmc = new FloodlightModuleContext(); - + FloodlightProvider cm = new FloodlightProvider(); controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class); fmc.addService(IFloodlightProviderService.class, controller); - + MemoryStorageSource memstorage = new MemoryStorageSource(); fmc.addService(IStorageSourceService.class, memstorage); - + RestApiServer restApi = new RestApiServer(); fmc.addService(IRestApiService.class, restApi); - + CounterStore cs = new CounterStore(); fmc.addService(ICounterStoreService.class, cs); - + PktInProcessingTime ppt = new PktInProcessingTime(); fmc.addService(IPktInProcessingTimeService.class, ppt); - + tp = new MockThreadPoolService(); fmc.addService(IThreadPoolService.class, tp); - + ppt.init(fmc); restApi.init(fmc); memstorage.init(fmc); @@ -167,15 +164,15 @@ public class ControllerTest extends FloodlightTestCase sr.setFlags((short) 1); return sr; } - + /* Set the mock expectations for sw when sw is passed to addSwitch */ protected void setupSwitchForAddSwitch(IOFSwitch sw, long dpid) { String dpidString = HexString.toHexString(dpid); - + expect(sw.getId()).andReturn(dpid).anyTimes(); expect(sw.getStringId()).andReturn(dpidString).anyTimes(); } - + /** * Run the controller's main loop so that updates are processed */ @@ -219,7 +216,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -250,7 +247,7 @@ public class ControllerTest extends FloodlightTestCase // verify STOP works reset(test1, test2, sw); - expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP); + expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP); expect(sw.getId()).andReturn(0L).anyTimes(); expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); replay(test1, test2, sw); @@ -291,7 +288,7 @@ public class ControllerTest extends FloodlightTestCase } /** - * + * * @throws Exception */ @Test @@ -379,7 +376,7 @@ public class ControllerTest extends FloodlightTestCase fmCntx.addService(IFloodlightProviderService.class, mfp); fmCntx.addService(IThreadPoolService.class, mtp); String sid = null; - + mfm.init(fmCntx); mfm.startUp(fmCntx); @@ -402,14 +399,14 @@ public class ControllerTest extends FloodlightTestCase assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()); // Deleting the filter works. - mfm.setupFilter(sid, null, -1); + mfm.setupFilter(sid, null, -1); assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()-1); - // Creating mock switch to which we will send packet out and + // Creating mock switch to which we will send packet out and IOFSwitch sw = createMock(IOFSwitch.class); expect(sw.getId()).andReturn(new Long(0)); - // Mock Packet-in + // Mock Packet-in IPacket testPacket = new Ethernet() .setSourceMACAddress("00:44:33:22:11:00") .setDestinationMACAddress("00:11:22:33:44:55") @@ -427,7 +424,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -452,7 +449,7 @@ public class ControllerTest extends FloodlightTestCase // Let's check the listeners. - List <IOFMessageListener> lm; + List <IOFMessageListener> lm; // Check to see if all the listeners are active. lm = mfp.getListeners().get(OFType.PACKET_OUT); @@ -467,7 +464,7 @@ public class ControllerTest extends FloodlightTestCase assertTrue(lm.size() == 1); assertTrue(lm.get(0).equals(mfm)); - HashSet<String> matchedFilters; + HashSet<String> matchedFilters; // Send a packet in and check if it matches a filter. matchedFilters = mfm.getMatchedFilters(pi, cntx); @@ -501,14 +498,14 @@ public class ControllerTest extends FloodlightTestCase expect(newsw.getId()).andReturn(0L).anyTimes(); expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); controller.activeSwitches.put(0L, oldsw); - + replay(newsw, channel); controller.addSwitch(newsw, false); verify(newsw, channel); } - + @Test public void testAddSwitchClearFM() throws Exception { controller.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); @@ -530,14 +527,14 @@ public class ControllerTest extends FloodlightTestCase newsw.clearAllFlowMods(); expectLastCall().once(); controller.activeSwitches.put(0L, oldsw); - + replay(newsw, channel); controller.addSwitch(newsw, true); verify(newsw, channel); } - + @Test public void testUpdateQueue() throws Exception { class DummySwitchListener implements IOFSwitchListener { @@ -573,29 +570,29 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw = createMock(IOFSwitch.class); ControllerRunThread t = new ControllerRunThread(); t.start(); - + controller.addOFSwitchListener(switchListener); synchronized(switchListener) { controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.ADDED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.addedSwitch() was not called", + assertTrue("IOFSwitchListener.addedSwitch() was not called", switchListener.nAdded == 1); - controller.updates.put(controller.new SwitchUpdate(sw, + controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.REMOVED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.removedSwitch() was not called", + assertTrue("IOFSwitchListener.removedSwitch() was not called", switchListener.nRemoved == 1); - controller.updates.put(controller.new SwitchUpdate(sw, + controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.PORTCHANGED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.switchPortChanged() was not called", + assertTrue("IOFSwitchListener.switchPortChanged() was not called", switchListener.nPortChanged == 1); } } - - private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, + + private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, String type, int number, String discoveredIP ) { HashMap<String, Object> row = new HashMap<String,Object>(); row.put(Controller.CONTROLLER_INTERFACE_ID, id); @@ -608,14 +605,14 @@ public class ControllerTest extends FloodlightTestCase /** * Test notifications for controller node IP changes. This requires - * synchronization between the main test thread and another thread + * synchronization between the main test thread and another thread * that runs Controller's main loop and takes / handles updates. We - * synchronize with wait(timeout) / notifyAll(). We check for the + * synchronize with wait(timeout) / notifyAll(). We check for the * expected condition after the wait returns. However, if wait returns * due to the timeout (or due to spurious awaking) and the check fails we * might just not have waited long enough. Using a long enough timeout - * mitigates this but we cannot get rid of the fundamental "issue". - * + * mitigates this but we cannot get rid of the fundamental "issue". + * * @throws Exception */ @Test @@ -625,16 +622,16 @@ public class ControllerTest extends FloodlightTestCase public Map<String, String> addedControllerNodeIPs; public Map<String, String> removedControllerNodeIPs; public int nCalled; - + public DummyHAListener() { this.nCalled = 0; } - + @Override public void roleChanged(Role oldRole, Role newRole) { // ignore } - + @Override public synchronized void controllerNodeIPsChanged( Map<String, String> curControllerNodeIPs, @@ -646,19 +643,19 @@ public class ControllerTest extends FloodlightTestCase this.nCalled++; notifyAll(); } - + public void do_assert(int nCalled, Map<String, String> curControllerNodeIPs, Map<String, String> addedControllerNodeIPs, Map<String, String> removedControllerNodeIPs) { assertEquals("nCalled is not as expected", nCalled, this.nCalled); - assertEquals("curControllerNodeIPs is not as expected", + assertEquals("curControllerNodeIPs is not as expected", curControllerNodeIPs, this.curControllerNodeIPs); - assertEquals("addedControllerNodeIPs is not as expected", + assertEquals("addedControllerNodeIPs is not as expected", addedControllerNodeIPs, this.addedControllerNodeIPs); - assertEquals("removedControllerNodeIPs is not as expected", + assertEquals("removedControllerNodeIPs is not as expected", removedControllerNodeIPs, this.removedControllerNodeIPs); - + } } long waitTimeout = 250; // ms @@ -666,11 +663,11 @@ public class ControllerTest extends FloodlightTestCase HashMap<String,String> expectedCurMap = new HashMap<String, String>(); HashMap<String,String> expectedAddedMap = new HashMap<String, String>(); HashMap<String,String> expectedRemovedMap = new HashMap<String, String>(); - + controller.addHAListener(listener); ControllerRunThread t = new ControllerRunThread(); t.start(); - + synchronized(listener) { // Insert a first entry controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, @@ -682,12 +679,12 @@ public class ControllerTest extends FloodlightTestCase expectedAddedMap.put("c1", "1.1.1.1"); listener.wait(waitTimeout); listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap); - - // Add an interface that we want to ignore. + + // Add an interface that we want to ignore. controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2")); listener.wait(waitTimeout); // TODO: do a different check. This call will have to wait for the timeout - assertTrue("controllerNodeIPsChanged() should not have been called here", + assertTrue("controllerNodeIPsChanged() should not have been called here", listener.nCalled == 1); // Add another entry @@ -717,7 +714,7 @@ public class ControllerTest extends FloodlightTestCase listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap); // Delete an entry - controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, + controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3"); expectedCurMap.clear(); expectedAddedMap.clear(); @@ -728,11 +725,11 @@ public class ControllerTest extends FloodlightTestCase listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap); } } - + @Test public void testGetControllerNodeIPs() { HashMap<String,String> expectedCurMap = new HashMap<String, String>(); - + controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1")); controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, @@ -740,11 +737,11 @@ public class ControllerTest extends FloodlightTestCase controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2")); expectedCurMap.put("c1", "1.1.1.1"); - expectedCurMap.put("c2", "2.2.2.2"); - assertEquals("expectedControllerNodeIPs is not as expected", + expectedCurMap.put("c2", "2.2.2.2"); + assertEquals("expectedControllerNodeIPs is not as expected", expectedCurMap, controller.getControllerNodeIPs()); } - + @Test public void testSetRoleNull() { try { @@ -755,31 +752,31 @@ public class ControllerTest extends FloodlightTestCase //exptected } } - - @Test + + @Test public void testSetRole() { controller.connectedSwitches.add(new OFSwitchImpl()); - RoleChanger roleChanger = createMock(RoleChanger.class); + RoleChanger roleChanger = createMock(RoleChanger.class); roleChanger.submitRequest(controller.connectedSwitches, Role.SLAVE); controller.roleChanger = roleChanger; - - assertEquals("Check that update queue is empty", 0, + + assertEquals("Check that update queue is empty", 0, controller.updates.size()); - + replay(roleChanger); controller.setRole(Role.SLAVE, "Testing"); controller.doSetRole(); // avoid wait verify(roleChanger); - + Controller.IUpdate upd = controller.updates.poll(); assertNotNull("Check that update queue has an update", upd); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); Controller.HARoleUpdate roleUpd = (Controller.HARoleUpdate)upd; assertSame(Role.MASTER, roleUpd.oldRole); assertSame(Role.SLAVE, roleUpd.newRole); } - + @SuppressWarnings("unchecked") @Test public void testCheckSwitchReady() { @@ -790,8 +787,8 @@ public class ControllerTest extends FloodlightTestCase OFDescriptionStatistics desc = new OFDescriptionStatistics(); OFFeaturesReply featuresReply = new OFFeaturesReply(); featuresReply.setPorts(new ArrayList<OFPhysicalPort>()); - - // Wrong current state + + // Wrong current state // Should not go to READY state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = false; @@ -802,7 +799,7 @@ public class ControllerTest extends FloodlightTestCase replay(channel); chdlr.checkSwitchReady(); assertSame(OFChannelState.HandshakeState.HELLO, state.hsState); - + // Have only config reply state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; state.hasDescription = false; @@ -813,7 +810,7 @@ public class ControllerTest extends FloodlightTestCase assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState); assertTrue(controller.connectedSwitches.isEmpty()); assertTrue(controller.activeSwitches.isEmpty()); - + // Have only desc reply state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; state.hasDescription = true; @@ -824,7 +821,7 @@ public class ControllerTest extends FloodlightTestCase assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState); assertTrue(controller.connectedSwitches.isEmpty()); assertTrue(controller.activeSwitches.isEmpty()); - + ////////////////////////////////////////// // Finally, everything is right. Should advance to READY ////////////////////////////////////////// @@ -836,7 +833,7 @@ public class ControllerTest extends FloodlightTestCase state.featuresReply = featuresReply; state.switchBindingDone = false; // Role support disabled. Switch should be promoted to active switch - // list. + // list. // setupSwitchForAddSwitch(chdlr.sw, 0L); // chdlr.sw.clearAllFlowMods(); desc.setManufacturerDescription("test vendor"); @@ -850,14 +847,14 @@ public class ControllerTest extends FloodlightTestCase reset(controller.roleChanger); controller.connectedSwitches.clear(); controller.activeSwitches.clear(); - - - // Role support enabled. + + + // Role support enabled. state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; controller.role = Role.MASTER; - Capture<Collection<IOFSwitch>> swListCapture = + Capture<Collection<IOFSwitch>> swListCapture = new Capture<Collection<IOFSwitch>>(); - controller.roleChanger.submitRequest(capture(swListCapture), + controller.roleChanger.submitRequest(capture(swListCapture), same(Role.MASTER)); replay(controller.roleChanger); chdlr.checkSwitchReady(); @@ -868,7 +865,7 @@ public class ControllerTest extends FloodlightTestCase Collection<IOFSwitch> swList = swListCapture.getValue(); assertEquals(1, swList.size()); } - + public class TestSwitchClass extends OFSwitchImpl { } @@ -878,11 +875,11 @@ public class ControllerTest extends FloodlightTestCase @Test public void testBindSwitchToDriver() { controller.addOFSwitchDriver("test", this); - + OFChannelState state = new OFChannelState(); Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); - + // Swith should be bound of OFSwitchImpl (default) state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = true; @@ -899,7 +896,7 @@ public class ControllerTest extends FloodlightTestCase chdlr.bindSwitchToDriver(); assertTrue(chdlr.sw instanceof OFSwitchImpl); assertTrue(!(chdlr.sw instanceof TestSwitchClass)); - + // Switch should be bound to TestSwitchImpl state.switchBindingDone = false; desc.setManufacturerDescription("test1 switch"); @@ -920,7 +917,7 @@ public class ControllerTest extends FloodlightTestCase chdlr.bindSwitchToDriver(); assertTrue(chdlr.sw instanceof Test11SwitchClass); } - + @Test public void testBindSwitchOrder() { List<String> order = new ArrayList<String>(3); @@ -931,12 +928,12 @@ public class ControllerTest extends FloodlightTestCase order.add("test"); order.add(""); test_bind_order = true; - + OFChannelState state = new OFChannelState(); Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = null; - + // Swith should be bound of OFSwitchImpl (default) state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = true; @@ -958,29 +955,29 @@ public class ControllerTest extends FloodlightTestCase test_bind_order = false; bind_order = null; } - + @Test public void testChannelDisconnected() throws Exception { OFChannelState state = new OFChannelState(); state.hsState = OFChannelState.HandshakeState.READY; Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = createMock(IOFSwitch.class); - - // Switch is active + + // Switch is active expect(chdlr.sw.getId()).andReturn(0L).anyTimes(); expect(chdlr.sw.getStringId()).andReturn("00:00:00:00:00:00:00:00") .anyTimes(); chdlr.sw.cancelAllStatisticsReplies(); chdlr.sw.setConnected(false); expect(chdlr.sw.isConnected()).andReturn(true); - + controller.connectedSwitches.add(chdlr.sw); controller.activeSwitches.put(0L, chdlr.sw); - + replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Switch is connected but not active reset(chdlr.sw); expect(chdlr.sw.getId()).andReturn(0L).anyTimes(); @@ -988,20 +985,20 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Not in ready state state.hsState = HandshakeState.START; reset(chdlr.sw); replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Switch is null state.hsState = HandshakeState.READY; chdlr.sw = null; chdlr.channelDisconnected(null, null); } - + /* @Test public void testRoleChangeForSerialFailoverSwitch() throws Exception { @@ -1010,12 +1007,12 @@ public class ControllerTest extends FloodlightTestCase expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); Channel channel2 = createMock(Channel.class); expect(newsw.getChannel()).andReturn(channel2); - + // newsw.role is null because the switch does not support // role request messages expect(newsw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) .andReturn(false); - // switch is connected + // switch is connected controller.connectedSwitches.add(newsw); // the switch should get disconnected when role is changed to SLAVE @@ -1035,40 +1032,40 @@ public class ControllerTest extends FloodlightTestCase Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = createMock(IOFSwitch.class); Channel ch = createMock(Channel.class); - + // the error returned when role request message is not supported by sw OFError msg = new OFError(); msg.setType(OFType.ERROR); msg.setXid(xid); msg.setErrorType(OFErrorType.OFPET_BAD_REQUEST); - + // the switch connection should get disconnected when the controller is // in SLAVE mode and the switch does not support role-request messages controller.role = Role.SLAVE; - setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); + setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, false); expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE); chdlr.sw.disconnectOutputStream(); - + replay(ch, chdlr.sw); chdlr.processOFMessage(msg); verify(ch, chdlr.sw); assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); reset(ch, chdlr.sw); - + // We are MASTER, the switch should be added to the list of active // switches. controller.role = Role.MASTER; - setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); + setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(controller.role, false); setupSwitchForAddSwitch(chdlr.sw, 0L); chdlr.sw.clearAllFlowMods(); expect(chdlr.sw.getHARole()).andReturn(null).anyTimes(); replay(ch, chdlr.sw); - + chdlr.processOFMessage(msg); verify(ch, chdlr.sw); assertSame("activeSwitches must contain this switch", @@ -1076,12 +1073,12 @@ public class ControllerTest extends FloodlightTestCase reset(ch, chdlr.sw); } - - - @Test + + + @Test public void testVendorMessageUnknown() throws Exception { // Check behavior with an unknown vendor id - // Ensure that vendor message listeners get called, even for Vendors + // Ensure that vendor message listeners get called, even for Vendors // unknown to floodlight. It is the responsibility of the listener to // discard unknown vendors. OFChannelState state = new OFChannelState(); @@ -1092,15 +1089,15 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw = createMock(IOFSwitch.class); chdlr.sw = sw; controller.activeSwitches.put(1L, sw); - + // prepare the Vendor Message Listener expectations - ListenerDispatcher<OFType, IOFMessageListener> ld = + ListenerDispatcher<OFType, IOFMessageListener> ld = new ListenerDispatcher<OFType, IOFMessageListener>(); IOFMessageListener ml = createMock(IOFMessageListener.class); expect(ml.getName()).andReturn("Dummy").anyTimes(); - expect(ml.isCallbackOrderingPrereq((OFType)anyObject(), + expect(ml.isCallbackOrderingPrereq((OFType)anyObject(), (String)anyObject())).andReturn(false).anyTimes(); - expect(ml.isCallbackOrderingPostreq((OFType)anyObject(), + expect(ml.isCallbackOrderingPostreq((OFType)anyObject(), (String)anyObject())).andReturn(false).anyTimes(); expect(ml.receive(eq(sw), eq(msg), isA(FloodlightContext.class))). andReturn(Command.CONTINUE).once(); @@ -1112,7 +1109,7 @@ public class ControllerTest extends FloodlightTestCase expect(sw.isConnected()).andReturn(true).anyTimes(); expect(sw.getHARole()).andReturn(Role.MASTER).anyTimes(); expect(sw.getId()).andReturn(1L).anyTimes(); - + // test replay(chdlr.sw, lock, ml); ld.addListener(OFType.VENDOR, ml); @@ -1143,12 +1140,23 @@ public class ControllerTest extends FloodlightTestCase // Make sure controller attempts to reset switch master expect(sw.getAttribute("supportsNxRole")).andReturn(true).anyTimes(); expect(sw.getNextTransactionId()).andReturn(0).anyTimes(); + sw.write(EasyMock.<List<OFMessage>> anyObject(), + (FloodlightContext)anyObject()); // test replay(sw, lock); chdlr.processOFMessage(error); - // Verify there is a pending role change request - assertTrue(controller.roleChanger.pendingTasks.poll() != null); + DelayQueue<RoleChangeTask> pendingTasks = + controller.roleChanger.pendingTasks; + synchronized (pendingTasks) { + RoleChangeTask t; + while ((t = pendingTasks.peek()) == null || + RoleChanger.RoleChangeTask.Type.TIMEOUT != t.type) { + pendingTasks.wait(); + } + } + // Now there should be exactly one timeout task pending + assertEquals(1, pendingTasks.size()); } // Helper function. @@ -1159,19 +1167,19 @@ public class ControllerTest extends FloodlightTestCase chdlr.sw = createMock(IOFSwitch.class); return chdlr; } - + // Helper function protected OFVendor getRoleReplyMsgForRoleReplyTest(int xid, int nicira_role) { OFVendor msg = new OFVendor(); msg.setXid(xid); msg.setVendor(OFNiciraVendorData.NX_VENDOR_ID); - OFRoleReplyVendorData roleReplyVendorData = + OFRoleReplyVendorData roleReplyVendorData = new OFRoleReplyVendorData(OFRoleReplyVendorData.NXT_ROLE_REPLY); msg.setVendorData(roleReplyVendorData); roleReplyVendorData.setRole(nicira_role); return msg; } - + // Helper function protected void setupPendingRoleRequest(IOFSwitch sw, int xid, Role role, long cookie) { @@ -1182,10 +1190,10 @@ public class ControllerTest extends FloodlightTestCase new PendingRoleRequestEntry(xid, role, cookie); pendingList.add(entry); } - + /** invalid role in role reply */ - @Test - public void testNiciraRoleReplyInvalidRole() + @Test + public void testNiciraRoleReplyInvalidRole() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); @@ -1196,17 +1204,17 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(msg); verify(chdlr.sw, ch); } - + /** First role reply message received: transition from slave to master */ - @Test - public void testNiciraRoleReplySlave2MasterFristTime() + @Test + public void testNiciraRoleReplySlave2MasterFristTime() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.MASTER, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1217,18 +1225,18 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - - + + /** Not first role reply message received: transition from slave to master */ - @Test - public void testNiciraRoleReplySlave2MasterNotFristTime() + @Test + public void testNiciraRoleReplySlave2MasterNotFristTime() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE); chdlr.sw.setHARole(Role.MASTER, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1239,17 +1247,17 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - + /** transition from slave to equal */ - @Test - public void testNiciraRoleReplySlave2Equal() + @Test + public void testNiciraRoleReplySlave2Equal() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_OTHER); - - setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.EQUAL, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1260,16 +1268,16 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); }; - + @Test /** Slave2Slave transition ==> no change */ public void testNiciraRoleReplySlave2Slave() throws Exception{ int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_SLAVE); - - setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1279,19 +1287,19 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.processOFMessage(msg); verify(chdlr.sw); - assertTrue("activeSwitches must be empty", + assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); } - + @Test /** Equal2Master transition ==> no change */ public void testNiciraRoleReplyEqual2Master() throws Exception{ int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.MASTER, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1305,16 +1313,16 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - - @Test - public void testNiciraRoleReplyMaster2Slave() + + @Test + public void testNiciraRoleReplyMaster2Slave() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_SLAVE); - - setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1327,10 +1335,10 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.processOFMessage(msg); verify(chdlr.sw); - assertTrue("activeSwitches must be empty", + assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); } - + /** * Tests that you can't remove a switch from the active * switch list. @@ -1351,7 +1359,7 @@ public class ControllerTest extends FloodlightTestCase assertTrue(exceptionThrown); verify(sw); } - + public void verifyPortChangedUpdateInQueue(IOFSwitch sw) throws Exception { assertEquals(1, controller.updates.size()); IUpdate update = controller.updates.take(); @@ -1360,21 +1368,21 @@ public class ControllerTest extends FloodlightTestCase assertEquals(sw, swUpdate.sw); assertEquals(SwitchUpdateType.PORTCHANGED, swUpdate.switchUpdateType); } - + /* * Test handlePortStatus() * TODO: test correct updateStorage behavior! */ - @Test + @Test public void testHandlePortStatus() throws Exception { IOFSwitch sw = createMock(IOFSwitch.class); OFPhysicalPort port = new OFPhysicalPort(); port.setName("myPortName1"); port.setPortNumber((short)42); - + OFPortStatus ofps = new OFPortStatus(); ofps.setDesc(port); - + ofps.setReason((byte)OFPortReason.OFPPR_ADD.ordinal()); sw.setPort(port); expectLastCall().once(); @@ -1383,7 +1391,7 @@ public class ControllerTest extends FloodlightTestCase verify(sw); verifyPortChangedUpdateInQueue(sw); reset(sw); - + ofps.setReason((byte)OFPortReason.OFPPR_MODIFY.ordinal()); sw.setPort(port); expectLastCall().once(); @@ -1392,7 +1400,7 @@ public class ControllerTest extends FloodlightTestCase verify(sw); verifyPortChangedUpdateInQueue(sw); reset(sw); - + ofps.setReason((byte)OFPortReason.OFPPR_DELETE.ordinal()); sw.deletePort(port.getPortNumber()); expectLastCall().once(); @@ -1423,8 +1431,8 @@ public class ControllerTest extends FloodlightTestCase } return null; } - - private void setupSwitchForDispatchTest(IOFSwitch sw, + + private void setupSwitchForDispatchTest(IOFSwitch sw, boolean isConnected, Role role) { Lock lock = createNiceMock(Lock.class); @@ -1434,10 +1442,10 @@ public class ControllerTest extends FloodlightTestCase expect(sw.isConnected()).andReturn(isConnected).anyTimes(); expect(sw.getHARole()).andReturn(role).anyTimes(); replay(lock); - + } - - @Test + + @Test public void testMessageDispatch() throws Exception { // Mock a dummy packet in // Build our test packet @@ -1458,7 +1466,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -1466,60 +1474,60 @@ public class ControllerTest extends FloodlightTestCase .setReason(OFPacketInReason.NO_MATCH) .setTotalLength((short) testPacketSerialized.length); - - // Mock switch and add to data structures + + // Mock switch and add to data structures IOFSwitch sw = createMock(IOFSwitch.class); - + controller.connectedSwitches.add(sw); - - // create a channel handler + + // create a channel handler OFChannelState state = new OFChannelState(); state.hsState = HandshakeState.READY; Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = sw; - - // mock role changer - RoleChanger roleChanger = createMock(RoleChanger.class); - roleChanger.submitRequest(eq(controller.connectedSwitches), + + // mock role changer + RoleChanger roleChanger = createMock(RoleChanger.class); + roleChanger.submitRequest(eq(controller.connectedSwitches), anyObject(Role.class)); expectLastCall().anyTimes(); controller.roleChanger = roleChanger; - - - // Mock message listener and add + + + // Mock message listener and add IOFMessageListener listener = createNiceMock(IOFMessageListener.class); expect(listener.getName()).andReturn("foobar").anyTimes(); replay(listener); controller.addOFMessageListener(OFType.PACKET_IN, listener); resetToStrict(listener); - - - assertEquals("Check that update queue is empty", 0, + + + assertEquals("Check that update queue is empty", 0, controller.updates.size()); - - + + replay(roleChanger); - + //------------------- // Test 1: role is master, switch is master and in activeMap - // we expect the msg to be dispatched + // we expect the msg to be dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(sw, listener); chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1b: role is master, switch is master and in activeMap - // but switch is not connected - // no message dispatched + // but switch is not connected + // no message dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); @@ -1528,11 +1536,11 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1c: role is master, switch is slave and in activeMap - // no message dispatched + // no message dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); @@ -1541,11 +1549,11 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1d: role is master, switch is master but not in activeMap - // we expect the msg to be dispatched + // we expect the msg to be dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.remove(1L); @@ -1553,18 +1561,18 @@ public class ControllerTest extends FloodlightTestCase replay(sw, listener); chdlr.processOFMessage(pi); verify(sw, listener); - assertEquals(0, controller.updates.size()); - - - + assertEquals(0, controller.updates.size()); + + + //------------------- // Test 2: check correct dispatch and HA notification behavior - // We set the role to slave but do not notify the clients + // We set the role to slave but do not notify the clients reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(sw, listener); @@ -1573,10 +1581,10 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(1, controller.updates.size()); - - // Now notify listeners + + // Now notify listeners Controller.IUpdate upd = controller.updates.poll(1, TimeUnit.NANOSECONDS); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); upd.dispatch(); resetToDefault(listener); @@ -1584,7 +1592,7 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(listener); assertEquals(0, controller.updates.size()); - + // transition back to master but don't notify yet resetToDefault(listener); replay(listener); @@ -1593,40 +1601,40 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(listener); assertEquals(1, controller.updates.size()); - - // now notify listeners + + // now notify listeners upd = controller.updates.poll(1, TimeUnit.NANOSECONDS); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); upd.dispatch(); resetToDefault(listener); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(listener); chdlr.processOFMessage(pi); verify(listener); assertEquals(0, controller.updates.size()); - + verify(sw); } - - + + /* * Test correct timing behavior between HA Role notification and dispatching - * OFMessages to listeners. + * OFMessages to listeners. * When transitioning to SLAVE: stop dispatching message before sending * notifications * When transitioning to MASTER: start dispatching messages after sending * notifications - * (This implies that messages should not be dispatched while the - * notifications are being sent). - * + * (This implies that messages should not be dispatched while the + * notifications are being sent). + * * We encapsulate the logic for this in a class that implements both * IHAListener and IOFMessageListener. Then we inject an OFMessage fom - * the IHAListener and check that it gets dropped correctly. + * the IHAListener and check that it gets dropped correctly. */ - @Test + @Test public void testRoleNotifcationAndMessageDispatch() throws Exception { class TestRoleNotificationsAndDispatch implements IHAListener, IOFMessageListener { OFPacketIn pi; @@ -1634,7 +1642,7 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw; private boolean haveReceived; private boolean doInjectMessageFromHAListener; - + public TestRoleNotificationsAndDispatch() { IPacket testPacket = new Ethernet() .setSourceMACAddress("00:44:33:22:11:00") @@ -1652,78 +1660,78 @@ public class ControllerTest extends FloodlightTestCase .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - - // Build the PacketIn + + // Build the PacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) .setPacketData(testPacketSerialized) .setReason(OFPacketInReason.NO_MATCH) .setTotalLength((short) testPacketSerialized.length); - - // Mock switch and add to data structures + + // Mock switch and add to data structures sw = createMock(IOFSwitch.class); controller.connectedSwitches.add(sw); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); replay(sw); - - // create a channel handler + + // create a channel handler OFChannelState state = new OFChannelState(); state.hsState = HandshakeState.READY; chdlr = controller.new OFChannelHandler(state); chdlr.sw = this.sw; - + // add ourself as listeners controller.addOFMessageListener(OFType.PACKET_IN, this); controller.addHAListener(this); } - - + + private void injectMessage(boolean shouldReceive) throws Exception { haveReceived = false; chdlr.processOFMessage(pi); assertEquals(shouldReceive, haveReceived); } - + public void transitionToSlave() throws Exception { IUpdate update; - + // Bring controller into well defined state for MASTER - doInjectMessageFromHAListener = false; + doInjectMessageFromHAListener = false; update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE); update.dispatch(); doInjectMessageFromHAListener = true; - - - // inject message. Listener called + + + // inject message. Listener called injectMessage(true); - // Dispatch update + // Dispatch update update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER); update.dispatch(); // inject message. Listener not called injectMessage(false); } - + public void transitionToMaster() throws Exception { IUpdate update; - + // Bring controller into well defined state for SLAVE - doInjectMessageFromHAListener = false; + doInjectMessageFromHAListener = false; update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER); update.dispatch(); doInjectMessageFromHAListener = true; - - - // inject message. Listener not called + + + // inject message. Listener not called injectMessage(false); - // Dispatch update + // Dispatch update update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE); update.dispatch(); // inject message. Listener called injectMessage(true); } - + //--------------- // IHAListener //--------------- @@ -1737,7 +1745,7 @@ public class ControllerTest extends FloodlightTestCase e.printStackTrace(); } } - + @Override public void @@ -1746,7 +1754,7 @@ public class ControllerTest extends FloodlightTestCase Map<String, String> removedControllerNodeIPs) { // TODO Auto-generated method stub } - + //------------------------- // IOFMessageListener //------------------------- @@ -1763,18 +1771,18 @@ public class ControllerTest extends FloodlightTestCase return false; } @Override - public Command receive(IOFSwitch sw, - OFMessage msg, + public Command receive(IOFSwitch sw, + OFMessage msg, FloodlightContext cntx) { haveReceived = true; return Command.STOP; } } - + TestRoleNotificationsAndDispatch x = new TestRoleNotificationsAndDispatch(); x.transitionToSlave(); x.transitionToMaster(); - + } } diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 40986e9f2577af119523534ac3762404bb66c6f0..2efb2cd5c684dd7a18888a9f3f8281bd7f1689aa 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -780,10 +780,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockListener); reset(mockListener); - //mockListener.deviceMoved((isA(IDevice.class))); + mockListener.deviceMoved((isA(IDevice.class))); //mockListener.deviceIPV4AddrChanged((isA(IDevice.class))); replay(mockListener); - // there is no device moved because entity 1 was not learned due to suppression d = deviceManager.learnDeviceByEntity(entity2); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 78fb6112d0d08a927090f6d35cf924a328db8d69..6e894a359cc2da78bbbde3b523cb329e7f552210 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -357,9 +357,9 @@ public class ForwardingTest extends FloodlightTestCase { OFFlowMod fm2 = fm1.clone(); ((OFActionOutput)fm2.getActions().get(0)).setPort((short) 3); - sw1.write(capture(wc1), capture(bc1)); + sw1.writeThrottled(capture(wc1), capture(bc1)); expectLastCall().anyTimes(); - sw2.write(capture(wc2), capture(bc2)); + sw2.writeThrottled(capture(wc2), capture(bc2)); expectLastCall().anyTimes(); reset(topology); @@ -420,8 +420,8 @@ public class ForwardingTest extends FloodlightTestCase { OFActionOutput.MINIMUM_LENGTH); // Record expected packet-outs/flow-mods - sw1.write(fm1, cntx); - sw1.write(packetOut, cntx); + sw1.writeThrottled(fm1, cntx); + sw1.writeThrottled(packetOut, cntx); reset(topology); expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes(); @@ -473,9 +473,9 @@ public class ForwardingTest extends FloodlightTestCase { // Record expected packet-outs/flow-mods // We will inject the packet_in 3 times and expect 1 flow mod and // 3 packet outs due to flow mod dampening - sw1.write(fm1, cntx); + sw1.writeThrottled(fm1, cntx); expectLastCall().once(); - sw1.write(packetOut, cntx); + sw1.writeThrottled(packetOut, cntx); expectLastCall().times(3); reset(topology); @@ -508,7 +508,7 @@ public class ForwardingTest extends FloodlightTestCase { .anyTimes(); expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) .andReturn(true).anyTimes(); - sw1.write(packetOutFlooded, cntx); + sw1.writeThrottled(packetOutFlooded, cntx); expectLastCall().once(); replay(sw1, sw2, routingEngine, topology); forwarding.receive(sw1, this.packetIn, cntx); diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java index 7311ac1aadd75dd13a19637f97a7edf7ff0e552b..2aeefa45658a1d595be5f14247bac1569fe14efe 100644 --- a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java +++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java @@ -433,7 +433,7 @@ public class LoadBalancerTest extends FloodlightTestCase { expect(sw1.getStringId()).andReturn("00:00:00:00:00:01").anyTimes(); expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes(); expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); - sw1.write(capture(wc1), capture(bc1)); + sw1.writeThrottled(capture(wc1), capture(bc1)); expectLastCall().anyTimes(); sw1.flush(); expectLastCall().anyTimes(); diff --git a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java index acea62d25f195b35f012b8714e70983adf84e8af..c62b77602b2124eb9305162ba9e0e7ccd48df6cf 100644 --- a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java +++ b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java @@ -107,6 +107,40 @@ public class IPv4Test { IPv4 packet = new IPv4(); packet.deserialize(pktSerializedPadded, 0, pktSerializedPadded.length); byte[] pktSerialized1 = packet.serialize(); + // Padding is ignored assertTrue(Arrays.equals(pktSerialized, pktSerialized1)); } + + @Test + public void testDeserializeFragment() { + // IP packet with more frag bit set + byte[] pktSerializedFirstFrag = new byte[] { 0x45, 0x00, + 0x00, 0x2e, 0x41, (byte) 0xbe, 0x20, 0x00, 0x40, 0x06, + (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55, + (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50, + (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18, + (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, + }; + IPv4 packet1 = new IPv4(); + packet1.deserialize(pktSerializedFirstFrag, 0, pktSerializedFirstFrag.length); + byte[] pktSerialized1 = packet1.serialize(); + assertTrue(packet1.isFragment()); + assertTrue(Arrays.equals(pktSerializedFirstFrag, pktSerialized1)); + + // IP packet with offset + byte[] pktSerializedLastFrag = new byte[] { 0x45, 0x00, + 0x00, 0x2e, 0x41, (byte) 0xbe, 0x01, 0x00, 0x40, 0x06, + (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55, + (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50, + (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18, + (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, + }; + IPv4 packet2 = new IPv4(); + packet2.deserialize(pktSerializedLastFrag, 0, pktSerializedLastFrag.length); + byte[] pktSerialized2 = packet2.serialize(); + assertTrue(packet2.isFragment()); + assertTrue(Arrays.equals(pktSerializedLastFrag, pktSerialized2)); + } } diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index 5f09dc7dc8d00471f00a8ce1e2f13da01079e568..29191365cddad3344d7390f4d363cfcd0eaf8820 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -102,9 +102,22 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { writtenContext = bc; writtenMessage = m; } + + @Override + public void writeThrottled(OFMessage msg, FloodlightContext cntx) + throws IOException { + write(msg, cntx); + } //------------------------------------------------------- // IOFSwitch: not-implemented methods + + @Override + public void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) + throws IOException { + assertTrue("Unexpected method call", false); + } + @Override public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException { @@ -420,4 +433,4 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { return false; } -} \ No newline at end of file +}