ONOS-7402 Broadcast objective type support by FabricNextPipeliner.

Change-Id: I08b544a82e257091a079648c07b1dc6fb72c9ab9
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
index 3b66b1b..d8dcc8a 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
@@ -17,6 +17,7 @@
 package org.onosproject.pipelines.fabric.pipeliner;
 
 import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.driver.Driver;
@@ -37,10 +38,14 @@
 import org.onosproject.net.flowobjective.ObjectiveError;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.net.pi.runtime.PiGroupKey;
 import org.onosproject.pipelines.fabric.FabricConstants;
 import org.slf4j.Logger;
@@ -77,6 +82,9 @@
             case HASHED:
                 processHashedNext(nextObjective, resultBuilder);
                 break;
+            case BROADCAST:
+                processBroadcastNext(nextObjective, resultBuilder);
+                break;
             default:
                 log.warn("Unsupported next type {}", nextObjective);
                 resultBuilder.setError(ObjectiveError.UNSUPPORTED);
@@ -292,4 +300,62 @@
                 .matchPi(nextIdCriterion)
                 .build();
     }
+
+    private void processBroadcastNext(NextObjective next, PipelinerTranslationResult.Builder resultBuilder) {
+        int groupId = next.id();
+        List<GroupBucket> bucketList = next.next().stream()
+                .filter(treatment -> treatment != null)
+                .map(DefaultGroupBucket::createAllGroupBucket)
+                .collect(Collectors.toList());
+
+        if (bucketList.size() != next.next().size()) {
+            // some action not converted
+            // set error
+            log.warn("Expected bucket size {}, got {}", next.next().size(), bucketList.size());
+            resultBuilder.setError(ObjectiveError.BADPARAMS);
+            return;
+        }
+
+        GroupBuckets buckets = new GroupBuckets(bucketList);
+        //Used DefaultGroupKey instead of PiGroupKey
+        //as we don't have any action profile to apply to the groups of ALL type
+        GroupKey groupKey = new DefaultGroupKey(FabricPipeliner.KRYO.serialize(groupId));
+
+        resultBuilder.addGroup(new DefaultGroupDescription(deviceId,
+                                                           GroupDescription.Type.ALL,
+                                                           buckets,
+                                                           groupKey,
+                                                           groupId,
+                                                           next.appId()));
+        //flow rule
+        TrafficSelector selector = buildNextIdSelector(next.id());
+        PiActionParam groupIdParam = new PiActionParam(FabricConstants.GID,
+                                                       ImmutableByteSequence.copyFrom(groupId));
+
+        PiAction setMcGroupAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP)
+                .withParameter(groupIdParam)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setMcGroupAction)
+                .build();
+
+        resultBuilder.addFlowRule(DefaultFlowRule.builder()
+                                          .withSelector(selector)
+                                          .withTreatment(treatment)
+                                          .forTable(FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST)
+                                          .makePermanent()
+                                          .withPriority(next.priority())
+                                          .forDevice(deviceId)
+                                          .fromApp(next.appId())
+                                          .build());
+
+        // Egress VLAN handling
+        next.next().forEach(trafficTreatment -> {
+            PortNumber outputPort = getOutputPort(trafficTreatment);
+            if (includesPopVlanInst(trafficTreatment) && outputPort != null) {
+                processVlanPopRule(outputPort, next, resultBuilder);
+            }
+        });
+    }
 }
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 e2a6af8..bfae578 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
@@ -17,8 +17,8 @@
 package org.onosproject.pipelines.fabric.pipeliner;
 
 import com.google.common.collect.ImmutableList;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -30,10 +30,14 @@
 import org.onosproject.net.flowobjective.NextObjective;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.net.pi.runtime.PiGroupKey;
 import org.onosproject.pipelines.fabric.FabricConstants;
 
@@ -257,8 +261,113 @@
      * Test program output group for Broadcast table.
      */
     @Test
-    @Ignore
     public void testBroadcastOutput() {
+        TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
+                .setOutput(PORT_1)
+                .build();
+        TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
+                .popVlan()
+                .setOutput(PORT_2)
+                .build();
+        NextObjective nextObjective = DefaultNextObjective.builder()
+                .withId(NEXT_ID_1)
+                .withPriority(PRIORITY)
+                .addTreatment(treatment1)
+                .addTreatment(treatment2)
+                .withMeta(VLAN_META)
+                .withType(NextObjective.Type.BROADCAST)
+                .makePermanent()
+                .fromApp(APP_ID)
+                .add();
 
+        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(nextObjective);
+
+        // Should generate 1 flow, 1 group and 2 buckets in it
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        assertEquals(3, flowRulesInstalled.size());
+        assertEquals(1, groupsInstalled.size());
+        assertEquals(2, groupsInstalled.get(0).buckets().buckets().size());
+
+        //create the expected flow rule
+        PiCriterion nextIdCriterion = PiCriterion.builder()
+                .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, NEXT_ID_1)
+                .build();
+        TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
+                .matchPi(nextIdCriterion)
+                .build();
+
+        PiActionParam groupIdParam = new PiActionParam(FabricConstants.GID,
+                                                       ImmutableByteSequence.copyFrom(NEXT_ID_1));
+        PiAction setMcGroupAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP)
+                .withParameter(groupIdParam)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setMcGroupAction)
+                .build();
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .withPriority(nextObjective.priority())
+                .forTable(FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST)
+                .withSelector(nextIdSelector)
+                .withTreatment(treatment)
+                .build();
+
+        // VLAN meta table
+        FlowRule vmFlowRule = flowRulesInstalled.get(0);
+        assertTrue(vmFlowRule.exactMatch(vlanMetaFlowRule));
+
+        FlowRule actualFlowRule = flowRulesInstalled.get(1);
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+
+        //prepare expected egress rule for the egress vlan pipeline
+        PiCriterion egressVlanTableMatch = PiCriterion.builder()
+                .matchExact(FabricConstants.STANDARD_METADATA_EGRESS_PORT,
+                            (short) PORT_2.toLong())
+                .build();
+        TrafficSelector selectorForEgressVlan = DefaultTrafficSelector.builder()
+                .matchPi(egressVlanTableMatch)
+                .matchVlanId(VLAN_100)
+                .build();
+        TrafficTreatment treatmentForEgressVlan = DefaultTrafficTreatment.builder()
+                .popVlan()
+                .build();
+        FlowRule expectedEgressVlanRule = DefaultFlowRule.builder()
+                .withSelector(selectorForEgressVlan)
+                .withTreatment(treatmentForEgressVlan)
+                .forTable(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN)
+                .makePermanent()
+                .withPriority(nextObjective.priority())
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .build();
+        //egress vlan table
+        FlowRule actualEgressVlanFlowRule = flowRulesInstalled.get(2);
+        assertTrue(expectedEgressVlanRule.exactMatch(actualEgressVlanFlowRule));
+
+        //create the expected group
+        GroupDescription actualGroup = groupsInstalled.get(0);
+        List<TrafficTreatment> treatments = ImmutableList.of(treatment1, treatment2);
+
+        List<GroupBucket> buckets = treatments.stream()
+                .map(DefaultGroupBucket::createAllGroupBucket)
+                .collect(Collectors.toList());
+
+        GroupBuckets groupBuckets = new GroupBuckets(buckets);
+
+        GroupKey groupKey = new DefaultGroupKey(FabricPipeliner.KRYO.serialize(NEXT_ID_1));
+
+        GroupDescription expectedGroup = new DefaultGroupDescription(
+                DEVICE_ID,
+                GroupDescription.Type.ALL,
+                groupBuckets,
+                groupKey,
+                NEXT_ID_1,
+                APP_ID
+        );
+        assertEquals(expectedGroup, actualGroup);
     }
 }