ONOS-7251 - Initial implementation of fabric.p4 L2 broadcast feature.
Thrift client cherry-picked from the commit dd5792ac9ee38a702c3128a34224852b5c284687
Change-Id: I989f2b2074485a892195889a7c976b518510da88
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/Bmv2PreGroupTranslatorImpl.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/Bmv2PreGroupTranslatorImpl.java
new file mode 100644
index 0000000..6652ee0
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/Bmv2PreGroupTranslatorImpl.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.drivers.bmv2.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Sets;
+import org.onosproject.core.GroupId;
+import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreGroup;
+import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreJsonGroups;
+import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreNode;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupDescription;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Implementation of BMv2 PRE group translation logic.
+ */
+public final class Bmv2PreGroupTranslatorImpl {
+
+ private static final int BMV2_PORT_MAP_SIZE = 256;
+
+ //hidden constructor
+ private Bmv2PreGroupTranslatorImpl() {
+ }
+
+ /**
+ * Returns a BMv2 PRE group equivalent to given group.
+ *
+ * @param group group
+ * @return a BMv2 PRE group
+ */
+ public static Bmv2PreGroup translate(Group group) {
+ if (!group.type().equals(GroupDescription.Type.ALL)) {
+ throw new RuntimeException("Unable to translate the group to BMv2 PRE group." +
+ "A BMv2 PRE group is to be of ALL type. GroupId="
+ + group.id());
+ }
+
+ Bmv2PreGroup.Bmv2PreGroupBuilder bmv2PreGroupBuilder = Bmv2PreGroup.builder();
+ bmv2PreGroupBuilder.withGroupId(group.id().id());
+ // RID is generated per an MG since L2 broadcast can be considered as a MG that have a single RID.
+ // for simplicity RID will be assigned to zero for any node in an MG.
+ int replicationId = 0;
+
+ Set<PortNumber> outPorts = Sets.newHashSet();
+ group.buckets().buckets().forEach(groupBucket -> {
+ //get output instructions of the bucket
+ Set<Instructions.OutputInstruction> outputInstructions = getOutputInstructions(groupBucket.treatment());
+ //check validity of output instructions
+ checkOutputInstructions(group.id(), outputInstructions);
+ outPorts.add(outputInstructions.iterator().next().port());
+ });
+ validatePorts(outPorts);
+ bmv2PreGroupBuilder.addNode(Bmv2PreNode.builder()
+ .withRid(replicationId)
+ .withPortMap(buildPortMap(outPorts))
+ .build());
+ return bmv2PreGroupBuilder.build();
+ }
+
+ /**
+ * Converts a PRE group list in JSON notation to list of Bmv2PreGroups.
+ *
+ * @param groupListJson group list string ing JSON notation
+ * @return list of Bmv2PreGroups
+ * @throws IOException in case JSON string can not be parsed
+ */
+ public static List<Bmv2PreGroup> translate(String groupListJson) throws IOException {
+ List<Bmv2PreGroup> preGroups = new ArrayList<>();
+ if (groupListJson == null) {
+ return preGroups;
+ }
+ ObjectMapper mapper = new ObjectMapper();
+ Bmv2PreJsonGroups bmv2PreJsonGroups = mapper.readValue(groupListJson, Bmv2PreJsonGroups.class);
+
+ Bmv2PreGroup.Bmv2PreGroupBuilder bmv2PreGroupBuilder;
+ for (Bmv2PreJsonGroups.Mgrp mgrp : bmv2PreJsonGroups.mgrps) {
+
+ bmv2PreGroupBuilder = Bmv2PreGroup.builder();
+ bmv2PreGroupBuilder.withGroupId(mgrp.id);
+
+ for (int l1handleId : mgrp.l1handles) {
+ Bmv2PreJsonGroups.L1Handle l1handle = getL1Handle(l1handleId, bmv2PreJsonGroups.l1handles);
+ if (l1handle == null) {
+ continue;
+ }
+ Bmv2PreJsonGroups.L2Handle l2handle = getL2Handle(l1handle.l2handle, bmv2PreJsonGroups.l2handles);
+ if (l2handle == null) {
+ continue;
+ }
+ bmv2PreGroupBuilder.addNode(Bmv2PreNode.builder()
+ .withRid(l1handle.rid)
+ .withPortMap(buildPortMap(l2handle.ports))
+ .withL1Handle(l1handleId)
+ .build());
+ }
+ preGroups.add(bmv2PreGroupBuilder.build());
+ }
+ return preGroups;
+ }
+
+ /**
+ * Retrieves L1Handle object pointed by given L1 handle pointer from L1 handles list.
+ *
+ * @param l1handlePointer pointer to a L1 handle
+ * @param l1handles list of L1 handles
+ * @return L1 handle object if exists; null otherwise
+ */
+ private static Bmv2PreJsonGroups.L1Handle getL1Handle(int l1handlePointer,
+ Bmv2PreJsonGroups.L1Handle[] l1handles) {
+ for (Bmv2PreJsonGroups.L1Handle l1Handle : l1handles) {
+ if (l1handlePointer == l1Handle.handle) {
+ return l1Handle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves L2Handle object pointed by given L2 handle pointer from L2 handles list.
+ *
+ * @param l2handlePointer pointer to a L2 handle
+ * @param l2handles list of L2 handles
+ * @return L2 handle object if exists; null otherwise
+ */
+ private static Bmv2PreJsonGroups.L2Handle getL2Handle(int l2handlePointer,
+ Bmv2PreJsonGroups.L2Handle[] l2handles) {
+ for (Bmv2PreJsonGroups.L2Handle l2handle : l2handles) {
+ if (l2handlePointer == l2handle.handle) {
+ return l2handle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Builds a port map string composing of 1 and 0s.
+ * BMv2 API reads a port map from most significant to least significant char.
+ * Index of 1s indicates port numbers.
+ * For example, if port numbers are 4,3 and 1, the generated port map string looks like 11010.
+ *
+ * @param outPorts set of output port numbers
+ * @return port map string
+ */
+ private static String buildPortMap(Set<PortNumber> outPorts) {
+ //Sorts port numbers in descending order
+ SortedSet<PortNumber> outPortsSorted = new TreeSet<>((o1, o2) -> (int) (o2.toLong() - o1.toLong()));
+ outPortsSorted.addAll(outPorts);
+ PortNumber biggestPort = outPortsSorted.iterator().next();
+ int portMapSize = (int) biggestPort.toLong() + 1;
+ //init and fill port map with zero characters
+ char[] portMap = new char[portMapSize];
+ Arrays.fill(portMap, '0');
+ //fill in the ports with 1
+ outPortsSorted.forEach(portNumber -> portMap[portMapSize - (int) portNumber.toLong() - 1] = '1');
+ return String.valueOf(portMap);
+ }
+
+ /**
+ * Builds a port map string composing of 1 and 0s.
+ * BMv2 API reads a port map from most significant to least significant char.
+ * The index of 1s indicates port numbers.
+ * For example, if port numbers are 4,3 and 1, the generated port map string looks like 11010.
+ *
+ * @param portArr array of output port numbers
+ * @return port map string
+ */
+ private static String buildPortMap(int[] portArr) {
+ Set<PortNumber> outPorts = new HashSet<>();
+ for (int port : portArr) {
+ outPorts.add(PortNumber.portNumber(port));
+ }
+ return buildPortMap(outPorts);
+ }
+
+ /**
+ * Retrieves output instructions out of the instruction set of the given traffic treatment.
+ *
+ * @param trafficTreatment
+ * @return set of output instructions
+ */
+ private static Set<Instructions.OutputInstruction> getOutputInstructions(TrafficTreatment trafficTreatment) {
+ if (trafficTreatment == null ||
+ trafficTreatment.allInstructions() == null) {
+ return Sets.newHashSet();
+ }
+ Set<Instructions.OutputInstruction> resultList = Sets.newHashSet();
+ trafficTreatment.allInstructions().forEach(instruction -> {
+ if (instruction instanceof Instructions.OutputInstruction) {
+ resultList.add((Instructions.OutputInstruction) instruction);
+ }
+ });
+ return resultList;
+ }
+
+ /**
+ * Checks validity of output instructions of a bucket.
+ * A bucket of a an ALL group must only have one output instruction.
+ * Other conditions can not pass the validation.
+ *
+ * @param groupId group identifier
+ * @param outputInstructions set of output instructions
+ * @throws RuntimeException if the instructions can not be validated
+ */
+ private static void checkOutputInstructions(GroupId groupId,
+ Set<Instructions.OutputInstruction> outputInstructions) {
+ if (outputInstructions.isEmpty()) {
+ throw new RuntimeException(String.format("Group bucket contains no output instruction. GroupId=%s",
+ groupId));
+ }
+ if (outputInstructions.size() != 1) {
+ throw new RuntimeException(String.format("Group bucket contains more than one output instructions. " +
+ "Only one is supported. GroupId=%s", groupId));
+ }
+ }
+
+ /**
+ * Checks whether a port number is a valid physical BMv2 port or not.
+ * If not, throws RuntimeException; does nothing otherwise.
+ *
+ * @param portNumber port number
+ * @throws RuntimeException iff the port number can not be validated
+ */
+ private static void validatePort(PortNumber portNumber) {
+ if (portNumber.toLong() < 0 || portNumber.toLong() >= BMV2_PORT_MAP_SIZE) {
+ throw new RuntimeException(String.format("Port number %d is not a valid BMv2 physical port number." +
+ "Valid port range is [0,255]", portNumber));
+ }
+ }
+
+ /**
+ * Checks whether a port number is a valid physical BMv2 port or not.
+ * If not, throws RuntimeException; does nothing otherwise.
+ *
+ * @param portNumbers port number set
+ * @throws RuntimeException iff a port number can not be validated
+ */
+ private static void validatePorts(Set<PortNumber> portNumbers) {
+ //validate one by one
+ for (PortNumber portNumber : portNumbers) {
+ validatePort(portNumber);
+ }
+ }
+}
\ No newline at end of file
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/package-info.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/package-info.java
new file mode 100644
index 0000000..5d03c9f
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * Translator implementation for Group to BMv2 PRE Group translation logic.
+ */
+package org.onosproject.drivers.bmv2.impl;
\ No newline at end of file