Xconnect support for fabric.p4 pipeliner
Change-Id: I3bd802ccbc34561b71862a160bab67adeccc2891
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 a90353e..580d6f7 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
@@ -76,7 +76,8 @@
private static final Set<PiTableId> NEXT_CTRL_TBLS = ImmutableSet.of(
FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE,
- FabricConstants.FABRIC_INGRESS_NEXT_HASHED);
+ FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
+ FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT);
private static final Set<PiTableId> E_NEXT_CTRL_TBLS = ImmutableSet.of(
FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN);
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 4248111..bca8424 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
@@ -108,6 +108,8 @@
return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
} else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
+ } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
+ return mapNextXconnect(treatment, tableId);
}
throw new PiInterpreterException(format(
"Treatment mapping not supported for table '%s'", tableId));
@@ -174,6 +176,18 @@
}
}
+ private static PiAction mapNextXconnect(
+ TrafficTreatment treatment, PiTableId tableId)
+ throws PiInterpreterException {
+ final PortNumber outPort = ((OutputInstruction) instructionOrFail(
+ treatment, OUTPUT, tableId)).port();
+ return PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
+ .withParameter(new PiActionParam(
+ FabricConstants.PORT_NUM, outPort.toLong()))
+ .build();
+ }
+
static PiAction mapAclTreatment(TrafficTreatment treatment, PiTableId tableId)
throws PiInterpreterException {
if (isNoAction(treatment)) {
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 e45768a..b2c5406 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
@@ -66,6 +66,8 @@
class NextObjectiveTranslator
extends AbstractObjectiveTranslator<NextObjective> {
+ private static final String XCONNECT = "xconnect";
+
NextObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
super(deviceId, capabilities);
}
@@ -85,7 +87,11 @@
hashedNext(obj, resultBuilder);
break;
case BROADCAST:
- multicastNext(obj, resultBuilder);
+ if (isXconnect(obj)) {
+ xconnectNext(obj, resultBuilder);
+ } else {
+ multicastNext(obj, resultBuilder);
+ }
break;
default:
log.warn("Unsupported NextObjective type '{}'", obj);
@@ -248,12 +254,57 @@
}
private TrafficSelector nextIdSelector(int nextId) {
+ return nextIdSelectorBuilder(nextId).build();
+ }
+
+ private TrafficSelector.Builder nextIdSelectorBuilder(int nextId) {
final PiCriterion nextIdCriterion = PiCriterion.builder()
.matchExact(FabricConstants.HDR_NEXT_ID, nextId)
.build();
return DefaultTrafficSelector.builder()
- .matchPi(nextIdCriterion)
+ .matchPi(nextIdCriterion);
+ }
+
+ private void xconnectNext(NextObjective obj, ObjectiveTranslation.Builder resultBuilder)
+ throws FabricPipelinerException {
+
+ final Collection<DefaultNextTreatment> defaultNextTreatments =
+ defaultNextTreatmentsOrFail(obj.nextTreatments());
+
+ final List<PortNumber> outPorts = defaultNextTreatments.stream()
+ .map(DefaultNextTreatment::treatment)
+ .map(FabricUtils::outputPort)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ if (outPorts.size() != 2) {
+ throw new FabricPipelinerException(format(
+ "Handling XCONNECT with %d treatments (ports), but expected is 2",
+ defaultNextTreatments.size()), ObjectiveError.UNSUPPORTED);
+ }
+
+ final PortNumber port1 = outPorts.get(0);
+ final PortNumber port2 = outPorts.get(1);
+ final TrafficSelector selector1 = nextIdSelectorBuilder(obj.id())
+ .matchInPort(port1)
.build();
+ final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
+ .setOutput(port2)
+ .build();
+ final TrafficSelector selector2 = nextIdSelectorBuilder(obj.id())
+ .matchInPort(port2)
+ .build();
+ final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
+ .setOutput(port1)
+ .build();
+
+ resultBuilder.addFlowRule(flowRule(
+ obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
+ selector1, treatment1));
+ resultBuilder.addFlowRule(flowRule(
+ obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
+ selector2, treatment2));
+
}
private void multicastNext(NextObjective obj,
@@ -410,4 +461,8 @@
return obj.op() == Objective.Operation.ADD_TO_EXISTING ||
obj.op() == Objective.Operation.REMOVE_FROM_EXISTING;
}
+
+ private boolean isXconnect(NextObjective obj) {
+ return obj.appId().name().contains(XCONNECT);
+ }
}
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 081f68f..a56d26b 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
@@ -415,4 +415,83 @@
assertEquals(expectedTranslation, actualTranslation);
}
+
+ /**
+ * Test XConnect NextObjective.
+ *
+ * @throws FabricPipelinerException
+ */
+ @Test
+ public void testXconnectOutput() throws FabricPipelinerException {
+ TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
+ .setOutput(PORT_1)
+ .build();
+ TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
+ .setOutput(PORT_2)
+ .build();
+ NextObjective nextObjective = DefaultNextObjective.builder()
+ .withId(NEXT_ID_1)
+ .withPriority(PRIORITY)
+ .addTreatment(treatment1)
+ .addTreatment(treatment2)
+ .withType(NextObjective.Type.BROADCAST)
+ .makePermanent()
+ .fromApp(XCONNECT_APP_ID)
+ .add();
+
+ ObjectiveTranslation actualTranslation = translatorHashed.doTranslate(nextObjective);
+
+ // Should generate 2 flows for the xconnect table.
+
+ // Expected multicast table flow rule.
+ PiCriterion nextIdCriterion = PiCriterion.builder()
+ .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
+ .build();
+ TrafficSelector xcSelector1 = DefaultTrafficSelector.builder()
+ .matchPi(nextIdCriterion)
+ .matchInPort(PORT_1)
+ .build();
+ TrafficTreatment xcTreatment1 = DefaultTrafficTreatment.builder()
+ .piTableAction(PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
+ .withParameter(new PiActionParam(FabricConstants.PORT_NUM, PORT_2.toLong()))
+ .build())
+ .build();
+ TrafficSelector xcSelector2 = DefaultTrafficSelector.builder()
+ .matchPi(nextIdCriterion)
+ .matchInPort(PORT_2)
+ .build();
+ TrafficTreatment xcTreatment2 = DefaultTrafficTreatment.builder()
+ .piTableAction(PiAction.builder()
+ .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
+ .withParameter(new PiActionParam(FabricConstants.PORT_NUM, PORT_1.toLong()))
+ .build())
+ .build();
+
+ FlowRule expectedXcFlowRule1 = DefaultFlowRule.builder()
+ .forDevice(DEVICE_ID)
+ .fromApp(XCONNECT_APP_ID)
+ .makePermanent()
+ .withPriority(nextObjective.priority())
+ .forTable(FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT)
+ .withSelector(xcSelector1)
+ .withTreatment(xcTreatment1)
+ .build();
+ FlowRule expectedXcFlowRule2 = DefaultFlowRule.builder()
+ .forDevice(DEVICE_ID)
+ .fromApp(XCONNECT_APP_ID)
+ .makePermanent()
+ .withPriority(nextObjective.priority())
+ .forTable(FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT)
+ .withSelector(xcSelector2)
+ .withTreatment(xcTreatment2)
+ .build();
+
+ ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+ .addFlowRule(expectedXcFlowRule1)
+ .addFlowRule(expectedXcFlowRule2)
+ .build();
+
+ assertEquals(expectedTranslation, actualTranslation);
+ }
}
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 a5704da..0b8a71c 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
@@ -37,6 +37,7 @@
public class FabricPipelinerTest {
static final ApplicationId APP_ID = TestApplicationId.create("FabricPipelinerTest");
+ static final ApplicationId XCONNECT_APP_ID = TestApplicationId.create("FabricPipelinerTest.xconnect");
static final DeviceId DEVICE_ID = DeviceId.deviceId("device:bmv2:11");
static final int PRIORITY = 100;
static final PortNumber PORT_1 = PortNumber.portNumber(1);