Implement compile method for PacketPathFlow class.
- This patch supports ADD operation only.
- This task is a part of ONOS-1741 and ONOS-1690.
Change-Id: Idd1cad5cf582df77633dae45b1fd5a9756708134
diff --git a/src/main/java/net/onrc/onos/api/flowmanager/PacketPathFlow.java b/src/main/java/net/onrc/onos/api/flowmanager/PacketPathFlow.java
index ce1fdb4..42cbd65 100644
--- a/src/main/java/net/onrc/onos/api/flowmanager/PacketPathFlow.java
+++ b/src/main/java/net/onrc/onos/api/flowmanager/PacketPathFlow.java
@@ -1,16 +1,24 @@
package net.onrc.onos.api.flowmanager;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import net.onrc.onos.api.flowmanager.FlowBatchOperation.Operator;
+import net.onrc.onos.core.matchaction.MatchAction;
import net.onrc.onos.core.matchaction.MatchActionIdGenerator;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
import net.onrc.onos.core.matchaction.MatchActionOperations;
import net.onrc.onos.core.matchaction.MatchActionOperationsIdGenerator;
import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.OutputAction;
import net.onrc.onos.core.matchaction.match.PacketMatch;
import net.onrc.onos.core.util.PortNumber;
+import net.onrc.onos.core.util.SwitchPort;
/**
* Flow object representing a packet path.
@@ -70,7 +78,105 @@
public List<MatchActionOperations> compile(Operator op,
MatchActionIdGenerator maIdGenerator,
MatchActionOperationsIdGenerator maoIdGenerator) {
- // TODO Auto-generated method stub
- return null;
+ switch (op) {
+ case ADD:
+ return compileAddOperation(maIdGenerator, maoIdGenerator);
+ case REMOVE:
+ return compileRemoveOperation(maIdGenerator, maoIdGenerator);
+ default:
+ throw new UnsupportedOperationException("Unknown operation.");
+ }
+ }
+
+ /**
+ * Creates the next {@link MatchAction} object using iterators.
+ *
+ * @param portIterator the iterator for {@link SwitchPort} objects
+ * @param actionsIterator the iterator for the lists of {@link Action}
+ * @param maIdGenerator the ID generator of {@link MatchAction}
+ * @return {@link MatchAction} object based on the specified iterators
+ */
+ private MatchAction createNextMatchAction(Iterator<SwitchPort> portIterator,
+ Iterator<List<Action>> actionsIterator,
+ MatchActionIdGenerator maIdGenerator) {
+ if (portIterator == null || actionsIterator == null ||
+ !portIterator.hasNext() || !actionsIterator.hasNext()) {
+ return null;
+ }
+
+ // TODO: Update this after merging the new MatchAction API.
+ return new MatchAction(
+ maIdGenerator.getNewId(),
+ portIterator.next(),
+ getMatch(), actionsIterator.next());
+ }
+
+ /**
+ * Generates the list of {@link MatchActionOperations} objects with
+ * add-operation.
+ *
+ * @return the list of {@link MatchActionOperations} objects
+ */
+ private List<MatchActionOperations> compileAddOperation(
+ MatchActionIdGenerator maIdGenerator,
+ MatchActionOperationsIdGenerator maoIdGenerator) {
+ Path path = checkNotNull(getPath());
+ checkState(path.size() > 0, "Path object has no link.");
+
+ // Preparing each actions and ingress port for each switch
+ List<List<Action>> actionsList = new LinkedList<>();
+ List<SwitchPort> portList = new LinkedList<>();
+ for (FlowLink link : path) {
+ portList.add(link.getDstSwitchPort());
+ actionsList.add(Arrays.asList(
+ (Action) new OutputAction(link.getSrcPortNumber())));
+ }
+
+ // The head switch's ingress port
+ portList.add(0, new SwitchPort(path.getSrcDpid(), getIngressPortNumber()));
+
+ // The tail switch's action
+ actionsList.add(getEgressActions());
+
+ Iterator<SwitchPort> portIterator = portList.iterator();
+ Iterator<List<Action>> actionsIterator = actionsList.iterator();
+
+ // Creates the second phase operation
+ // using the head switch's match action
+ MatchAction headMatchAction = createNextMatchAction(portIterator,
+ actionsIterator, maIdGenerator);
+ if (headMatchAction == null) {
+ return null;
+ }
+ MatchActionOperations secondOp = new MatchActionOperations(
+ maoIdGenerator.getNewId());
+ secondOp.addOperation(new MatchActionOperationEntry(
+ MatchActionOperations.Operator.ADD, headMatchAction));
+
+ // Creates the first phase operation
+ // using the remaining switches' match actions
+ MatchActionOperations firstOp = new MatchActionOperations(
+ maoIdGenerator.getNewId());
+ MatchAction ma;
+ while ((ma = createNextMatchAction(portIterator, actionsIterator, maIdGenerator)) != null) {
+ firstOp.addOperation(new MatchActionOperationEntry(
+ MatchActionOperations.Operator.ADD, ma));
+ }
+
+ return Arrays.asList(firstOp, secondOp);
+ }
+
+ /**
+ * Generates the list of {@link MatchActionOperations} objects with
+ * remote-operation.
+ *
+ * @return the list of {@link MatchActionOperations} objects
+ */
+ private List<MatchActionOperations> compileRemoveOperation(
+ MatchActionIdGenerator maIdGenerator,
+ MatchActionOperationsIdGenerator maoIdGenerator) {
+ // TODO implement it
+ throw new UnsupportedOperationException(
+ "REMOVE operation is not implemented yet.");
}
}
diff --git a/src/test/java/net/onrc/onos/api/flowmanager/PacketPathFlowTest.java b/src/test/java/net/onrc/onos/api/flowmanager/PacketPathFlowTest.java
new file mode 100644
index 0000000..04e2649
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/flowmanager/PacketPathFlowTest.java
@@ -0,0 +1,234 @@
+package net.onrc.onos.api.flowmanager;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.matchaction.MatchAction;
+import net.onrc.onos.core.matchaction.MatchActionIdGeneratorWithIdBlockAllocator;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.MatchActionOperations;
+import net.onrc.onos.core.matchaction.MatchActionOperations.Operator;
+import net.onrc.onos.core.matchaction.MatchActionOperationsIdGeneratorWithIdBlockAllocator;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.ModifyDstMacAction;
+import net.onrc.onos.core.matchaction.action.OutputAction;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatchBuilder;
+import net.onrc.onos.core.util.IdBlock;
+import net.onrc.onos.core.util.IdBlockAllocator;
+import net.onrc.onos.core.util.PortNumber;
+import net.onrc.onos.core.util.SwitchPort;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link PacketPathFlow} class.
+ */
+public class PacketPathFlowTest {
+ private Path pathWith4Switches;
+ private Path pathWith2Switches;
+ private PacketMatch match;
+ private List<Action> actions;
+ private IdBlockAllocator allocator;
+
+ @Before
+ public void setUp() throws Exception {
+ allocator = createMock(IdBlockAllocator.class);
+ expect(allocator.allocateUniqueIdBlock())
+ .andReturn(new IdBlock(0, 99))
+ .andReturn(new IdBlock(100, 199));
+ replay(allocator);
+
+ pathWith4Switches = new Path();
+ pathWith4Switches.add(new FlowLink(
+ new SwitchPort(1, (short) 10), new SwitchPort(2, (short) 11)));
+ pathWith4Switches.add(new FlowLink(
+ new SwitchPort(2, (short) 12), new SwitchPort(3, (short) 13)));
+ pathWith4Switches.add(new FlowLink(
+ new SwitchPort(3, (short) 14), new SwitchPort(4, (short) 15)));
+
+ pathWith2Switches = new Path();
+ pathWith2Switches.add(new FlowLink(
+ new SwitchPort(1, (short) 10), new SwitchPort(2, (short) 11)));
+
+ PacketMatchBuilder builder = new PacketMatchBuilder();
+ builder.setDstMac(MACAddress.valueOf(54321));
+ match = builder.build();
+
+ actions = Arrays.asList(
+ new ModifyDstMacAction(MACAddress.valueOf(12345)),
+ new OutputAction(PortNumber.uint32(101)));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ /**
+ * Checks the constructor initializes fields properly.
+ */
+ @Test
+ public void testConstructor() {
+ PacketPathFlow flow = new PacketPathFlow(
+ new FlowId(1L), match, PortNumber.uint32(100),
+ pathWith4Switches, actions, 0, 0);
+
+ assertNotNull(flow);
+ assertEquals(new FlowId(1L), flow.getId());
+ assertEquals(match, flow.getMatch());
+ assertEquals(PortNumber.uint32(100), flow.getIngressPortNumber());
+ assertEquals(pathWith4Switches, flow.getPath());
+ assertEquals(actions, flow.getEgressActions());
+ assertEquals(0, flow.getHardTimeout());
+ assertEquals(0, flow.getIdleTimeout());
+ }
+
+ /**
+ * Checks the compile method with add-operation. This test creates a flow
+ * object using the path with 2 switches, then checks if the compiled the
+ * list of MatchActionOperations objects are generated properly.
+ */
+ @Test
+ public void testCompileWithAddOperationFor2Switches() {
+ PacketPathFlow flow = new PacketPathFlow(
+ new FlowId(1L), match, PortNumber.uint32(100),
+ pathWith2Switches, actions, 0, 0);
+
+ List<MatchActionOperations> maOpsList =
+ flow.compile(FlowBatchOperation.Operator.ADD,
+ new MatchActionIdGeneratorWithIdBlockAllocator(allocator),
+ new MatchActionOperationsIdGeneratorWithIdBlockAllocator(
+ allocator)
+ );
+
+ assertEquals(2, maOpsList.size());
+
+ MatchActionOperations firstOp = maOpsList.get(0);
+ assertEquals(1, firstOp.size());
+ assertEquals(1, firstOp.getOperations().size());
+
+ MatchActionOperations secondOp = maOpsList.get(1);
+ assertEquals(1, secondOp.size());
+ assertEquals(1, secondOp.getOperations().size());
+
+ MatchActionOperationEntry entry1 = secondOp.getOperations().get(0);
+ MatchActionOperationEntry entry2 = firstOp.getOperations().get(0);
+
+ assertEquals(Operator.ADD, entry1.getOperator());
+ assertEquals(Operator.ADD, entry2.getOperator());
+
+ MatchAction ma1 = entry1.getTarget();
+ MatchAction ma2 = entry2.getTarget();
+
+ assertNotNull(ma1);
+ assertNotNull(ma2);
+
+ assertEquals(new SwitchPort(1, (short) 100), ma1.getSwitchPort());
+ assertEquals(new SwitchPort(2, (short) 11), ma2.getSwitchPort());
+
+ assertEquals(match, ma1.getMatch());
+ assertEquals(match, ma2.getMatch());
+
+ assertNotNull(ma1.getActions());
+ assertNotNull(ma2.getActions());
+
+ assertEquals(1, ma1.getActions().size());
+ assertEquals(2, ma2.getActions().size());
+
+ assertEquals(new OutputAction(PortNumber.uint32(10)),
+ ma1.getActions().get(0));
+ assertEquals(new ModifyDstMacAction(MACAddress.valueOf(12345)),
+ ma2.getActions().get(0));
+ assertEquals(new OutputAction(PortNumber.uint32(101)),
+ ma2.getActions().get(1));
+ }
+
+ /**
+ * Checks the compile method with add-operation. This test creates a flow
+ * object using the path with 4 switches, then checks if the compiled the
+ * list of MatchActionOperations objects are generated properly.
+ */
+ @Test
+ public void testCompileWithAddOperationFor4Switches() {
+ PacketPathFlow flow = new PacketPathFlow(
+ new FlowId(1L), match, PortNumber.uint32(100),
+ pathWith4Switches, actions, 0, 0);
+
+ List<MatchActionOperations> maOpsList =
+ flow.compile(FlowBatchOperation.Operator.ADD,
+ new MatchActionIdGeneratorWithIdBlockAllocator(allocator),
+ new MatchActionOperationsIdGeneratorWithIdBlockAllocator(
+ allocator)
+ );
+
+ assertEquals(2, maOpsList.size());
+
+ MatchActionOperations firstOp = maOpsList.get(0);
+ assertEquals(3, firstOp.size());
+ assertEquals(3, firstOp.getOperations().size());
+
+ MatchActionOperations secondOp = maOpsList.get(1);
+ assertEquals(1, secondOp.size());
+ assertEquals(1, secondOp.getOperations().size());
+
+ MatchActionOperationEntry entry1 = secondOp.getOperations().get(0);
+ MatchActionOperationEntry entry2 = firstOp.getOperations().get(0);
+ MatchActionOperationEntry entry3 = firstOp.getOperations().get(1);
+ MatchActionOperationEntry entry4 = firstOp.getOperations().get(2);
+
+ assertEquals(Operator.ADD, entry1.getOperator());
+ assertEquals(Operator.ADD, entry2.getOperator());
+ assertEquals(Operator.ADD, entry3.getOperator());
+ assertEquals(Operator.ADD, entry4.getOperator());
+
+ MatchAction ma1 = entry1.getTarget();
+ MatchAction ma2 = entry2.getTarget();
+ MatchAction ma3 = entry3.getTarget();
+ MatchAction ma4 = entry4.getTarget();
+
+ assertNotNull(ma1);
+ assertNotNull(ma2);
+ assertNotNull(ma3);
+ assertNotNull(ma4);
+
+ assertEquals(new SwitchPort(1, (short) 100), ma1.getSwitchPort());
+ assertEquals(new SwitchPort(2, (short) 11), ma2.getSwitchPort());
+ assertEquals(new SwitchPort(3, (short) 13), ma3.getSwitchPort());
+ assertEquals(new SwitchPort(4, (short) 15), ma4.getSwitchPort());
+
+ assertEquals(match, ma1.getMatch());
+ assertEquals(match, ma2.getMatch());
+ assertEquals(match, ma3.getMatch());
+ assertEquals(match, ma4.getMatch());
+
+ assertNotNull(ma1.getActions());
+ assertNotNull(ma2.getActions());
+ assertNotNull(ma3.getActions());
+ assertNotNull(ma4.getActions());
+
+ assertEquals(1, ma1.getActions().size());
+ assertEquals(1, ma2.getActions().size());
+ assertEquals(1, ma3.getActions().size());
+ assertEquals(2, ma4.getActions().size());
+
+ assertEquals(new OutputAction(PortNumber.uint32(10)),
+ ma1.getActions().get(0));
+ assertEquals(new OutputAction(PortNumber.uint32(12)),
+ ma2.getActions().get(0));
+ assertEquals(new OutputAction(PortNumber.uint32(14)),
+ ma3.getActions().get(0));
+ assertEquals(new ModifyDstMacAction(MACAddress.valueOf(12345)),
+ ma4.getActions().get(0));
+ assertEquals(new OutputAction(PortNumber.uint32(101)),
+ ma4.getActions().get(1));
+ }
+}