Support in fabric pipeliner for pushing double VLAN tag in Next obj
- Small modification to better support pop and route
- To support route and push we expect to receive a Next Objective with two VLAN_ID
- Added capability to check if the pipeline support double VLAN termination
Change-Id: I8bfbf61ccd838a069121e5ab4a804f695a446bac
(cherry picked from commit f51d0c110af13bd0bfc0d006e070f0e6bbbcd231)
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/AbstractFabricHandlerBehavior.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/AbstractFabricHandlerBehavior.java
index 2753844..db4da90 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/AbstractFabricHandlerBehavior.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/AbstractFabricHandlerBehavior.java
@@ -30,21 +30,48 @@
/**
* Abstract implementation of HandlerBehaviour for the fabric pipeconf
* behaviors.
+ * <p>
+ * All sub-classes must implement a default constructor, used by the abstract
+ * projectable model (i.e., {@link org.onosproject.net.Device#as(Class)}.
*/
-public class AbstractFabricHandlerBehavior extends AbstractHandlerBehaviour {
+public abstract class AbstractFabricHandlerBehavior extends AbstractHandlerBehaviour {
protected final Logger log = getLogger(getClass());
protected FabricCapabilities capabilities;
+ /**
+ * Creates a new instance of this behavior with the given capabilities.
+ * Note: this constructor should be invoked only by other classes of this
+ * package that can retrieve capabilities on their own.
+ * <p>
+ * When using the abstract projectable model (i.e., {@link
+ * org.onosproject.net.Device#as(Class)}, capabilities will be set by the
+ * driver manager when calling {@link #setHandler(DriverHandler)})
+ *
+ * @param capabilities capabilities
+ */
+ protected AbstractFabricHandlerBehavior(FabricCapabilities capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ /**
+ * Create a new instance of this behaviour. Used by the abstract projectable
+ * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
+ */
+ public AbstractFabricHandlerBehavior() {
+ // Do nothing
+ }
+
@Override
public void setHandler(DriverHandler handler) {
super.setHandler(handler);
final PiPipeconfService pipeconfService = handler().get(PiPipeconfService.class);
- setCapabilities(handler().data().deviceId(), pipeconfService);
+ setCapabilitiesFromHandler(handler().data().deviceId(), pipeconfService);
}
- private void setCapabilities(DeviceId deviceId, PiPipeconfService pipeconfService) {
+ private void setCapabilitiesFromHandler(
+ DeviceId deviceId, PiPipeconfService pipeconfService) {
checkNotNull(deviceId);
checkNotNull(pipeconfService);
// Get pipeconf capabilities.
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricCapabilities.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricCapabilities.java
index 07986f9..b0ce208 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricCapabilities.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricCapabilities.java
@@ -76,4 +76,14 @@
return Optional.empty();
}
}
+
+ public boolean supportDoubleVlanTerm() {
+ if (pipeconf.pipelineModel()
+ .table(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN).isPresent()) {
+ return pipeconf.pipelineModel().table(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN)
+ .get().action(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
+ .isPresent();
+ }
+ return false;
+ }
}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricIntProgrammable.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricIntProgrammable.java
index d593262..1770576 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricIntProgrammable.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricIntProgrammable.java
@@ -86,6 +86,23 @@
private DeviceId deviceId;
private ApplicationId appId;
+ /**
+ * Creates a new instance of this behavior with the given capabilities.
+ *
+ * @param capabilities capabilities
+ */
+ protected FabricIntProgrammable(FabricCapabilities capabilities) {
+ super(capabilities);
+ }
+
+ /**
+ * Create a new instance of this behaviour. Used by the abstract projectable
+ * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
+ */
+ public FabricIntProgrammable() {
+ super();
+ }
+
private boolean setupBehaviour() {
deviceId = this.data().deviceId();
flowRuleService = handler().get(FlowRuleService.class);
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
index 69edeff..8567a3a 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
@@ -27,6 +27,7 @@
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instructions;
@@ -116,6 +117,36 @@
.put(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4, NOP)
.build();
+ private FabricTreatmentInterpreter treatmentInterpreter;
+
+ /**
+ * Creates a new instance of this behavior with the given capabilities.
+ *
+ * @param capabilities capabilities
+ */
+ public FabricInterpreter(FabricCapabilities capabilities) {
+ super(capabilities);
+ instantiateTreatmentInterpreter();
+ }
+
+ /**
+ * Create a new instance of this behaviour. Used by the abstract projectable
+ * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
+ */
+ public FabricInterpreter() {
+ super();
+ }
+
+ private void instantiateTreatmentInterpreter() {
+ this.treatmentInterpreter = new FabricTreatmentInterpreter(this.capabilities);
+ }
+
+ @Override
+ public void setHandler(DriverHandler handler) {
+ super.setHandler(handler);
+ instantiateTreatmentInterpreter();
+ }
+
@Override
public Optional<PiMatchFieldId> mapCriterionType(Criterion.Type type) {
return Optional.ofNullable(CRITERION_MAP.get(type));
@@ -132,15 +163,15 @@
public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
throws PiInterpreterException {
if (FILTERING_CTRL_TBLS.contains(piTableId)) {
- return FabricTreatmentInterpreter.mapFilteringTreatment(treatment, piTableId);
+ return treatmentInterpreter.mapFilteringTreatment(treatment, piTableId);
} else if (FORWARDING_CTRL_TBLS.contains(piTableId)) {
- return FabricTreatmentInterpreter.mapForwardingTreatment(treatment, piTableId);
+ return treatmentInterpreter.mapForwardingTreatment(treatment, piTableId);
} else if (ACL_CTRL_TBLS.contains(piTableId)) {
- return FabricTreatmentInterpreter.mapAclTreatment(treatment, piTableId);
+ return treatmentInterpreter.mapAclTreatment(treatment, piTableId);
} else if (NEXT_CTRL_TBLS.contains(piTableId)) {
- return FabricTreatmentInterpreter.mapNextTreatment(treatment, piTableId);
+ return treatmentInterpreter.mapNextTreatment(treatment, piTableId);
} else if (E_NEXT_CTRL_TBLS.contains(piTableId)) {
- return FabricTreatmentInterpreter.mapEgressNextTreatment(treatment, piTableId);
+ return treatmentInterpreter.mapEgressNextTreatment(treatment, piTableId);
} else {
throw new PiInterpreterException(format(
"Treatment mapping not supported for table '%s'", piTableId));
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
index b7ee4c5..b501f33 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
@@ -32,6 +32,9 @@
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
+import java.util.List;
+import java.util.stream.Collectors;
+
import static java.lang.String.format;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
@@ -42,12 +45,14 @@
import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
import static org.onosproject.pipelines.fabric.FabricUtils.instruction;
import static org.onosproject.pipelines.fabric.FabricUtils.l2Instruction;
+import static org.onosproject.pipelines.fabric.FabricUtils.l2Instructions;
/**
* Treatment translation logic.
*/
final class FabricTreatmentInterpreter {
+ private final FabricCapabilities capabilities;
private static final ImmutableMap<PiTableId, PiActionId> NOP_ACTIONS =
ImmutableMap.<PiTableId, PiActionId>builder()
.put(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
@@ -60,8 +65,9 @@
FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
.build();
- private FabricTreatmentInterpreter() {
- // Hide default constructor
+
+ FabricTreatmentInterpreter(FabricCapabilities capabilities) {
+ this.capabilities = capabilities;
}
static PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId)
@@ -73,7 +79,8 @@
tableException(tableId);
}
- if (isNoAction(treatment)) {
+ // VLAN_POP action is equivalent to the permit action (VLANs pop is done anyway)
+ if (isNoAction(treatment) || isFilteringPopAction(treatment)) {
// Permit action if table is ingress_port_vlan;
return nop(tableId);
}
@@ -99,7 +106,7 @@
return null;
}
- static PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
+ PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
throws PiInterpreterException {
if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN) {
return mapNextVlanTreatment(treatment, tableId);
@@ -114,16 +121,29 @@
"Treatment mapping not supported for table '%s'", tableId));
}
- private static PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
+ private PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
throws PiInterpreterException {
- final ModVlanIdInstruction modVlanIdInst = (ModVlanIdInstruction)
- l2InstructionOrFail(treatment, VLAN_ID, tableId);
- return PiAction.builder()
- .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
- .withParameter(new PiActionParam(
- FabricConstants.VLAN_ID,
- modVlanIdInst.vlanId().toShort()))
- .build();
+ final List<ModVlanIdInstruction> modVlanIdInst = l2InstructionsOrFail(treatment, VLAN_ID, tableId)
+ .stream().map(i -> (ModVlanIdInstruction) i).collect(Collectors.toList());
+ if (modVlanIdInst.size() == 1) {
+ return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
+ .withParameter(new PiActionParam(
+ FabricConstants.VLAN_ID,
+ modVlanIdInst.get(0).vlanId().toShort()))
+ .build();
+ }
+ if (modVlanIdInst.size() == 2 && capabilities.supportDoubleVlanTerm()) {
+ return PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
+ .withParameter(new PiActionParam(
+ FabricConstants.INNER_VLAN_ID,
+ modVlanIdInst.get(0).vlanId().toShort()))
+ .withParameter(new PiActionParam(
+ FabricConstants.OUTER_VLAN_ID,
+ modVlanIdInst.get(1).vlanId().toShort()))
+ .build();
+ }
+ throw new PiInterpreterException("Too many VLAN instructions");
}
private static PiAction mapNextHashedOrSimpleTreatment(
@@ -223,6 +243,10 @@
treatment.allInstructions().isEmpty();
}
+ private static boolean isFilteringPopAction(TrafficTreatment treatment) {
+ return l2Instruction(treatment, VLAN_POP) != null;
+ }
+
private static Instruction l2InstructionOrFail(
TrafficTreatment treatment,
L2ModificationInstruction.L2SubType subType, PiTableId tableId)
@@ -234,6 +258,17 @@
return inst;
}
+ private static List<L2ModificationInstruction> l2InstructionsOrFail(
+ TrafficTreatment treatment,
+ L2ModificationInstruction.L2SubType subType, PiTableId tableId)
+ throws PiInterpreterException {
+ final List<L2ModificationInstruction> inst = l2Instructions(treatment, subType);
+ if (inst == null || inst.isEmpty()) {
+ treatmentException(tableId, treatment, format("missing %s instruction", subType));
+ }
+ return inst;
+ }
+
private static Instruction instructionOrFail(
TrafficTreatment treatment, Instruction.Type type, PiTableId tableId)
throws PiInterpreterException {
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricUtils.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricUtils.java
index 8e5caa1..ae000b5 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricUtils.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricUtils.java
@@ -27,6 +27,8 @@
import org.onosproject.net.flowobjective.NextTreatment;
import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
@@ -77,6 +79,15 @@
.findFirst().orElse(null);
}
+ public static List<L2ModificationInstruction> l2Instructions(
+ TrafficTreatment treatment, L2ModificationInstruction.L2SubType subType) {
+ return treatment.allInstructions().stream()
+ .filter(i -> i.type().equals(Instruction.Type.L2MODIFICATION))
+ .map(i -> (L2ModificationInstruction) i)
+ .filter(i -> i.subtype().equals(subType))
+ .collect(Collectors.toList());
+ }
+
public static Instructions.OutputInstruction outputInstruction(TrafficTreatment treatment) {
return instruction(treatment, Instruction.Type.OUTPUT);
}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/AbstractObjectiveTranslator.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/AbstractObjectiveTranslator.java
index c1e2f4d..c2ca0ff 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/AbstractObjectiveTranslator.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/AbstractObjectiveTranslator.java
@@ -46,11 +46,12 @@
protected final FabricCapabilities capabilities;
protected final DeviceId deviceId;
- private final PiPipelineInterpreter interpreter = new FabricInterpreter();
+ private final PiPipelineInterpreter interpreter;
AbstractObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
this.deviceId = checkNotNull(deviceId);
this.capabilities = checkNotNull(capabilities);
+ this.interpreter = new FabricInterpreter(capabilities);
}
public ObjectiveTranslation translate(T obj) {
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
index 4bf1bb5..20cafde 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
@@ -38,6 +38,7 @@
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupService;
import org.onosproject.pipelines.fabric.AbstractFabricHandlerBehavior;
+import org.onosproject.pipelines.fabric.FabricCapabilities;
import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
@@ -78,6 +79,23 @@
private final ExecutorService callbackExecutor = SharedExecutors.getPoolThreadExecutor();
+ /**
+ * Creates a new instance of this behavior with the given capabilities.
+ *
+ * @param capabilities capabilities
+ */
+ public FabricPipeliner(FabricCapabilities capabilities) {
+ super(capabilities);
+ }
+
+ /**
+ * Create a new instance of this behaviour. Used by the abstract projectable
+ * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
+ */
+ public FabricPipeliner() {
+ super();
+ }
+
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
this.deviceId = deviceId;
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
index ec1c64b..d06d383 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
@@ -61,6 +61,7 @@
import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
import static org.onosproject.pipelines.fabric.FabricUtils.criterion;
import static org.onosproject.pipelines.fabric.FabricUtils.l2Instruction;
+import static org.onosproject.pipelines.fabric.FabricUtils.l2Instructions;
import static org.onosproject.pipelines.fabric.FabricUtils.outputPort;
/**
@@ -112,35 +113,45 @@
private void nextVlan(NextObjective obj,
ObjectiveTranslation.Builder resultBuilder)
throws FabricPipelinerException {
+ // We expect NextObjective treatments to contain one or two VLAN instructions.
+ // If two, this treatment should be mapped to an action for double-vlan push.
+ // In fabric.p4, mapping next IDs to VLAN IDs is done by a direct table (next_vlan),
+ // for this reason, we also make sure that all treatments in the NextObjective
+ // have exactly the same VLAN instructions, as they will be mapped to a single action
- // Set the egress VLAN for this next ID. Expect a VLAN_ID instruction
- // in the treatment, or use what's in meta.
- final List<ModVlanIdInstruction> vlanInstructions = defaultNextTreatments(
+ // Try to extract VLAN instructions in the treatment,
+ // later we check if we support multiple VLAN termination.
+ final List<List<ModVlanIdInstruction>> vlanInstructions = defaultNextTreatments(
obj.nextTreatments(), false).stream()
- .map(t -> (ModVlanIdInstruction) l2Instruction(t.treatment(), VLAN_ID))
- .filter(Objects::nonNull)
+ .map(defaultNextTreatment ->
+ l2Instructions(defaultNextTreatment.treatment(), VLAN_ID)
+ .stream().map(v -> (ModVlanIdInstruction) v)
+ .collect(Collectors.toList()))
+ .filter(l -> !l.isEmpty())
.collect(Collectors.toList());
+
final VlanIdCriterion vlanIdCriterion = obj.meta() == null ? null
: (VlanIdCriterion) criterion(obj.meta().criteria(), Criterion.Type.VLAN_VID);
- VlanId vlanId;
+ final List<VlanId> vlanIdList;
if (vlanInstructions.isEmpty() && vlanIdCriterion == null) {
// No VLAN_ID to apply.
return;
- } else if (!vlanInstructions.isEmpty()) {
+ }
+ if (!vlanInstructions.isEmpty()) {
// Give priority to what found in the instructions.
- // Expect the same VLAN ID for all instructions.
- final Set<VlanId> vlanIds = vlanInstructions.stream()
- .map(ModVlanIdInstruction::vlanId)
+ // Expect the same VLAN ID (or two VLAN IDs in the same order) for all instructions.
+ final Set<List<VlanId>> vlanIds = vlanInstructions.stream()
+ .map(l -> l.stream().map(ModVlanIdInstruction::vlanId).collect(Collectors.toList()))
.collect(Collectors.toSet());
if (obj.nextTreatments().size() != vlanInstructions.size() ||
vlanIds.size() != 1) {
throw new FabricPipelinerException(
"Inconsistent VLAN_ID instructions, cannot process " +
"next_vlan rule. It is required that all " +
- "treatments have the same VLAN_ID instruction.");
+ "treatments have the same VLAN_ID instructions.");
}
- vlanId = vlanIds.iterator().next();
+ vlanIdList = vlanIds.iterator().next();
} else {
// Use the value in meta.
// FIXME: there should be no need to generate a next_vlan rule for
@@ -149,13 +160,13 @@
// existing packet fields. But, for some reason, if we remove this
// rule, traffic is not forwarded at spines. We might need to look
// at the way default VLANs are handled in fabric.p4.
- vlanId = vlanIdCriterion.vlanId();
+ vlanIdList = List.of(vlanIdCriterion.vlanId());
}
final TrafficSelector selector = nextIdSelector(obj.id());
- final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setVlanId(vlanId)
- .build();
+ final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+ vlanIdList.stream().forEach(vlanId -> treatmentBuilder.setVlanId(vlanId));
+ final TrafficTreatment treatment = treatmentBuilder.build();
resultBuilder.addFlowRule(flowRule(
obj, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
index e59086b..bf37694 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
@@ -29,6 +29,9 @@
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
/**
@@ -43,9 +46,15 @@
private FabricInterpreter interpreter;
+ FabricCapabilities allCapabilities;
+
@Before
public void setup() {
- interpreter = new FabricInterpreter();
+ allCapabilities = createNiceMock(FabricCapabilities.class);
+ expect(allCapabilities.hasHashedTable()).andReturn(true).anyTimes();
+ expect(allCapabilities.supportDoubleVlanTerm()).andReturn(true).anyTimes();
+ replay(allCapabilities);
+ interpreter = new FabricInterpreter(allCapabilities);
}
/* Filtering control block */
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
index dbb2bd4..e15b117 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
@@ -265,6 +265,52 @@
}
/**
+ * Test double VLAN pop filtering objective
+ * Creates one rule for ingress_port_vlan table and 3 rules for
+ * fwd_classifier table (IPv4, IPv6 and MPLS unicast) when
+ * the condition is MAC + VLAN + INNER_VLAN.
+ */
+ @Test
+ public void testPopVlan() throws FabricPipelinerException {
+ FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+ .withKey(Criteria.matchInPort(PORT_1))
+ .addCondition(Criteria.matchEthDst(ROUTER_MAC))
+ .addCondition(Criteria.matchVlanId(VLAN_100))
+ .addCondition(Criteria.matchInnerVlanId(VLAN_200))
+ .withPriority(PRIORITY)
+ .fromApp(APP_ID)
+ .withMeta(DefaultTrafficTreatment.builder()
+ .popVlan()
+ .build())
+ .permit()
+ .add();
+ ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
+
+ // Ingress port vlan rule
+ FlowRule inportFlowRuleExpected = buildExpectedVlanInPortRule(
+ PORT_1, VLAN_100, VLAN_200, VlanId.NONE,
+ FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
+ // Forwarding classifier rules (ipv6, ipv4, mpls)
+ FlowRule classifierV4FlowRuleExpected = buildExpectedFwdClassifierRule(
+ PORT_1, ROUTER_MAC, null, Ethernet.TYPE_IPV4,
+ FilteringObjectiveTranslator.FWD_IPV4_ROUTING);
+ FlowRule classifierV6FlowRuleExpected = buildExpectedFwdClassifierRule(
+ PORT_1, ROUTER_MAC, null, Ethernet.TYPE_IPV6,
+ FilteringObjectiveTranslator.FWD_IPV6_ROUTING);
+ FlowRule classifierMplsFlowRuleExpected = buildExpectedFwdClassifierRule(
+ PORT_1, ROUTER_MAC, null, Ethernet.MPLS_UNICAST,
+ FilteringObjectiveTranslator.FWD_MPLS);
+ ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+ .addFlowRule(inportFlowRuleExpected)
+ .addFlowRule(classifierV4FlowRuleExpected)
+ .addFlowRule(classifierV6FlowRuleExpected)
+ .addFlowRule(classifierMplsFlowRuleExpected)
+ .build();
+
+ assertEquals(expectedTranslation, actualTranslation);
+ }
+
+ /**
* Incorrect filtering key or filtering conditions test.
*/
@Test
@@ -381,6 +427,9 @@
.build();
} else {
selector.matchVlanId(vlanId);
+ if (vlanValid(innerVlanId)) {
+ selector.matchInnerVlanId(innerVlanId);
+ }
piAction = PiAction.builder()
.withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
.build();
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
index cf7d741..28f699c 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
@@ -207,6 +207,88 @@
}
/**
+ * Test Route and Push Next Objective (set mac, set double vlan and output port).
+ */
+ @Test
+ public void testRouteAndPushNextObjective() throws FabricPipelinerException {
+ TrafficTreatment routeAndPushTreatment = DefaultTrafficTreatment.builder()
+ .setEthSrc(ROUTER_MAC)
+ .setEthDst(HOST_MAC)
+ .setOutput(PORT_1)
+ .setVlanId(VLAN_100)
+ .pushVlan()
+ .setVlanId(VLAN_200)
+ .build();
+
+ NextObjective nextObjective = DefaultNextObjective.builder()
+ .withId(NEXT_ID_1)
+ .withPriority(PRIORITY)
+ .addTreatment(routeAndPushTreatment)
+ .withType(NextObjective.Type.SIMPLE)
+ .makePermanent()
+ .fromApp(APP_ID)
+ .add();
+
+ ObjectiveTranslation actualTranslation = translatorSimple.translate(nextObjective);
+
+ PiAction piActionRouting = PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE)
+ .withParameter(new PiActionParam(
+ FabricConstants.SMAC, ROUTER_MAC.toBytes()))
+ .withParameter(new PiActionParam(
+ FabricConstants.DMAC, HOST_MAC.toBytes()))
+ .withParameter(new PiActionParam(
+ FabricConstants.PORT_NUM, PORT_1.toLong()))
+ .build();
+
+ PiAction piActionPush = PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
+ .withParameter(new PiActionParam(
+ FabricConstants.INNER_VLAN_ID, VLAN_100.toShort()))
+ .withParameter(new PiActionParam(
+ FabricConstants.OUTER_VLAN_ID, VLAN_200.toShort()))
+ .build();
+
+
+ TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
+ .matchPi(PiCriterion.builder()
+ .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
+ .build())
+ .build();
+ FlowRule expectedFlowRuleRouting = DefaultFlowRule.builder()
+ .forDevice(DEVICE_ID)
+ .fromApp(APP_ID)
+ .makePermanent()
+ // FIXME: currently next objective doesn't support priority, ignore this
+ .withPriority(0)
+ .forTable(FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE)
+ .withSelector(nextIdSelector)
+ .withTreatment(DefaultTrafficTreatment.builder()
+ .piTableAction(piActionRouting).build())
+ .build();
+ FlowRule expectedFlowRuleDoublePush = DefaultFlowRule.builder()
+ .withSelector(nextIdSelector)
+ .withTreatment(DefaultTrafficTreatment.builder()
+ .piTableAction(piActionPush)
+ .build())
+ .forTable(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN)
+ .makePermanent()
+ // FIXME: currently next objective doesn't support priority, ignore this
+ .withPriority(0)
+ .forDevice(DEVICE_ID)
+ .fromApp(APP_ID)
+ .build();
+
+ ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+ .addFlowRule(expectedFlowRuleDoublePush)
+ .addFlowRule(expectedFlowRuleRouting)
+ .build();
+
+
+ assertEquals(expectedTranslation, actualTranslation);
+ }
+
+ /**
* Test program ecmp output group for Hashed table.
*/
@Test
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
index 619a0b7..6c3551b 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
@@ -41,6 +41,7 @@
static final PortNumber PORT_1 = PortNumber.portNumber(1);
static final PortNumber PORT_2 = PortNumber.portNumber(2);
static final VlanId VLAN_100 = VlanId.vlanId("100");
+ static final VlanId VLAN_200 = VlanId.vlanId("200");
static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
static final MacAddress ROUTER_MAC = MacAddress.valueOf("00:00:00:00:02:01");
static final IpPrefix IPV4_UNICAST_ADDR = IpPrefix.valueOf("10.0.0.1/32");
@@ -61,6 +62,7 @@
this.capabilitiesSimple = createNiceMock(FabricCapabilities.class);
expect(capabilitiesHashed.hasHashedTable()).andReturn(true).anyTimes();
expect(capabilitiesSimple.hasHashedTable()).andReturn(false).anyTimes();
+ expect(capabilitiesSimple.supportDoubleVlanTerm()).andReturn(true).anyTimes();
replay(capabilitiesHashed);
replay(capabilitiesSimple);
}