diff --git a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar index 9e685f2be1d758a4564fba5acf01bdbb4b237dac..0fa8cd6dac7eeb914fe77a9b0a89ea9145a3ee20 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar and b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar differ diff --git a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar index bd732775843be73ff338598b296ecfb9510e40b1..b8ab5fcc55fca207c5bf2eb42f7267995f059836 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar and b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar differ diff --git a/lib/openflowj-0.9.0-SNAPSHOT.jar b/lib/openflowj-0.9.0-SNAPSHOT.jar index bef1d780013e3943927c357a4adb2173215e4753..2bd4f87c31d76c74891ec7f3c10d5e37d624da1e 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT.jar and b/lib/openflowj-0.9.0-SNAPSHOT.jar differ diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index df2856c64f84834ee1737d93616cfe63298546d0..ccb68176f6086b187071cb4fc9fbf01e9e3df40d 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -34,10 +34,14 @@ import org.projectfloodlight.openflow.protocol.OFStatsReply; import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TableId; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; + +import net.floodlightcontroller.core.internal.TableFeatures; import net.floodlightcontroller.core.web.serializers.IOFSwitchSerializer; + import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * An openflow switch connecting to the controller. This interface offers @@ -336,4 +340,15 @@ public interface IOFSwitch extends IOFMessageWriter { * return a Future that immediately fails with a @link{SwitchDisconnectedException}. */ <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request, LogicalOFMessageCategory category); + + /** + * Get the features of a particular switch table. The features are cached from + * the initial handshake, or, if applicable, from a more recent + * OFTableFeaturesStatsRequest/Reply sent by a user module. + * + * @param table, The table of which to get features. + * @return The table features or null if no features are known for the table requested. + */ + public TableFeatures getTableFeatures(TableId table); + } diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java index 75685c19a480c0bb4cb90bef48caa65d46023223..a14ca20199c549213544d85c73a4b7ade72058ed 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java @@ -18,6 +18,7 @@ package net.floodlightcontroller.core; import java.util.Collection; +import java.util.List; import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsReply; import org.projectfloodlight.openflow.protocol.OFControllerRole; @@ -26,6 +27,8 @@ import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFTableFeatures; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; import net.floodlightcontroller.util.OrderedCollection; @@ -82,6 +85,15 @@ public interface IOFSwitchBackend extends IOFSwitch { * conflicting appear before before events adding new ports */ OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps); + + /** + * Add or modify a switch table. + * This is called by the core controller code in response to an OFTableFeaturesReply message. + * It should not typically be called by other Floodlight modules or applications. + * + * @param tf, The table features to be updated. + */ + void processOFTableFeatures(List<OFTableFeaturesStatsReply> replies); /** * Compute the changes that would be required to replace the old ports diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java index f54f604597af07abec182c5ee5651b21c8a16c54..8b401dad680cdda48eccaa36fad5db0b987d08a3 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitch.java @@ -39,6 +39,7 @@ import javax.annotation.Nonnull; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.internal.IOFSwitchManager; +import net.floodlightcontroller.core.internal.TableFeatures; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.core.util.URIUtil; @@ -52,7 +53,6 @@ import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFeaturesReply; import org.projectfloodlight.openflow.protocol.OFFlowWildcards; import org.projectfloodlight.openflow.protocol.OFMessage; - import org.projectfloodlight.openflow.protocol.OFPortConfig; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; @@ -62,10 +62,13 @@ import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFRequest; import org.projectfloodlight.openflow.protocol.OFStatsReply; import org.projectfloodlight.openflow.protocol.OFStatsRequest; +import org.projectfloodlight.openflow.protocol.OFTableFeatures; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TableId; import net.floodlightcontroller.util.LinkedHashSetWrapper; import net.floodlightcontroller.util.OrderedCollection; @@ -76,6 +79,7 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -96,6 +100,8 @@ public class OFSwitch implements IOFSwitchBackend { protected short tables; protected final DatapathId datapathId; + private Map<TableId, TableFeatures> tableFeaturesByTableId; + private boolean startDriverHandshakeCalled = false; private final Map<OFAuxId, IOFConnectionBackend> connections; private volatile Map<URI, Map<OFAuxId, OFBsnControllerConnection>> controllerConnections; @@ -106,6 +112,8 @@ public class OFSwitch implements IOFSwitchBackend { */ private final PortManager portManager; + //private final TableManager tableManager; + private volatile boolean connected; private volatile OFControllerRole role; @@ -154,6 +162,8 @@ public class OFSwitch implements IOFSwitchBackend { this.setAttribute(PROP_FASTWILDCARDS, EnumSet.allOf(OFFlowWildcards.class)); this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, Boolean.TRUE); this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.TRUE); + + this.tableFeaturesByTableId = new HashMap<TableId, TableFeatures>(); } private static int ident(int i) { @@ -847,6 +857,23 @@ public class OFSwitch implements IOFSwitchBackend { return portManager.handlePortStatusMessage(ps); } + @Override + public void processOFTableFeatures(List<OFTableFeaturesStatsReply> replies) { + /* + * Parse out all the individual replies for each table. + */ + for (OFTableFeaturesStatsReply reply : replies) { + /* + * Add or update the features for a particular table. + */ + List<OFTableFeatures> tfs = reply.getEntries(); + for (OFTableFeatures tf : tfs) { + tableFeaturesByTableId.put(tf.getTableId(), TableFeatures.of(tf)); + log.trace("Received TableFeatures for TableId {}, TableName {}", tf.getTableId().toString(), tf.getName()); + } + } + } + @Override public Collection<OFPortDesc> getSortedPorts() { List<OFPortDesc> sortedPorts = @@ -920,12 +947,69 @@ public class OFSwitch implements IOFSwitchBackend { @Override public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(OFStatsRequest<REPLY> request) { - return connections.get(OFAuxId.MAIN).writeStatsRequest(request); + return addInternalStatsReplyListener(connections.get(OFAuxId.MAIN).writeStatsRequest(request), request); } @Override public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(OFStatsRequest<REPLY> request, LogicalOFMessageCategory category) { - return getConnection(category).writeStatsRequest(request); + return addInternalStatsReplyListener(getConnection(category).writeStatsRequest(request), request); + } + + /** + * Append a listener to receive an OFStatsReply and update the + * internal OFSwitch data structures. + * + * This presently taps into the following stats request + * messages to listen for the corresponding reply: + * -- OFTableFeaturesStatsRequest + * + * Extend this to tap into and update other OFStatsType messages. + * + * @param future + * @param request + * @return + */ + private <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> addInternalStatsReplyListener(final ListenableFuture<List<REPLY>> future, OFStatsRequest<REPLY> request) { + switch (request.getStatsType()) { + case TABLE_FEATURES: + /* case YOUR_CASE_HERE */ + future.addListener(new Runnable() { + /* + * We know the reply will be a list of OFStatsReply. + */ + @SuppressWarnings("unchecked") + @Override + public void run() { + /* + * The OFConnection handles REPLY_MORE for us in the case there + * are multiple OFStatsReply messages with the same XID. + */ + try { + List<? extends OFStatsReply> replies = future.get(); + if (!replies.isEmpty()) { + /* + * By checking only the 0th element, we assume all others are the same type. + * TODO If not, what then? + */ + switch (replies.get(0).getStatsType()) { + case TABLE_FEATURES: + processOFTableFeatures((List<OFTableFeaturesStatsReply>) future.get()); + break; + /* case YOUR_CASE_HERE */ + default: + throw new Exception("Received an invalid OFStatsReply of " + + replies.get(0).getStatsType().toString() + ". Expected TABLE_FEATURES."); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }, MoreExecutors.sameThreadExecutor()); /* No need for another thread. */ + default: + break; + } + return future; /* either unmodified or with an additional listener */ } @Override @@ -1119,4 +1203,9 @@ public class OFSwitch implements IOFSwitchBackend { } return false; } + + @Override + public TableFeatures getTableFeatures(TableId table) { + return tableFeaturesByTableId.get(table); + } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java index 3ce00d120738e332717836ba3729a301229e0487..6b165b5cab61b015996b90c3d9fd5af976e7bc38 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -62,7 +62,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { private Channel channel; private final Timer timer; private volatile OFChannelState state; - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); + private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14); private OFFeaturesReply featuresReply; private volatile OFConnection connection; private final IDebugCounterService debugCounters; diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java index b171312cacc4f54be88bd3dc9d906629f331e984..a17e7d5ae859978d5d7832ae10eed3273d7dc7b2 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java @@ -58,8 +58,11 @@ import org.projectfloodlight.openflow.protocol.OFRoleReply; import org.projectfloodlight.openflow.protocol.OFRoleRequest; import org.projectfloodlight.openflow.protocol.OFSetConfig; import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; import org.projectfloodlight.openflow.protocol.OFStatsRequestFlags; import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsRequest; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; @@ -96,8 +99,8 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { private final Map<OFAuxId, IOFConnectionBackend> auxConnections; private volatile OFSwitchHandshakeState state; private RoleChanger roleChanger; - // Default to 1.3 - This is overwritten by the features reply - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); + // Default to 1.4 - This is overwritten by the features reply + private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14); private final OFFeaturesReply featuresReply; private final Timer timer; @@ -763,6 +766,28 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { switchManager.notifyPortChanged(sw, ev.port, ev.type); } } + + /** + * Handle a table features message. + * + * Handle a table features message by updating the tables in the + * IOFSwitch instance and notifying Controller about the change so + * it can dispatch a switch update. + * + * @param h The OFChannelHandler that received the message + * @param m The OFTableFeatures message we received + * @param doNotify if true switch table changed events will be + * dispatched + */ + protected void handleTableFeaturesMessage(List<OFTableFeaturesStatsReply> replies, boolean doNotify) { + if (sw == null) { + String msg = getSwitchStateMessage(!replies.isEmpty() ? replies.get(0) : null, "State machine error: switch is null. Should never happen"); + throw new SwitchStateException(msg); + } + sw.processOFTableFeatures(replies); + //TODO like port status, might want to create an event and dispatch it. Not sure how useful this would be though... + } + /** * Process an OF message received on the channel and @@ -1001,12 +1026,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { // Handle pending messages now that we have a sw object handlePendingPortStatusMessages(description); - sw.startDriverHandshake(); - if (sw.isDriverHandshakeComplete()) { - setState(new WaitAppHandshakeState()); - } else { - setState(new WaitSwitchDriverSubHandshakeState()); - } + setState(new WaitTableFeaturesReplyState()); } void handlePendingPortStatusMessages(SwitchDescription description){ @@ -1022,6 +1042,77 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { sendHandshakeDescriptionStatsRequest(); } } + + /* + * New state: WaitSwitchTableFeaturesReplyState + */ + public class WaitTableFeaturesReplyState extends OFSwitchHandshakeState { + + private ArrayList<OFTableFeaturesStatsReply> replies; + WaitTableFeaturesReplyState() { + super(false); + replies = new ArrayList<OFTableFeaturesStatsReply>(); + } + + @Override + /** + * Accumulate a list of the OFTableFeaturesStatsReply's until there + * are no more remaining. Then, pass the list to the switch for + * parsing and configuration. + * + * The assumption is that the OFMessage dispatcher will call this each + * time, which it does. We don't loop and receive here. + * + * @param m, The potential OFTableFeaturesStatsReply message we want to include + */ + void processOFStatsReply(OFStatsReply m) { + if (m.getStatsType() == OFStatsType.TABLE_FEATURES) { + replies.add((OFTableFeaturesStatsReply) m); + if (!((OFTableFeaturesStatsReply)m).getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { + handleTableFeaturesMessage(replies, false); + nextState(); + } + } else { + /* should only receive TABLE_FEATURES here */ + log.error("Received {} message but expected TABLE_FEATURES.", m.getStatsType().toString()); + } + + } + + @Override + void processOFError(OFErrorMsg m) { + if ((m.getErrType() == OFErrorType.BAD_REQUEST) && + ((((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.MULTIPART_BUFFER_OVERFLOW) + || ((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_STAT)) { + log.warn("Switch {} is {} but does not support OFTableFeaturesStats. Assuming all tables can perform any match, action, and instruction in the spec.", + sw.getId().toString(), sw.getOFFactory().getVersion().toString()); + } else { + log.error("Received unexpected OFErrorMsg {} on switch {}.", m.toString(), sw.getId().toString()); + } + nextState(); + + } + + private void nextState() { + /* move on to the next state */ + sw.startDriverHandshake(); + if (sw.isDriverHandshakeComplete()) { + setState(new WaitAppHandshakeState()); + } else { + setState(new WaitSwitchDriverSubHandshakeState()); + } + } + + @Override + void enterState() { + if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) < 0) { + nextState(); + } else { + sendHandshakeTableFeaturesRequest(); + } + } + + } public class WaitSwitchDriverSubHandshakeState extends OFSwitchHandshakeState { @@ -1348,6 +1439,12 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { void processOFFlowRemoved(OFFlowRemoved m) { dispatchMessage(m); } + + @Override + void processOFStatsReply(OFStatsReply m) { + // TODO Auto-generated method stub + super.processOFStatsReply(m); + } } /** @@ -1665,6 +1762,17 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { .build(); mainConnection.write(descStatsRequest); } + + /** + * send a table features request + */ + private void sendHandshakeTableFeaturesRequest() { + OFTableFeaturesStatsRequest tfsr = factory.buildTableFeaturesStatsRequest() + /* leave entries blank --> just ask, don't set */ + .setXid(handshakeTransactionIds--) + .build(); + mainConnection.write(tfsr); + } OFSwitchHandshakeState getStateForTesting() { return state; diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java index 60c32deb2c9eaf50e0aa9bfadc8066d56cb936b3..3c5ea1085267f0e30bec55659d0bdd049e5052ec 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java @@ -715,6 +715,11 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen log.warn("Clear switch flow tables on each transition to master: TRUE"); OFSwitchManager.clearTablesOnEachTransitionToMaster = true; } + + String tablesToGetFTCFlow = configParams.get("addDefaultSendToControllerFlowInTables"); + if (tablesToGetFTCFlow == null || tablesToGetFTCFlow.isEmpty()) { + + } } @Override diff --git a/src/main/java/net/floodlightcontroller/core/internal/TableFeatures.java b/src/main/java/net/floodlightcontroller/core/internal/TableFeatures.java new file mode 100644 index 0000000000000000000000000000000000000000..35d12883df2dc3ca09017c458f7e680ea9a8d5b7 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/TableFeatures.java @@ -0,0 +1,442 @@ +package net.floodlightcontroller.core.internal; + +import java.util.List; + +import org.projectfloodlight.openflow.protocol.OFTableFeatureProp; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyActions; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyActionsMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplySetfield; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplySetfieldMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropExperimenter; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropExperimenterMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropInstructions; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropInstructionsMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropMatch; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropNextTables; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropNextTablesMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropTableSyncFrom; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropType; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWildcards; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteActions; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteActionsMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteSetfield; +import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteSetfieldMiss; +import org.projectfloodlight.openflow.protocol.OFTableFeatures; +import org.projectfloodlight.openflow.protocol.ver13.OFTableFeaturePropTypeSerializerVer13; +import org.projectfloodlight.openflow.protocol.ver14.OFTableFeaturePropTypeSerializerVer14; +import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U64; + +/** + * A small, immutable container for organizing the features of a + * particular table on a switch. OFTableFeatures is pretty much the + * same, but it doesn't easily expose the properties for quick access. + * + * This class will sacrifice the added time to instantiate instead + * of during each access to a particular property. Had the properties + * been in a Map of some sort, this might not be necessary. The + * assumption is that we won't ever change switch table properties + * frequently. It's more of a setup/initialization task. Otherwise, + * frequently making these would be quite inefficient considering + * the amount of data within and the number of tables switches can have. + * + * <b> FIXME: OFTableFeatureProp types don't override equals in Loxi... </b> + * + * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com + * + */ +public class TableFeatures { + /* OF1.3+ */ + private OFTableFeaturePropApplyActions aa; + private OFTableFeaturePropApplyActionsMiss aam; + private OFTableFeaturePropApplySetfield asf; + private OFTableFeaturePropApplySetfieldMiss asfm; + private OFTableFeaturePropExperimenter e; + private OFTableFeaturePropExperimenterMiss em; + private OFTableFeaturePropInstructions i; + private OFTableFeaturePropInstructionsMiss im; + private OFTableFeaturePropMatch m; + private OFTableFeaturePropNextTables nt; + private OFTableFeaturePropNextTablesMiss ntm; + private OFTableFeaturePropWildcards w; + private OFTableFeaturePropWriteActions wa; + private OFTableFeaturePropWriteActionsMiss wam; + private OFTableFeaturePropWriteSetfield wsf; + private OFTableFeaturePropWriteSetfieldMiss wsfm; + /* OF1.4+ */ + private OFTableFeaturePropTableSyncFrom tsf; + + private long config; + private long maxEntries; + private U64 metadataMatch; + private U64 metadataWrite; + private String tableName; + private TableId tableId; + + /* + * Loxi-style constructor for familiarity. Also eliminates + * the need to use the new keyword. + */ + public static TableFeatures of(OFTableFeatures tableFeatures) { + return new TableFeatures(tableFeatures); + } + + /* + * Make default constructor inaccessible. We don't set default + * values so, we must start with an OFTableFeatures. + */ + private TableFeatures() {} + + /* + * Called from of(OFTableFeatures tableFeatures) above. + * Private to avoid confusion. + */ + private TableFeatures(OFTableFeatures tf) { + /* + * First, get the properties. + */ + List<OFTableFeatureProp> properties = tf.getProperties(); + for (OFTableFeatureProp p : properties) { + OFTableFeaturePropType pt = getTableFeaturePropType(p); + switch (pt) { + case APPLY_ACTIONS: + aa = (OFTableFeaturePropApplyActions) p; + break; + case APPLY_ACTIONS_MISS: + aam = (OFTableFeaturePropApplyActionsMiss) p; + break; + case APPLY_SETFIELD: + asf = (OFTableFeaturePropApplySetfield) p; + break; + case APPLY_SETFIELD_MISS: + asfm = (OFTableFeaturePropApplySetfieldMiss) p; + break; + case EXPERIMENTER: + e = (OFTableFeaturePropExperimenter) p; + break; + case EXPERIMENTER_MISS: + em = (OFTableFeaturePropExperimenterMiss) p; + break; + case INSTRUCTIONS: + i = (OFTableFeaturePropInstructions) p; + break; + case INSTRUCTIONS_MISS: + im = (OFTableFeaturePropInstructionsMiss) p; + break; + case MATCH: + m = (OFTableFeaturePropMatch) p; + break; + case NEXT_TABLES: + nt = (OFTableFeaturePropNextTables) p; + break; + case NEXT_TABLES_MISS: + ntm = (OFTableFeaturePropNextTablesMiss) p; + break; + case TABLE_SYNC_FROM: + tsf = (OFTableFeaturePropTableSyncFrom) p; + break; + case WILDCARDS: + w = (OFTableFeaturePropWildcards) p; + break; + case WRITE_ACTIONS: + wa = (OFTableFeaturePropWriteActions) p; + break; + case WRITE_ACTIONS_MISS: + wam = (OFTableFeaturePropWriteActionsMiss) p; + break; + case WRITE_SETFIELD: + wsf = (OFTableFeaturePropWriteSetfield) p; + break; + case WRITE_SETFIELD_MISS: + wsfm = (OFTableFeaturePropWriteSetfieldMiss) p; + break; + default: + throw new UnsupportedOperationException("OFTableFeaturePropType " + pt.toString() + " not accounted for in " + this.getClass().getCanonicalName()); + } + } + + /* + * Next, get the other info e.g. name, ID, etc. + */ + config = tf.getConfig(); + maxEntries = tf.getMaxEntries(); + metadataMatch = tf.getMetadataMatch(); + metadataWrite = tf.getMetadataWrite(); + tableId = tf.getTableId(); + tableName = tf.getName(); + } + + /* + * Internal helper function to deserialize the property type in a version-agnostic way + * from the perspective of the calling function. + */ + private static OFTableFeaturePropType getTableFeaturePropType(OFTableFeatureProp p) { + switch (p.getVersion()) { + case OF_13: + return OFTableFeaturePropTypeSerializerVer13.ofWireValue((short) p.getType()); + case OF_14: + return OFTableFeaturePropTypeSerializerVer14.ofWireValue((short) p.getType()); + default: + throw new IllegalArgumentException("OFVersion " + p.getVersion().toString() + " does not support OFTableFeature messages."); + } + } + + public long getConfig() { + return config; + } + + public long getMaxEntries() { + return maxEntries; + } + + public U64 getMetadataMatch() { + return metadataMatch; + } + + public U64 getMetadataWrite() { + return metadataWrite; + } + + public String getTableName() { + return tableName; + } + + public TableId getTableId() { + return tableId; + } + + public OFTableFeaturePropApplyActions getPropApplyActions() { + return aa; + } + + public OFTableFeaturePropApplyActionsMiss getPropApplyActionsMiss() { + return aam; + } + + public OFTableFeaturePropApplySetfield getPropApplySetField() { + return asf; + } + + public OFTableFeaturePropApplySetfieldMiss getPropApplySetFieldMiss() { + return asfm; + } + + public OFTableFeaturePropExperimenter getPropExperimenter() { + return e; + } + + public OFTableFeaturePropExperimenterMiss getPropExperimenterMiss() { + return em; + } + + public OFTableFeaturePropInstructions getPropInstructions() { + return i; + } + + public OFTableFeaturePropInstructionsMiss getPropInstructionsMiss() { + return im; + } + + public OFTableFeaturePropMatch getPropMatch() { + return m; + } + + public OFTableFeaturePropNextTables getPropNextTables() { + return nt; + } + + public OFTableFeaturePropNextTablesMiss getPropNextTablesMiss() { + return ntm; + } + + public OFTableFeaturePropWildcards getPropWildcards() { + return w; + } + + public OFTableFeaturePropWriteActions getPropWriteActions() { + return wa; + } + + public OFTableFeaturePropWriteActionsMiss getPropWriteActionsMiss() { + return wam; + } + + public OFTableFeaturePropWriteSetfield getPropWriteSetField() { + return wsf; + } + + public OFTableFeaturePropWriteSetfieldMiss getPropWriteSetFieldMiss() { + return wsfm; + } + + public OFTableFeaturePropTableSyncFrom getPropTableSyncFrom() { + return tsf; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((aa == null) ? 0 : aa.hashCode()); + result = prime * result + ((aam == null) ? 0 : aam.hashCode()); + result = prime * result + ((asf == null) ? 0 : asf.hashCode()); + result = prime * result + ((asfm == null) ? 0 : asfm.hashCode()); + result = prime * result + (int) (config ^ (config >>> 32)); + result = prime * result + ((e == null) ? 0 : e.hashCode()); + result = prime * result + ((em == null) ? 0 : em.hashCode()); + result = prime * result + ((i == null) ? 0 : i.hashCode()); + result = prime * result + ((im == null) ? 0 : im.hashCode()); + result = prime * result + ((m == null) ? 0 : m.hashCode()); + result = prime * result + (int) (maxEntries ^ (maxEntries >>> 32)); + result = prime * result + + ((metadataMatch == null) ? 0 : metadataMatch.hashCode()); + result = prime * result + + ((metadataWrite == null) ? 0 : metadataWrite.hashCode()); + result = prime * result + ((nt == null) ? 0 : nt.hashCode()); + result = prime * result + ((ntm == null) ? 0 : ntm.hashCode()); + result = prime * result + ((tableId == null) ? 0 : tableId.hashCode()); + result = prime * result + + ((tableName == null) ? 0 : tableName.hashCode()); + result = prime * result + ((tsf == null) ? 0 : tsf.hashCode()); + result = prime * result + ((w == null) ? 0 : w.hashCode()); + result = prime * result + ((wa == null) ? 0 : wa.hashCode()); + result = prime * result + ((wam == null) ? 0 : wam.hashCode()); + result = prime * result + ((wsf == null) ? 0 : wsf.hashCode()); + result = prime * result + ((wsfm == null) ? 0 : wsfm.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TableFeatures other = (TableFeatures) obj; + if (aa == null) { + if (other.aa != null) + return false; + } else if (!aa.equals(other.aa)) + return false; + if (aam == null) { + if (other.aam != null) + return false; + } else if (!aam.equals(other.aam)) + return false; + if (asf == null) { + if (other.asf != null) + return false; + } else if (!asf.equals(other.asf)) + return false; + if (asfm == null) { + if (other.asfm != null) + return false; + } else if (!asfm.equals(other.asfm)) + return false; + if (config != other.config) + return false; + if (e == null) { + if (other.e != null) + return false; + } else if (!e.equals(other.e)) + return false; + if (em == null) { + if (other.em != null) + return false; + } else if (!em.equals(other.em)) + return false; + if (i == null) { + if (other.i != null) + return false; + } else if (!i.equals(other.i)) + return false; + if (im == null) { + if (other.im != null) + return false; + } else if (!im.equals(other.im)) + return false; + if (m == null) { + if (other.m != null) + return false; + } else if (!m.equals(other.m)) + return false; + if (maxEntries != other.maxEntries) + return false; + if (metadataMatch == null) { + if (other.metadataMatch != null) + return false; + } else if (!metadataMatch.equals(other.metadataMatch)) + return false; + if (metadataWrite == null) { + if (other.metadataWrite != null) + return false; + } else if (!metadataWrite.equals(other.metadataWrite)) + return false; + if (nt == null) { + if (other.nt != null) + return false; + } else if (!nt.equals(other.nt)) + return false; + if (ntm == null) { + if (other.ntm != null) + return false; + } else if (!ntm.equals(other.ntm)) + return false; + if (tableId == null) { + if (other.tableId != null) + return false; + } else if (!tableId.equals(other.tableId)) + return false; + if (tableName == null) { + if (other.tableName != null) + return false; + } else if (!tableName.equals(other.tableName)) + return false; + if (tsf == null) { + if (other.tsf != null) + return false; + } else if (!tsf.equals(other.tsf)) + return false; + if (w == null) { + if (other.w != null) + return false; + } else if (!w.equals(other.w)) + return false; + if (wa == null) { + if (other.wa != null) + return false; + } else if (!wa.equals(other.wa)) + return false; + if (wam == null) { + if (other.wam != null) + return false; + } else if (!wam.equals(other.wam)) + return false; + if (wsf == null) { + if (other.wsf != null) + return false; + } else if (!wsf.equals(other.wsf)) + return false; + if (wsfm == null) { + if (other.wsfm != null) + return false; + } else if (!wsfm.equals(other.wsfm)) + return false; + return true; + } + + @Override + public String toString() { + return "TableFeatures [TableName=" + tableName + ", TableId=" + tableId + + ", Config=" + config + ", MaxEntries=" + maxEntries + + ", MetadataMatch=" + metadataMatch + ", MetadataWrite=" + metadataWrite + + ", ApplyActions=" + aa + ", ApplyActionsMiss=" + aam + ", ApplySetField=" + asf + + ", ApplySetFieldMiss=" + asfm + ", Experimenter=" + e + ", ExperimenterMiss=" + em + ", Instructions=" + i + + ", InstructionsMiss=" + im + ", Match=" + m + ", NextTable=" + nt + ", NextTableMiss=" + ntm + + ", Wildcards=" + w + ", WriteActions=" + wa + ", WriteActionsMiss=" + wam + ", WriteSetField=" + wsf + + ", WriteSetFieldMiss=" + wsfm + ", TableSyncFrom=" + tsf + + "]"; + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java index 5c2c1b02ae1a2346c51f123b2578959eca6c6cfe..85fbf0f46fa1fee79dda7f06e2ca590fb7c519cb 100644 --- a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java +++ b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java @@ -10,11 +10,13 @@ import java.util.Map; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFMeterBandStats; import org.projectfloodlight.openflow.protocol.OFMeterBandType; import org.projectfloodlight.openflow.protocol.OFMeterConfig; import org.projectfloodlight.openflow.protocol.OFMeterMod; import org.projectfloodlight.openflow.protocol.OFMeterModCommand; +import org.projectfloodlight.openflow.protocol.OFMeterStatsRequest; import org.projectfloodlight.openflow.protocol.OFOxmClass; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFSetConfig; @@ -28,8 +30,10 @@ import org.projectfloodlight.openflow.protocol.OFTableModPropEvictionFlag; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionPushVlan; import org.projectfloodlight.openflow.protocol.action.OFActionSetField; import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.action.OFActions; import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions; import org.projectfloodlight.openflow.protocol.instruction.OFInstructionClearActions; @@ -37,12 +41,14 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionExperime import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable; import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter; import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions; +import org.projectfloodlight.openflow.protocol.instruction.OFInstructions; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand; import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop; import org.projectfloodlight.openflow.protocol.oxm.OFOxm; import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc; +import org.projectfloodlight.openflow.protocol.oxm.OFOxms; import org.projectfloodlight.openflow.protocol.ver13.OFMeterModCommandSerializerVer13; import org.projectfloodlight.openflow.types.ArpOpcode; import org.projectfloodlight.openflow.types.DatapathId; @@ -126,6 +132,42 @@ public class TestModule implements IFloodlightModule, IOFSwitchListener { @Override public void switchAdded(DatapathId switchId) { OFFactory factory = switchService.getSwitch(switchId).getOFFactory(); + + /*OFFlowAdd.Builder fab = factory.buildFlowAdd(); + fab.setMatch(factory.buildMatch().setExact(MatchField.ETH_TYPE, EthType.IPv4) + .setMasked(MatchField.IPV4_SRC, IPv4Address.of("10.0.123.1"), IPv4Address.of("255.255.0.255")) + .build()); + fab.setBufferId(OFBufferId.NO_BUFFER); + if (switchId.equals(DatapathId.of(1))) + switchService.getSwitch(switchId).write(fab.build());*/ + + OFFactory f =factory; + Match.Builder mb =f.buildMatch(); + mb.setExact(MatchField.ETH_SRC, MacAddress.of(2)); + Match m=mb.build(); + ArrayList<OFAction> actionList = new ArrayList<OFAction>(); + OFActions actions = f.actions(); + OFActionPushVlan vlan =actions.pushVlan(EthType.of(0x8100)); + actionList.add(vlan); + + + OFOxms oxms =f.oxms(); + OFActionSetField vlanid=actions.buildSetField().setField(oxms.buildVlanVid().setValue(OFVlanVidMatch.ofVlan(10)).build()).build(); + actionList.add(vlanid); + OFInstructions inst=f.instructions(); + OFInstructionApplyActions apply=inst.buildApplyActions().setActions(actionList).build(); + ArrayList<OFInstruction> instList= new ArrayList<OFInstruction>(); + instList.add(apply); + OFFlowMod.Builder fmb = factory.buildFlowAdd(); + OFFlowMod msg = fmb.setPriority(32769) + .setMatch(m) + .setInstructions(instList) + .setOutPort(OFPort.of(1)) + .build(); + + switchService.getSwitch(switchId).write(msg); + + /* * An attempt at meters, but they aren't supported anywhere, yet... * OFMeterBand mb = factory.meterBands().buildDrop() diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 7c9e3ea4d5bc71b948ef8bbf6caf0cafaceccbfa..2c8ca28ccbefd41c21583decb480458dc076366a 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -20,5 +20,5 @@ <logger name="net.floodlightcontroller.routing" level="INFO"></logger> <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger> <logger level="INFO" name="net.floodlightcontroller.learningswitch"></logger> - <logger level="INFO" name="net.floodlightcontroller.staticflowentry"></logger> + <logger level="INFO" name="org.projectfloodlight.openflow"></logger> </configuration> diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java index f9ffc92559e556b1bf3613d7d3c6b06c79b3b903..e7590df1c838b059a9bdfc33f4387bfc8aecc66b 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java @@ -19,6 +19,7 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.concurrent.TimeUnit; @@ -62,11 +63,16 @@ import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFSetConfig; import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFTableFeatureProp; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; import net.floodlightcontroller.util.LinkedHashSetWrapper; import net.floodlightcontroller.util.OrderedCollection; @@ -307,6 +313,25 @@ public abstract class OFSwitchHandlerTestBase { .build(); return statsReply; } + + protected OFTableFeaturesStatsReply createTableFeaturesStatsReply() { + OFTableFeaturesStatsReply statsReply = factory.buildTableFeaturesStatsReply() + .setEntries(Collections.singletonList(factory.buildTableFeatures() + .setConfig(0) + .setMaxEntries(100) + .setMetadataMatch(U64.NO_MASK) + .setMetadataWrite(U64.NO_MASK) + .setName("MyTable") + .setTableId(TableId.of(1)) + .setProperties(Collections.singletonList((OFTableFeatureProp)factory.buildTableFeaturePropMatch() + .setOxmIds(Collections.singletonList(U32.of(100))) + .build()) + ).build() + ) + + ).build(); + return statsReply; + } /** * setup the expectations for the mock switch that are needed diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java index cefde88995600fc48eeda697301e801f89ff5dd7..be752d0246f9075751fae5d2f321a7ee4af85ac7 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java @@ -79,6 +79,7 @@ public class OFSwitchHandshakeHandlerVer10Test extends OFSwitchHandlerTestBase { setupSwitchForInstantiationWithReset(); sw.startDriverHandshake(); expectLastCall().once(); + expect(sw.getOFFactory()).andReturn(factory).once(); sw.isDriverHandshakeComplete(); expectLastCall().andReturn(switchDriverComplete).once(); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java index b830915b6bac4cea5a21c0c039c84628ced42fe0..3d78ab6fc07bc836d32f96d1b000e6696ae5e337 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java @@ -11,6 +11,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import java.util.EnumSet; +import java.util.List; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; @@ -20,6 +21,7 @@ import net.floodlightcontroller.core.IOFSwitchBackend; import net.floodlightcontroller.core.OFConnection; import net.floodlightcontroller.core.SwitchDescription; import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitAppHandshakeState; +import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitTableFeaturesReplyState; import org.projectfloodlight.openflow.protocol.OFCapabilities; import org.projectfloodlight.openflow.protocol.OFControllerRole; @@ -33,6 +35,8 @@ import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest; import org.projectfloodlight.openflow.protocol.OFRoleReply; import org.projectfloodlight.openflow.protocol.OFRoleRequest; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsRequest; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; @@ -93,7 +97,7 @@ public class OFSwitchHandshakeHandlerVer13Test extends OFSwitchHandlerTestBase { switchHandler.processOFMessage(getPortDescStatsReply()); } - public void handleDescStatsAndCreateSwitch(boolean subHandShakeComplete) throws Exception { + public void handleDescStatsAndCreateSwitch() throws Exception { // build the stats reply OFDescStatsReply sr = createDescriptionStatsReply(); @@ -102,9 +106,8 @@ public class OFSwitchHandshakeHandlerVer13Test extends OFSwitchHandlerTestBase { setupSwitchForInstantiationWithReset(); sw.setPortDescStats(anyObject(OFPortDescStatsReply.class)); expectLastCall().once(); - sw.startDriverHandshake(); - expectLastCall().once(); - expect(sw.isDriverHandshakeComplete()).andReturn(subHandShakeComplete).once(); + + expect(sw.getOFFactory()).andReturn(factory).once(); replay(sw); reset(switchManager); @@ -122,14 +125,43 @@ public class OFSwitchHandshakeHandlerVer13Test extends OFSwitchHandlerTestBase { // send the description stats reply switchHandler.processOFMessage(sr); + OFMessage msg = connection.retrieveMessage(); + assertThat(msg, CoreMatchers.instanceOf(OFTableFeaturesStatsRequest.class)); + verifyUniqueXids(msg); + verify(sw, switchManager); } + + public void handleTableFeatures(boolean subHandShakeComplete) throws Exception { + // build the table features stats reply + OFTableFeaturesStatsReply tf = createTableFeaturesStatsReply(); + + reset(sw); + sw.startDriverHandshake(); + expectLastCall().once(); + expect(sw.isDriverHandshakeComplete()).andReturn(subHandShakeComplete).once(); + sw.processOFTableFeatures(anyObject(List.class)); + expectLastCall().once(); + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + replay(sw); + + switchHandler.processOFMessage(tf); + } + + @Test + public void moveToWaitTableFeaturesReplyState() throws Exception { + moveToWaitDescriptionStatReply(); + handleDescStatsAndCreateSwitch(); + + assertThat(switchHandler.getStateForTesting(), + CoreMatchers.instanceOf(WaitTableFeaturesReplyState.class)); + } @Test @Override public void moveToWaitAppHandshakeState() throws Exception { - moveToWaitDescriptionStatReply(); - handleDescStatsAndCreateSwitch(true); + moveToWaitTableFeaturesReplyState(); + handleTableFeatures(true); assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(WaitAppHandshakeState.class)); @@ -186,8 +218,8 @@ public class OFSwitchHandshakeHandlerVer13Test extends OFSwitchHandlerTestBase { @Override @Test public void moveToWaitSwitchDriverSubHandshake() throws Exception { - moveToWaitDescriptionStatReply(); - handleDescStatsAndCreateSwitch(false); + moveToWaitTableFeaturesReplyState(); + handleTableFeatures(false); //TODO assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitSwitchDriverSubHandshakeState.class)); assertThat("Unexpected message captured", connection.getMessages(), Matchers.empty()); diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index 338a013a064115eeffa11c7ab739ec38ad7ef72d..c11a14372eb33ad7d007e6d75c05bb2d1d68cc73 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -30,6 +30,7 @@ import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.LogicalOFMessageCategory; import net.floodlightcontroller.core.OFConnection; import net.floodlightcontroller.core.SwitchDescription; +import net.floodlightcontroller.core.internal.TableFeatures; import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFCapabilities; @@ -42,6 +43,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsReply; import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TableId; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; @@ -334,4 +336,10 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { // TODO Auto-generated method stub return null; } + + @Override + public TableFeatures getTableFeatures(TableId table) { + // TODO Auto-generated method stub + return null; + } }