Implemented convenient builders of BMv2 extension selectors and
treatments.
Match and action parameters can now be built from primitive data types
(short, int, long or byte[]) which are then casted automatically
according to a given BMv2 configuration. Also, simplified demo
applications code / structure.
Change-Id: Ia5bebf62301c73c0b20cf6a4ddfb74165889106f
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java
index e05803c..a9d3ed8 100644
--- a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Action.java
@@ -37,7 +37,7 @@
private final String name;
private final List<ImmutableByteSequence> parameters;
- private Bmv2Action(String name, List<ImmutableByteSequence> parameters) {
+ protected Bmv2Action(String name, List<ImmutableByteSequence> parameters) {
// hide constructor
this.name = name;
this.parameters = parameters;
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
index cd8ec77..d9d9d0e 100644
--- a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
@@ -19,18 +19,29 @@
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
import org.onlab.util.KryoNamespace;
+import org.onosproject.bmv2.api.context.Bmv2Configuration;
+import org.onosproject.bmv2.api.context.Bmv2FieldTypeModel;
+import org.onosproject.bmv2.api.context.Bmv2HeaderModel;
+import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.flow.AbstractExtension;
import org.onosproject.net.flow.criteria.ExtensionSelector;
import org.onosproject.net.flow.criteria.ExtensionSelectorType;
+import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.*;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
/**
- * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters.
+ * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters. Match parameters are
+ * encoded using a map where the keys are expected to be field names formatted as {@code headerName.fieldName}
+ * (e.g. {@code ethernet.dstAddr}).
*/
@Beta
public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
@@ -47,13 +58,12 @@
private Map<String, Bmv2MatchParam> parameterMap;
/**
- * Creates a new BMv2 extension selector for the given match parameters map, where the keys are expected to be field
- * names formatted as headerName.fieldMemberName (e.g. ethernet.dstAddr).
+ * Creates a new BMv2 extension selector for the given match parameters map.
*
* @param paramMap a map
*/
- public Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
- this.parameterMap = checkNotNull(paramMap, "param map cannot be null");
+ private Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
+ this.parameterMap = paramMap;
}
/**
@@ -104,4 +114,340 @@
.add("parameterMap", parameterMap)
.toString();
}
+
+ /**
+ * Returns a new, empty BMv2 extension selector.
+ *
+ * @return a BMv2 extension treatment
+ */
+ public static Bmv2ExtensionSelector empty() {
+ return new Bmv2ExtensionSelector(null);
+ }
+
+ /**
+ * Returns a new builder of BMv2 extension selectors.
+ *
+ * @return a builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of BMv2 extension selectors.
+ * <p>
+ * Match parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
+ * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
+ * configuration.
+ */
+ public static final class Builder {
+
+ private final Map<Pair<String, String>, Bmv2MatchParam> parameterMap = Maps.newHashMap();
+ private Bmv2Configuration configuration;
+
+ private Builder() {
+ // ban constructor.
+ }
+
+ /**
+ * Sets the BMv2 configuration to format the match parameters of the selector.
+ *
+ * @param config a BMv2 configuration
+ * @return this
+ */
+ public Builder forConfiguration(Bmv2Configuration config) {
+ this.configuration = config;
+ return this;
+ }
+
+ /**
+ * Adds an exact match parameter for the given header field and value.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a short value
+ * @return this
+ */
+ public Builder matchExact(String headerName, String fieldName, short value) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ exact(value));
+ return this;
+ }
+
+ /**
+ * Adds an exact match parameter for the given header field and value.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value an integer value
+ * @return this
+ */
+ public Builder matchExact(String headerName, String fieldName, int value) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ exact(value));
+ return this;
+ }
+
+ /**
+ * Adds an exact match parameter for the given header field and value.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a long value
+ * @return this
+ */
+ public Builder matchExact(String headerName, String fieldName, long value) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ exact(value));
+ return this;
+ }
+
+ /**
+ * Adds an exact match parameter for the given header field and value.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a byte array
+ * @return this
+ */
+ public Builder matchExact(String headerName, String fieldName, byte[] value) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ exact(value));
+ return this;
+ }
+
+ /**
+ * Adds a ternary match parameter for the given header field, value and mask.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a short value
+ * @param mask a short value
+ * @return this
+ */
+ public Builder matchTernary(String headerName, String fieldName, short value, short mask) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ ternary(value, mask));
+ return this;
+ }
+ /**
+ * Adds a ternary match parameter for the given header field, value and mask.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value an integer value
+ * @param mask an integer value
+ * @return this
+ */
+ public Builder matchTernary(String headerName, String fieldName, int value, int mask) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ ternary(value, mask));
+ return this;
+ }
+ /**
+ * Adds a ternary match parameter for the given header field, value and mask.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a long value
+ * @param mask a long value
+ * @return this
+ */
+ public Builder matchTernary(String headerName, String fieldName, long value, long mask) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ ternary(value, mask));
+ return this;
+ }
+ /**
+ * Adds a ternary match parameter for the given header field, value and mask.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a byte array
+ * @param mask a byte array
+ * @return this
+ */
+ public Builder matchTernary(String headerName, String fieldName, byte[] value, byte[] mask) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ ternary(value, mask));
+ return this;
+ }
+
+ /**
+ * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a short value
+ * @param prefixLength an integer value
+ * @return this
+ */
+ public Builder matchLpm(String headerName, String fieldName, short value, int prefixLength) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ lpm(value, prefixLength));
+ return this;
+ }
+ /**
+ * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value an integer value
+ * @param prefixLength an integer value
+ * @return this
+ */
+ public Builder matchLpm(String headerName, String fieldName, int value, int prefixLength) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ lpm(value, prefixLength));
+ return this;
+ }
+ /**
+ * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a long value
+ * @param prefixLength an integer value
+ * @return this
+ */
+ public Builder matchLpm(String headerName, String fieldName, long value, int prefixLength) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ lpm(value, prefixLength));
+ return this;
+ }
+ /**
+ * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param value a byte array
+ * @param prefixLength an integer value
+ * @return this
+ */
+ public Builder matchLpm(String headerName, String fieldName, byte[] value, int prefixLength) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ lpm(value, prefixLength));
+ return this;
+ }
+
+ /**
+ * Adds a valid match parameter for the given header field.
+ *
+ * @param headerName a string value
+ * @param fieldName a string value
+ * @param flag a boolean value
+ * @return this
+ */
+ public Builder matchValid(String headerName, String fieldName, boolean flag) {
+ parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
+ checkNotNull(fieldName, "field name cannot be null")),
+ new Bmv2ValidMatchParam(flag));
+ return this;
+ }
+
+ /**
+ * Returns a new BMv2 extension selector.
+ *
+ * @return a BMv2 extension selector
+ * @throws NullPointerException if a given header or field name is not defined in the given configuration
+ * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
+ * when trying to fit an integer value into a smaller, fixed-length parameter
+ * produces overflow.
+ */
+ public Bmv2ExtensionSelector build() {
+ checkNotNull(configuration, "configuration cannot be null");
+ checkState(parameterMap.size() > 0, "parameter map cannot be empty");
+
+ final Map<String, Bmv2MatchParam> newParameterMap = Maps.newHashMap();
+
+ for (Pair<String, String> key : parameterMap.keySet()) {
+
+ String headerName = key.getLeft();
+ String fieldName = key.getRight();
+
+ Bmv2HeaderModel headerModel = configuration.header(headerName);
+ checkNotNull(headerModel, "no such a header in configuration", headerName);
+
+ Bmv2FieldTypeModel fieldModel = headerModel.type().field(fieldName);
+ checkNotNull(fieldModel, "no such a field in configuration", key);
+
+ int bitWidth = fieldModel.bitWidth();
+
+ Bmv2MatchParam oldParam = parameterMap.get(key);
+ Bmv2MatchParam newParam = null;
+
+ try {
+ switch (oldParam.type()) {
+ case EXACT:
+ Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) oldParam;
+ newParam = new Bmv2ExactMatchParam(fitByteSequence(e.value(), bitWidth));
+ break;
+ case TERNARY:
+ Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) oldParam;
+ newParam = new Bmv2TernaryMatchParam(fitByteSequence(t.value(), bitWidth),
+ fitByteSequence(t.mask(), bitWidth));
+ break;
+ case LPM:
+ Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) oldParam;
+ checkArgument(l.prefixLength() <= bitWidth, "LPM parameter has prefix length too long",
+ key);
+ newParam = new Bmv2LpmMatchParam(fitByteSequence(l.value(), bitWidth),
+ l.prefixLength());
+ break;
+ case VALID:
+ newParam = oldParam;
+ break;
+ default:
+ throw new RuntimeException("Match parameter type not supported: " + oldParam.type());
+ }
+ } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
+ throw new IllegalArgumentException(e.getMessage() + " [" + key + "]");
+ }
+ // FIXME: should put the pair object instead of building a new string for the key.
+ newParameterMap.put(headerName + "." + fieldName, newParam);
+ }
+
+ return new Bmv2ExtensionSelector(newParameterMap);
+ }
+
+ private static Bmv2MatchParam exact(Object value) {
+ return new Bmv2ExactMatchParam(copyFrom(bb(value)));
+ }
+
+ private static Bmv2MatchParam ternary(Object value, Object mask) {
+ return new Bmv2TernaryMatchParam(copyFrom(bb(value)), copyFrom(bb(mask)));
+ }
+
+ private static Bmv2MatchParam lpm(Object value, int prefixLength) {
+ return new Bmv2LpmMatchParam(copyFrom(bb(value)), prefixLength);
+ }
+
+ private static ByteBuffer bb(Object value) {
+ if (value instanceof Short) {
+ return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
+ } else if (value instanceof Integer) {
+ return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
+ } else if (value instanceof Long) {
+ return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
+ } else if (value instanceof byte[]) {
+ byte[] bytes = (byte[]) value;
+ return ByteBuffer.allocate(bytes.length).put(bytes);
+ } else {
+ // Never here.
+ return null;
+ }
+ }
+ }
}
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
index b2f65f3..b2a9ba3 100644
--- a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
@@ -19,11 +19,26 @@
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import org.onlab.util.ImmutableByteSequence;
import org.onlab.util.KryoNamespace;
+import org.onosproject.bmv2.api.context.Bmv2ActionModel;
+import org.onosproject.bmv2.api.context.Bmv2Configuration;
+import org.onosproject.bmv2.api.context.Bmv2RuntimeDataModel;
+import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.flow.AbstractExtension;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
/**
@@ -40,7 +55,7 @@
*
* @param action an action
*/
- public Bmv2ExtensionTreatment(Bmv2Action action) {
+ private Bmv2ExtensionTreatment(Bmv2Action action) {
this.action = action;
}
@@ -91,4 +106,167 @@
.add("action", action)
.toString();
}
+
+ /**
+ * Returns a new, empty BMv2 extension treatment.
+ *
+ * @return a BMv2 extension treatment
+ */
+ public static Bmv2ExtensionTreatment empty() {
+ return new Bmv2ExtensionTreatment(null);
+ }
+
+ /**
+ * Returns a new BMv2 extension treatment builder.
+ *
+ * @return a builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder of BMv2 extension treatments.
+ *
+ * BMv2 action parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
+ * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
+ * configuration.
+ */
+ public static final class Builder {
+ private Bmv2Configuration configuration;
+ private String actionName;
+ private final Map<String, ImmutableByteSequence> parameters = Maps.newHashMap();
+
+ private Builder() {
+ // Ban constructor.
+ }
+
+ /**
+ * Sets the BMv2 configuration to format the action parameters.
+ *
+ * @param config a BMv2 configuration
+ * @return this
+ */
+ public Builder forConfiguration(Bmv2Configuration config) {
+ this.configuration = config;
+ return this;
+ }
+
+ /**
+ * Sets the action name.
+ *
+ * @param actionName a string value
+ * @return this
+ */
+ public Builder setActionName(String actionName) {
+ this.actionName = actionName;
+ return this;
+ }
+
+ /**
+ * Adds an action parameter.
+ *
+ * @param parameterName a string value
+ * @param value a short value
+ * @return this
+ */
+ public Builder addParameter(String parameterName, short value) {
+ this.parameters.put(parameterName, copyFrom(bb(value)));
+ return this;
+ }
+
+ /**
+ * Adds an action parameter.
+ *
+ * @param parameterName a string value
+ * @param value an integer value
+ * @return this
+ */
+ public Builder addParameter(String parameterName, int value) {
+ this.parameters.put(parameterName, copyFrom(bb(value)));
+ return this;
+ }
+
+ /**
+ * Adds an action parameter.
+ *
+ * @param parameterName a string value
+ * @param value a long value
+ * @return this
+ */
+ public Builder addParameter(String parameterName, long value) {
+ this.parameters.put(parameterName, copyFrom(bb(value)));
+ return this;
+ }
+
+ /**
+ * Adds an action parameter.
+ *
+ * @param parameterName a string value
+ * @param value a byte array
+ * @return this
+ */
+ public Builder addParameter(String parameterName, byte[] value) {
+ this.parameters.put(parameterName, copyFrom(bb(value)));
+ return this;
+ }
+
+ /**
+ * Returns a new BMv2 extension treatment.
+ *
+ * @return a BMv2 extension treatment
+ * @throws NullPointerException if the given action or parameter names are not defined in the given
+ * configuration
+ * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
+ * when trying to fit an integer value into a smaller, fixed-length parameter
+ * produces overflow.
+ */
+ public Bmv2ExtensionTreatment build() {
+ checkNotNull(configuration, "configuration cannot be null");
+ checkNotNull(actionName, "action name cannot be null");
+
+ Bmv2ActionModel actionModel = configuration.action(actionName);
+
+ checkNotNull(actionModel, "no such an action in configuration", actionName);
+ checkArgument(actionModel.runtimeDatas().size() == parameters.size(),
+ "invalid number of parameters", actionName);
+
+ List<ImmutableByteSequence> newParameters = new ArrayList<>(parameters.size());
+
+ for (String parameterName : parameters.keySet()) {
+ Bmv2RuntimeDataModel runtimeData = actionModel.runtimeData(parameterName);
+ checkNotNull(runtimeData, "no such an action parameter in configuration",
+ actionName + "->" + runtimeData.name());
+ int bitWidth = runtimeData.bitWidth();
+ try {
+ ImmutableByteSequence newSequence = fitByteSequence(parameters.get(parameterName), bitWidth);
+ int idx = actionModel.runtimeDatas().indexOf(runtimeData);
+ newParameters.add(idx, newSequence);
+ } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
+ throw new IllegalArgumentException(e.getMessage() +
+ " [" + actionName + "->" + runtimeData.name() + "]");
+ }
+ }
+
+ return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters));
+ }
+
+
+
+ private static ByteBuffer bb(Object value) {
+ if (value instanceof Short) {
+ return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
+ } else if (value instanceof Integer) {
+ return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
+ } else if (value instanceof Long) {
+ return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
+ } else if (value instanceof byte[]) {
+ byte[] bytes = (byte[]) value;
+ return ByteBuffer.allocate(bytes.length).put(bytes);
+ } else {
+ // Never here.
+ return null;
+ }
+ }
+ }
}
diff --git a/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionBuilderTest.java b/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionBuilderTest.java
new file mode 100644
index 0000000..2c9cc95
--- /dev/null
+++ b/protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionBuilderTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2.api.runtime;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.bmv2.api.context.Bmv2Configuration;
+import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class Bmv2ExtensionBuilderTest {
+
+ private Bmv2Configuration config;
+
+ @Before
+ public void setUp() throws Exception {
+ JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
+ this.getClass().getResourceAsStream("/simple.json")))).asObject();
+ config = Bmv2DefaultConfiguration.parse(json);
+ }
+
+ @Test
+ public void testExtensionSelector() throws Exception {
+
+ Bmv2ExtensionSelector extSelectorExact = Bmv2ExtensionSelector.builder()
+ .forConfiguration(config)
+ .matchExact("standard_metadata", "ingress_port", (short) 255)
+ .matchExact("ethernet", "etherType", 512)
+ .matchExact("ethernet", "dstAddr", 1024L)
+ .matchExact("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes())
+ .build();
+
+ Bmv2ExtensionSelector extSelectorTernary = Bmv2ExtensionSelector.builder()
+ .forConfiguration(config)
+ .matchTernary("standard_metadata", "ingress_port", (short) 255, (short) 255)
+ .matchTernary("ethernet", "etherType", 512, 512)
+ .matchTernary("ethernet", "dstAddr", 1024L, 1024L)
+ .matchTernary("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), MacAddress.NONE.toBytes())
+ .build();
+
+ Bmv2ExtensionSelector extSelectorLpm = Bmv2ExtensionSelector.builder()
+ .forConfiguration(config)
+ .matchLpm("standard_metadata", "ingress_port", (short) 255, 1)
+ .matchLpm("ethernet", "etherType", 512, 2)
+ .matchLpm("ethernet", "dstAddr", 1024L, 3)
+ .matchLpm("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), 4)
+ .build();
+
+ Bmv2ExtensionSelector extSelectorValid = Bmv2ExtensionSelector.builder()
+ .forConfiguration(config)
+ .matchValid("standard_metadata", "ingress_port", true)
+ .matchValid("ethernet", "etherType", true)
+ .matchValid("ethernet", "dstAddr", false)
+ .matchValid("ethernet", "srcAddr", false)
+ .build();
+
+ assertThat(extSelectorExact.parameterMap().size(), is(4));
+ assertThat(extSelectorTernary.parameterMap().size(), is(4));
+ assertThat(extSelectorLpm.parameterMap().size(), is(4));
+ assertThat(extSelectorValid.parameterMap().size(), is(4));
+
+ // TODO add more tests, e.g. check for byte sequences content and size.
+ }
+
+ @Test
+ public void testExtensionTreatment() throws Exception {
+
+ Bmv2ExtensionTreatment treatment = Bmv2ExtensionTreatment.builder()
+ .forConfiguration(config)
+ .setActionName("set_egress_port")
+ .addParameter("port", 1)
+ .build();
+
+ assertThat(treatment.action().parameters().size(), is(1));
+
+ // TODO add more tests, e.g. check for byte sequences content and size.
+ }
+}
\ No newline at end of file