Carmelo Cascone
Committed by Jonathan Hart

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
...@@ -19,11 +19,14 @@ package org.onosproject.bmv2.demo.app.ecmp; ...@@ -19,11 +19,14 @@ package org.onosproject.bmv2.demo.app.ecmp;
19 import com.eclipsesource.json.Json; 19 import com.eclipsesource.json.Json;
20 import com.eclipsesource.json.JsonObject; 20 import com.eclipsesource.json.JsonObject;
21 import com.google.common.collect.Lists; 21 import com.google.common.collect.Lists;
22 +import com.google.common.collect.Maps;
22 import org.apache.commons.lang3.tuple.Pair; 23 import org.apache.commons.lang3.tuple.Pair;
23 import org.apache.felix.scr.annotations.Component; 24 import org.apache.felix.scr.annotations.Component;
24 import org.onosproject.bmv2.api.context.Bmv2Configuration; 25 import org.onosproject.bmv2.api.context.Bmv2Configuration;
25 import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration; 26 import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
26 import org.onosproject.bmv2.api.context.Bmv2DeviceContext; 27 import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
28 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
29 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
27 import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp; 30 import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp;
28 import org.onosproject.net.DeviceId; 31 import org.onosproject.net.DeviceId;
29 import org.onosproject.net.Host; 32 import org.onosproject.net.Host;
...@@ -46,14 +49,13 @@ import java.io.InputStreamReader; ...@@ -46,14 +49,13 @@ import java.io.InputStreamReader;
46 import java.util.Collection; 49 import java.util.Collection;
47 import java.util.Iterator; 50 import java.util.Iterator;
48 import java.util.List; 51 import java.util.List;
52 +import java.util.Map;
49 import java.util.Set; 53 import java.util.Set;
50 import java.util.stream.Collectors; 54 import java.util.stream.Collectors;
51 55
52 import static java.util.stream.Collectors.toSet; 56 import static java.util.stream.Collectors.toSet;
53 import static org.onlab.packet.EthType.EtherType.IPV4; 57 import static org.onlab.packet.EthType.EtherType.IPV4;
54 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpGroupTreatmentBuilder.groupIdOf; 58 +import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.*;
55 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.ECMP_GROUP_TABLE;
56 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.TABLE0;
57 59
58 /** 60 /**
59 * Implementation of an upgradable fabric app for the ECMP configuration. 61 * Implementation of an upgradable fabric app for the ECMP configuration.
...@@ -69,6 +71,8 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -69,6 +71,8 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
69 private static final EcmpInterpreter ECMP_INTERPRETER = new EcmpInterpreter(); 71 private static final EcmpInterpreter ECMP_INTERPRETER = new EcmpInterpreter();
70 protected static final Bmv2DeviceContext ECMP_CONTEXT = new Bmv2DeviceContext(ECMP_CONFIGURATION, ECMP_INTERPRETER); 72 protected static final Bmv2DeviceContext ECMP_CONTEXT = new Bmv2DeviceContext(ECMP_CONFIGURATION, ECMP_INTERPRETER);
71 73
74 + private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
75 +
72 public EcmpFabricApp() { 76 public EcmpFabricApp() {
73 super(APP_NAME, MODEL_NAME, ECMP_CONTEXT); 77 super(APP_NAME, MODEL_NAME, ECMP_CONTEXT);
74 } 78 }
...@@ -211,10 +215,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -211,10 +215,7 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
211 Iterator<PortNumber> portIterator = fabricPorts.iterator(); 215 Iterator<PortNumber> portIterator = fabricPorts.iterator();
212 List<FlowRule> rules = Lists.newArrayList(); 216 List<FlowRule> rules = Lists.newArrayList();
213 for (short i = 0; i < groupSize; i++) { 217 for (short i = 0; i < groupSize; i++) {
214 - ExtensionSelector extSelector = new EcmpGroupTableSelectorBuilder() 218 + ExtensionSelector extSelector = buildEcmpSelector(groupId, i);
215 - .withGroupId(groupId)
216 - .withSelector(i)
217 - .build();
218 FlowRule rule = flowRuleBuilder(deviceId, ECMP_GROUP_TABLE) 219 FlowRule rule = flowRuleBuilder(deviceId, ECMP_GROUP_TABLE)
219 .withSelector( 220 .withSelector(
220 DefaultTrafficSelector.builder() 221 DefaultTrafficSelector.builder()
...@@ -228,14 +229,37 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -228,14 +229,37 @@ public class EcmpFabricApp extends AbstractUpgradableFabricApp {
228 rules.add(rule); 229 rules.add(rule);
229 } 230 }
230 231
231 - ExtensionTreatment extTreatment = new EcmpGroupTreatmentBuilder() 232 + ExtensionTreatment extTreatment = buildEcmpTreatment(groupId, groupSize);
232 - .withGroupId(groupId)
233 - .withGroupSize(groupSize)
234 - .build();
235 233
236 return Pair.of(extTreatment, rules); 234 return Pair.of(extTreatment, rules);
237 } 235 }
238 236
237 + private Bmv2ExtensionTreatment buildEcmpTreatment(int groupId, int groupSize) {
238 + return Bmv2ExtensionTreatment.builder()
239 + .forConfiguration(ECMP_CONTEXT.configuration())
240 + .setActionName(ECMP_GROUP)
241 + .addParameter(GROUP_ID, groupId)
242 + .addParameter(GROUP_SIZE, groupSize)
243 + .build();
244 + }
245 +
246 + private Bmv2ExtensionSelector buildEcmpSelector(int groupId, int selector) {
247 + return Bmv2ExtensionSelector.builder()
248 + .forConfiguration(ECMP_CONTEXT.configuration())
249 + .matchExact(ECMP_METADATA, GROUP_ID, groupId)
250 + .matchExact(ECMP_METADATA, SELECTOR, selector)
251 + .build();
252 + }
253 +
254 +
255 + public int groupIdOf(DeviceId deviceId, Set<PortNumber> ports) {
256 + DEVICE_GROUP_ID_MAP.putIfAbsent(deviceId, Maps.newHashMap());
257 + // Counts the number of unique portNumber sets for each deviceId.
258 + // Each distinct set of portNumbers will have a unique ID.
259 + return DEVICE_GROUP_ID_MAP.get(deviceId).computeIfAbsent(ports, (pp) ->
260 + (short) (DEVICE_GROUP_ID_MAP.get(deviceId).size() + 1));
261 + }
262 +
239 private static Bmv2Configuration loadConfiguration() { 263 private static Bmv2Configuration loadConfiguration() {
240 try { 264 try {
241 JsonObject json = Json.parse(new BufferedReader(new InputStreamReader( 265 JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
......
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.bmv2.demo.app.ecmp;
18 -
19 -import com.google.common.collect.ImmutableMap;
20 -import org.onlab.util.ImmutableByteSequence;
21 -import org.onosproject.bmv2.api.context.Bmv2HeaderTypeModel;
22 -import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
23 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
24 -import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
25 -import org.onosproject.net.flow.criteria.ExtensionSelector;
26 -
27 -import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
28 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpFabricApp.ECMP_CONTEXT;
29 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.*;
30 -
31 -/**
32 - * Builder of ECMP group table extension selector.
33 - */
34 -public class EcmpGroupTableSelectorBuilder {
35 -
36 - private int groupId;
37 - private int selector;
38 -
39 - /**
40 - * Sets the ECMP group ID.
41 - *
42 - * @param groupId an integer value
43 - * @return this
44 - */
45 - public EcmpGroupTableSelectorBuilder withGroupId(int groupId) {
46 - this.groupId = groupId;
47 - return this;
48 - }
49 -
50 - /**
51 - * Sets the ECMP selector.
52 - *
53 - * @param selector an integer value
54 - * @return this
55 - */
56 - public EcmpGroupTableSelectorBuilder withSelector(int selector) {
57 - this.selector = selector;
58 - return this;
59 - }
60 -
61 - /**
62 - * Returns a new extension selector.
63 - *
64 - * @return an extension selector
65 - */
66 - public ExtensionSelector build() {
67 - Bmv2HeaderTypeModel headerTypeModel = ECMP_CONTEXT.configuration().headerType(ECMP_METADATA_T);
68 - int groupIdBitWidth = headerTypeModel.field(GROUP_ID).bitWidth();
69 - int selectorBitWidth = headerTypeModel.field(SELECTOR).bitWidth();
70 -
71 - try {
72 - ImmutableByteSequence groupIdBs = fitByteSequence(ImmutableByteSequence.copyFrom(groupId),
73 - groupIdBitWidth);
74 - ImmutableByteSequence selectorBs = fitByteSequence(ImmutableByteSequence.copyFrom(selector),
75 - selectorBitWidth);
76 -
77 - Bmv2ExactMatchParam groupIdMatch = new Bmv2ExactMatchParam(groupIdBs);
78 - Bmv2ExactMatchParam hashMatch = new Bmv2ExactMatchParam(selectorBs);
79 -
80 - return new Bmv2ExtensionSelector(ImmutableMap.of(
81 - ECMP_METADATA + "." + GROUP_ID, groupIdMatch,
82 - ECMP_METADATA + "." + SELECTOR, hashMatch));
83 -
84 - } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
85 - throw new RuntimeException(e);
86 - }
87 - }
88 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.bmv2.demo.app.ecmp;
18 -
19 -import com.google.common.collect.Maps;
20 -import org.onlab.util.ImmutableByteSequence;
21 -import org.onosproject.bmv2.api.context.Bmv2ActionModel;
22 -import org.onosproject.bmv2.api.runtime.Bmv2Action;
23 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
24 -import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
25 -import org.onosproject.net.DeviceId;
26 -import org.onosproject.net.PortNumber;
27 -import org.onosproject.net.flow.instructions.ExtensionTreatment;
28 -
29 -import java.util.Map;
30 -import java.util.Set;
31 -
32 -import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
33 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpFabricApp.ECMP_CONTEXT;
34 -import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.*;
35 -
36 -/**
37 - * Builder of ECMP extension treatments.
38 - */
39 -public class EcmpGroupTreatmentBuilder {
40 -
41 - private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
42 - private int groupId;
43 - private int groupSize;
44 -
45 - /**
46 - * Sets the group ID.
47 - *
48 - * @param groupId an integer value
49 - * @return this
50 - */
51 - public EcmpGroupTreatmentBuilder withGroupId(int groupId) {
52 - this.groupId = groupId;
53 - return this;
54 - }
55 -
56 - /**
57 - * Sets the group size.
58 - *
59 - * @param groupSize an integer value
60 - * @return this
61 - */
62 - public EcmpGroupTreatmentBuilder withGroupSize(int groupSize) {
63 - this.groupSize = groupSize;
64 - return this;
65 - }
66 -
67 - /**
68 - * Returns a new extension treatment.
69 - *
70 - * @return an extension treatment
71 - */
72 - public ExtensionTreatment build() {
73 - Bmv2ActionModel actionModel = ECMP_CONTEXT.configuration().action(ECMP_GROUP);
74 - int groupIdBitWidth = actionModel.runtimeData(GROUP_ID).bitWidth();
75 - int groupSizeBitWidth = actionModel.runtimeData(GROUP_SIZE).bitWidth();
76 -
77 - try {
78 - ImmutableByteSequence groupIdBs = fitByteSequence(ImmutableByteSequence.copyFrom(groupId), groupIdBitWidth);
79 - ImmutableByteSequence groupSizeBs = fitByteSequence(ImmutableByteSequence.copyFrom(groupSize),
80 - groupSizeBitWidth);
81 -
82 - return new Bmv2ExtensionTreatment(Bmv2Action.builder()
83 - .withName(ECMP_GROUP)
84 - .addParameter(groupIdBs)
85 - .addParameter(groupSizeBs)
86 - .build());
87 -
88 - } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
89 - throw new RuntimeException(e);
90 - }
91 - }
92 -
93 - /**
94 - * Returns a group ID for the given device and set of ports.
95 - *
96 - * @param deviceId a device ID
97 - * @param ports a set of ports
98 - * @return an integer value
99 - */
100 - public static int groupIdOf(DeviceId deviceId, Set<PortNumber> ports) {
101 - DEVICE_GROUP_ID_MAP.putIfAbsent(deviceId, Maps.newHashMap());
102 - // Counts the number of unique portNumber sets for each deviceId.
103 - // Each distinct set of portNumbers will have a unique ID.
104 - return DEVICE_GROUP_ID_MAP.get(deviceId).computeIfAbsent(ports, (pp) ->
105 - (short) (DEVICE_GROUP_ID_MAP.get(deviceId).size() + 1));
106 - }
107 -}
...@@ -37,7 +37,6 @@ import static org.onosproject.net.flow.instructions.Instructions.OutputInstructi ...@@ -37,7 +37,6 @@ import static org.onosproject.net.flow.instructions.Instructions.OutputInstructi
37 */ 37 */
38 public class EcmpInterpreter implements Bmv2Interpreter { 38 public class EcmpInterpreter implements Bmv2Interpreter {
39 39
40 - protected static final String ECMP_METADATA_T = "ecmp_metadata_t";
41 protected static final String ECMP_METADATA = "ecmp_metadata"; 40 protected static final String ECMP_METADATA = "ecmp_metadata";
42 protected static final String SELECTOR = "selector"; 41 protected static final String SELECTOR = "selector";
43 protected static final String GROUP_ID = "groupId"; 42 protected static final String GROUP_ID = "groupId";
......
...@@ -18,6 +18,7 @@ package org.onosproject.bmv2.demo.app.wcmp; ...@@ -18,6 +18,7 @@ package org.onosproject.bmv2.demo.app.wcmp;
18 18
19 import com.eclipsesource.json.Json; 19 import com.eclipsesource.json.Json;
20 import com.eclipsesource.json.JsonObject; 20 import com.eclipsesource.json.JsonObject;
21 +import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.Lists; 22 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps; 23 import com.google.common.collect.Maps;
23 import com.google.common.collect.Sets; 24 import com.google.common.collect.Sets;
...@@ -30,6 +31,8 @@ import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration; ...@@ -30,6 +31,8 @@ import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
30 import org.onosproject.bmv2.api.context.Bmv2DeviceContext; 31 import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
31 import org.onosproject.bmv2.api.runtime.Bmv2Action; 32 import org.onosproject.bmv2.api.runtime.Bmv2Action;
32 import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent; 33 import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
34 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
35 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
33 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; 36 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
34 import org.onosproject.bmv2.api.service.Bmv2Controller; 37 import org.onosproject.bmv2.api.service.Bmv2Controller;
35 import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp; 38 import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp;
...@@ -50,18 +53,19 @@ import org.onosproject.net.topology.TopologyGraph; ...@@ -50,18 +53,19 @@ import org.onosproject.net.topology.TopologyGraph;
50 import java.io.BufferedReader; 53 import java.io.BufferedReader;
51 import java.io.IOException; 54 import java.io.IOException;
52 import java.io.InputStreamReader; 55 import java.io.InputStreamReader;
56 +import java.util.Arrays;
53 import java.util.Collection; 57 import java.util.Collection;
58 +import java.util.Collections;
54 import java.util.List; 59 import java.util.List;
55 import java.util.Map; 60 import java.util.Map;
56 import java.util.Set; 61 import java.util.Set;
57 import java.util.stream.Collectors; 62 import java.util.stream.Collectors;
58 63
64 +import static java.util.stream.Collectors.toList;
59 import static java.util.stream.Collectors.toSet; 65 import static java.util.stream.Collectors.toSet;
60 import static org.onlab.packet.EthType.EtherType.IPV4; 66 import static org.onlab.packet.EthType.EtherType.IPV4;
61 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpGroupTreatmentBuilder.groupIdOf; 67 +import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.roundToBytes;
62 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpGroupTreatmentBuilder.toPrefixLengths; 68 +import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.*;
63 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.TABLE0;
64 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.WCMP_GROUP_TABLE;
65 69
66 /** 70 /**
67 * Implementation of an upgradable fabric app for the WCMP configuration. 71 * Implementation of an upgradable fabric app for the WCMP configuration.
...@@ -79,6 +83,8 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -79,6 +83,8 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp {
79 private static final WcmpInterpreter WCMP_INTERPRETER = new WcmpInterpreter(); 83 private static final WcmpInterpreter WCMP_INTERPRETER = new WcmpInterpreter();
80 protected static final Bmv2DeviceContext WCMP_CONTEXT = new Bmv2DeviceContext(WCMP_CONFIGURATION, WCMP_INTERPRETER); 84 protected static final Bmv2DeviceContext WCMP_CONTEXT = new Bmv2DeviceContext(WCMP_CONFIGURATION, WCMP_INTERPRETER);
81 85
86 + private static final Map<DeviceId, Map<Map<PortNumber, Double>, Integer>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
87 +
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 private Bmv2Controller bmv2Controller; 89 private Bmv2Controller bmv2Controller;
84 90
...@@ -252,19 +258,11 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -252,19 +258,11 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp {
252 portNumbers.add(p); 258 portNumbers.add(p);
253 weights.add(w); 259 weights.add(w);
254 }); 260 });
255 - List<Integer> prefixLengths; 261 + List<Integer> prefixLengths = toPrefixLengths(weights);
256 - try {
257 - prefixLengths = toPrefixLengths(weights);
258 - } catch (WcmpGroupTreatmentBuilder.WcmpGroupException e) {
259 - throw new FlowRuleGeneratorException(e);
260 - }
261 262
262 List<FlowRule> rules = Lists.newArrayList(); 263 List<FlowRule> rules = Lists.newArrayList();
263 for (int i = 0; i < portNumbers.size(); i++) { 264 for (int i = 0; i < portNumbers.size(); i++) {
264 - ExtensionSelector extSelector = new WcmpGroupTableSelectorBuilder() 265 + ExtensionSelector extSelector = buildWcmpSelector(groupId, prefixLengths.get(i));
265 - .withGroupId(groupId)
266 - .withPrefixLength(prefixLengths.get(i))
267 - .build();
268 FlowRule rule = flowRuleBuilder(deviceId, WCMP_GROUP_TABLE) 266 FlowRule rule = flowRuleBuilder(deviceId, WCMP_GROUP_TABLE)
269 .withSelector(DefaultTrafficSelector.builder() 267 .withSelector(DefaultTrafficSelector.builder()
270 .extension(extSelector, deviceId) 268 .extension(extSelector, deviceId)
...@@ -277,11 +275,78 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp { ...@@ -277,11 +275,78 @@ public class WcmpFabricApp extends AbstractUpgradableFabricApp {
277 rules.add(rule); 275 rules.add(rule);
278 } 276 }
279 277
280 - ExtensionTreatment extTreatment = new WcmpGroupTreatmentBuilder().withGroupId(groupId).build(); 278 + ExtensionTreatment extTreatment = buildWcmpTreatment(groupId);
281 279
282 return Pair.of(extTreatment, rules); 280 return Pair.of(extTreatment, rules);
283 } 281 }
284 282
283 + private Bmv2ExtensionSelector buildWcmpSelector(int groupId, int prefixLength) {
284 + byte[] ones = new byte[roundToBytes(prefixLength)];
285 + Arrays.fill(ones, (byte) 0xFF);
286 + return Bmv2ExtensionSelector.builder()
287 + .forConfiguration(WCMP_CONTEXT.configuration())
288 + .matchExact(WCMP_META, GROUP_ID, groupId)
289 + .matchLpm(WCMP_META, SELECTOR, ones, prefixLength)
290 + .build();
291 + }
292 +
293 + private Bmv2ExtensionTreatment buildWcmpTreatment(int groupId) {
294 + return Bmv2ExtensionTreatment.builder()
295 + .forConfiguration(WCMP_CONTEXT.configuration())
296 + .setActionName(WCMP_GROUP)
297 + .addParameter(GROUP_ID, groupId)
298 + .build();
299 + }
300 +
301 + public int groupIdOf(DeviceId did, Map<PortNumber, Double> weightedPorts) {
302 + DEVICE_GROUP_ID_MAP.putIfAbsent(did, Maps.newHashMap());
303 + // Counts the number of unique portNumber sets for each device ID.
304 + // Each distinct set of portNumbers will have a unique ID.
305 + return DEVICE_GROUP_ID_MAP.get(did).computeIfAbsent(weightedPorts,
306 + (pp) -> DEVICE_GROUP_ID_MAP.get(did).size() + 1);
307 + }
308 +
309 + public List<Integer> toPrefixLengths(List<Double> weigths) {
310 +
311 + final double weightSum = weigths.stream()
312 + .mapToDouble(Double::doubleValue)
313 + .map(this::roundDouble)
314 + .sum();
315 +
316 + if (Math.abs(weightSum - 1) > 0.0001) {
317 + throw new RuntimeException("WCMP weights sum is expected to be 1, found was " + weightSum);
318 + }
319 +
320 + final int selectorBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(SELECTOR).bitWidth();
321 + final int availableBits = selectorBitWidth - 1;
322 +
323 + List<Long> prefixDiffs = weigths.stream().map(w -> Math.round(w * availableBits)).collect(toList());
324 +
325 + final long bitSum = prefixDiffs.stream().mapToLong(Long::longValue).sum();
326 + final long error = availableBits - bitSum;
327 +
328 + if (error != 0) {
329 + // Lazy intuition here is that the error can be absorbed by the longest prefixDiff with the minor impact.
330 + Long maxDiff = Collections.max(prefixDiffs);
331 + int idx = prefixDiffs.indexOf(maxDiff);
332 + prefixDiffs.remove(idx);
333 + prefixDiffs.add(idx, maxDiff + error);
334 + }
335 + List<Integer> prefixLengths = Lists.newArrayList();
336 +
337 + int prefix = 1;
338 + for (Long p : prefixDiffs) {
339 + prefixLengths.add(prefix);
340 + prefix += p;
341 + }
342 + return ImmutableList.copyOf(prefixLengths);
343 + }
344 +
345 + private double roundDouble(double n) {
346 + // 5 digits precision.
347 + return (double) Math.round(n * 100000d) / 100000d;
348 + }
349 +
285 private static Bmv2Configuration loadConfiguration() { 350 private static Bmv2Configuration loadConfiguration() {
286 try { 351 try {
287 JsonObject json = Json.parse(new BufferedReader(new InputStreamReader( 352 JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
......
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.bmv2.demo.app.wcmp;
18 -
19 -import com.google.common.collect.ImmutableMap;
20 -import org.onlab.util.ImmutableByteSequence;
21 -import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
22 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
23 -import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
24 -import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
25 -import org.onosproject.net.flow.criteria.ExtensionSelector;
26 -
27 -import static com.google.common.base.Preconditions.checkArgument;
28 -import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
29 -import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.roundToBytes;
30 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpFabricApp.WCMP_CONTEXT;
31 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.*;
32 -
33 -/**
34 - * Builder of WCMP group table extension selector.
35 - */
36 -public final class WcmpGroupTableSelectorBuilder {
37 -
38 - private int groupId;
39 - private int prefixLength;
40 -
41 - /**
42 - * Sets the WCMP group ID.
43 - *
44 - * @param groupId an integer value
45 - * @return this
46 - */
47 - public WcmpGroupTableSelectorBuilder withGroupId(int groupId) {
48 - this.groupId = groupId;
49 - return this;
50 - }
51 -
52 - /**
53 - * Sets the WCMP selector's prefix length.
54 - *
55 - * @param prefixLength an integer value
56 - * @return this
57 - */
58 - public WcmpGroupTableSelectorBuilder withPrefixLength(int prefixLength) {
59 - this.prefixLength = prefixLength;
60 - return this;
61 - }
62 -
63 - /**
64 - * Returns a new extension selector.
65 - *
66 - * @return an extension selector
67 - */
68 - public ExtensionSelector build() {
69 -
70 - final int selectorBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(SELECTOR).bitWidth();
71 - final int groupIdBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(GROUP_ID).bitWidth();
72 - final ImmutableByteSequence ones = ImmutableByteSequence.ofOnes(roundToBytes(selectorBitWidth));
73 -
74 - checkArgument(prefixLength >= 1 && prefixLength <= selectorBitWidth,
75 - "prefix length must be between 1 and " + selectorBitWidth);
76 - try {
77 - ImmutableByteSequence groupIdBs = fitByteSequence(ImmutableByteSequence.copyFrom(groupId), groupIdBitWidth);
78 - Bmv2ExactMatchParam groupIdMatch = new Bmv2ExactMatchParam(groupIdBs);
79 - Bmv2LpmMatchParam selectorMatch = new Bmv2LpmMatchParam(ones, prefixLength);
80 -
81 - return new Bmv2ExtensionSelector(ImmutableMap.of(
82 - WCMP_META + "." + GROUP_ID, groupIdMatch,
83 - WCMP_META + "." + SELECTOR, selectorMatch));
84 -
85 - } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
86 - throw new RuntimeException(e);
87 - }
88 - }
89 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.bmv2.demo.app.wcmp;
18 -
19 -import com.google.common.collect.ImmutableList;
20 -import com.google.common.collect.Lists;
21 -import com.google.common.collect.Maps;
22 -import org.onlab.util.ImmutableByteSequence;
23 -import org.onosproject.bmv2.api.runtime.Bmv2Action;
24 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
25 -import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
26 -import org.onosproject.net.DeviceId;
27 -import org.onosproject.net.PortNumber;
28 -import org.onosproject.net.flow.instructions.ExtensionTreatment;
29 -
30 -import java.util.Collections;
31 -import java.util.List;
32 -import java.util.Map;
33 -
34 -import static com.google.common.base.Preconditions.checkArgument;
35 -import static java.util.stream.Collectors.toList;
36 -import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
37 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpFabricApp.WCMP_CONTEXT;
38 -import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.*;
39 -
40 -/**
41 - * Builder of WCMP extension treatment.
42 - */
43 -public final class WcmpGroupTreatmentBuilder {
44 -
45 - private static final double MAX_ERROR = 0.0001;
46 -
47 - private static final Map<DeviceId, Map<Map<PortNumber, Double>, Integer>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
48 -
49 - private int groupId;
50 -
51 - /**
52 - * Sets the WCMP group ID.
53 - *
54 - * @param groupId an integer value
55 - * @return this
56 - */
57 - public WcmpGroupTreatmentBuilder withGroupId(int groupId) {
58 - this.groupId = groupId;
59 - return this;
60 - }
61 -
62 - /**
63 - * Returns a new extension treatment.
64 - *
65 - * @return an extension treatment
66 - */
67 - public ExtensionTreatment build() {
68 - checkArgument(groupId >= 0, "group id must be a non-zero positive integer");
69 - ImmutableByteSequence groupIdBs = ImmutableByteSequence.copyFrom(groupId);
70 - final int groupIdBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(GROUP_ID).bitWidth();
71 - try {
72 - groupIdBs = fitByteSequence(groupIdBs, groupIdBitWidth);
73 - return new Bmv2ExtensionTreatment(
74 - Bmv2Action.builder()
75 - .withName(WCMP_GROUP)
76 - .addParameter(groupIdBs)
77 - .build());
78 - } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
79 - throw new RuntimeException(e);
80 - }
81 - }
82 -
83 - public static int groupIdOf(DeviceId did, Map<PortNumber, Double> weightedPorts) {
84 - DEVICE_GROUP_ID_MAP.putIfAbsent(did, Maps.newHashMap());
85 - // Counts the number of unique portNumber sets for each device ID.
86 - // Each distinct set of portNumbers will have a unique ID.
87 - return DEVICE_GROUP_ID_MAP.get(did).computeIfAbsent(weightedPorts,
88 - (pp) -> DEVICE_GROUP_ID_MAP.get(did).size() + 1);
89 - }
90 -
91 - public static List<Integer> toPrefixLengths(List<Double> weigths) throws WcmpGroupException {
92 -
93 - double weightSum = weigths.stream()
94 - .mapToDouble(Double::doubleValue)
95 - .map(WcmpGroupTreatmentBuilder::roundDouble)
96 - .sum();
97 -
98 - if (Math.abs(weightSum - 1) > MAX_ERROR) {
99 - throw new WcmpGroupException("weights sum is expected to be 1, found was " + weightSum);
100 - }
101 -
102 - final int selectorBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(SELECTOR).bitWidth();
103 - final int availableBits = selectorBitWidth - 1;
104 -
105 - List<Long> prefixDiffs = weigths.stream().map(w -> Math.round(w * availableBits)).collect(toList());
106 -
107 - final long bitSum = prefixDiffs.stream().mapToLong(Long::longValue).sum();
108 - final long error = availableBits - bitSum;
109 -
110 - if (error != 0) {
111 - // Lazy intuition here is that the error can be absorbed by the longest prefixDiff with the minor impact.
112 - Long maxDiff = Collections.max(prefixDiffs);
113 - int idx = prefixDiffs.indexOf(maxDiff);
114 - prefixDiffs.remove(idx);
115 - prefixDiffs.add(idx, maxDiff + error);
116 - }
117 - List<Integer> prefixLengths = Lists.newArrayList();
118 -
119 - int prefix = 1;
120 - for (Long p : prefixDiffs) {
121 - prefixLengths.add(prefix);
122 - prefix += p;
123 - }
124 - return ImmutableList.copyOf(prefixLengths);
125 - }
126 -
127 - private static double roundDouble(double n) {
128 - // 5 digits precision.
129 - return (double) Math.round(n * 100000d) / 100000d;
130 - }
131 -
132 - public static class WcmpGroupException extends Exception {
133 - public WcmpGroupException(String s) {
134 - }
135 - }
136 -}
...@@ -22,8 +22,6 @@ import org.onosproject.net.driver.AbstractHandlerBehaviour; ...@@ -22,8 +22,6 @@ import org.onosproject.net.driver.AbstractHandlerBehaviour;
22 import org.onosproject.net.flow.criteria.ExtensionSelector; 22 import org.onosproject.net.flow.criteria.ExtensionSelector;
23 import org.onosproject.net.flow.criteria.ExtensionSelectorType; 23 import org.onosproject.net.flow.criteria.ExtensionSelectorType;
24 24
25 -import java.util.Collections;
26 -
27 import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS; 25 import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS;
28 26
29 /** 27 /**
...@@ -34,7 +32,7 @@ public class Bmv2ExtensionSelectorResolver extends AbstractHandlerBehaviour impl ...@@ -34,7 +32,7 @@ public class Bmv2ExtensionSelectorResolver extends AbstractHandlerBehaviour impl
34 @Override 32 @Override
35 public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) { 33 public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) {
36 if (type.equals(BMV2_MATCH_PARAMS.type())) { 34 if (type.equals(BMV2_MATCH_PARAMS.type())) {
37 - return new Bmv2ExtensionSelector(Collections.emptyMap()); 35 + return Bmv2ExtensionSelector.empty();
38 } 36 }
39 37
40 return null; 38 return null;
......
...@@ -32,7 +32,7 @@ public class Bmv2ExtensionTreatmentResolver extends AbstractHandlerBehaviour imp ...@@ -32,7 +32,7 @@ public class Bmv2ExtensionTreatmentResolver extends AbstractHandlerBehaviour imp
32 @Override 32 @Override
33 public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) { 33 public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
34 if (type.equals(BMV2_ACTION.type())) { 34 if (type.equals(BMV2_ACTION.type())) {
35 - return new Bmv2ExtensionTreatment(null); 35 + return Bmv2ExtensionTreatment.empty();
36 } 36 }
37 return null; 37 return null;
38 } 38 }
......
...@@ -37,7 +37,7 @@ public final class Bmv2Action { ...@@ -37,7 +37,7 @@ public final class Bmv2Action {
37 private final String name; 37 private final String name;
38 private final List<ImmutableByteSequence> parameters; 38 private final List<ImmutableByteSequence> parameters;
39 39
40 - private Bmv2Action(String name, List<ImmutableByteSequence> parameters) { 40 + protected Bmv2Action(String name, List<ImmutableByteSequence> parameters) {
41 // hide constructor 41 // hide constructor
42 this.name = name; 42 this.name = name;
43 this.parameters = parameters; 43 this.parameters = parameters;
......
...@@ -19,18 +19,29 @@ package org.onosproject.bmv2.api.runtime; ...@@ -19,18 +19,29 @@ package org.onosproject.bmv2.api.runtime;
19 import com.google.common.annotations.Beta; 19 import com.google.common.annotations.Beta;
20 import com.google.common.base.MoreObjects; 20 import com.google.common.base.MoreObjects;
21 import com.google.common.base.Objects; 21 import com.google.common.base.Objects;
22 +import com.google.common.collect.Maps;
23 +import org.apache.commons.lang3.tuple.Pair;
22 import org.onlab.util.KryoNamespace; 24 import org.onlab.util.KryoNamespace;
25 +import org.onosproject.bmv2.api.context.Bmv2Configuration;
26 +import org.onosproject.bmv2.api.context.Bmv2FieldTypeModel;
27 +import org.onosproject.bmv2.api.context.Bmv2HeaderModel;
28 +import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
23 import org.onosproject.net.flow.AbstractExtension; 29 import org.onosproject.net.flow.AbstractExtension;
24 import org.onosproject.net.flow.criteria.ExtensionSelector; 30 import org.onosproject.net.flow.criteria.ExtensionSelector;
25 import org.onosproject.net.flow.criteria.ExtensionSelectorType; 31 import org.onosproject.net.flow.criteria.ExtensionSelectorType;
26 32
33 +import java.nio.ByteBuffer;
27 import java.util.HashMap; 34 import java.util.HashMap;
28 import java.util.Map; 35 import java.util.Map;
29 36
30 -import static com.google.common.base.Preconditions.checkNotNull; 37 +import static com.google.common.base.Preconditions.*;
38 +import static org.onlab.util.ImmutableByteSequence.copyFrom;
39 +import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
31 40
32 /** 41 /**
33 - * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters. 42 + * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters. Match parameters are
43 + * encoded using a map where the keys are expected to be field names formatted as {@code headerName.fieldName}
44 + * (e.g. {@code ethernet.dstAddr}).
34 */ 45 */
35 @Beta 46 @Beta
36 public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector { 47 public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
...@@ -47,13 +58,12 @@ public final class Bmv2ExtensionSelector extends AbstractExtension implements Ex ...@@ -47,13 +58,12 @@ public final class Bmv2ExtensionSelector extends AbstractExtension implements Ex
47 private Map<String, Bmv2MatchParam> parameterMap; 58 private Map<String, Bmv2MatchParam> parameterMap;
48 59
49 /** 60 /**
50 - * Creates a new BMv2 extension selector for the given match parameters map, where the keys are expected to be field 61 + * Creates a new BMv2 extension selector for the given match parameters map.
51 - * names formatted as headerName.fieldMemberName (e.g. ethernet.dstAddr).
52 * 62 *
53 * @param paramMap a map 63 * @param paramMap a map
54 */ 64 */
55 - public Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) { 65 + private Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
56 - this.parameterMap = checkNotNull(paramMap, "param map cannot be null"); 66 + this.parameterMap = paramMap;
57 } 67 }
58 68
59 /** 69 /**
...@@ -104,4 +114,340 @@ public final class Bmv2ExtensionSelector extends AbstractExtension implements Ex ...@@ -104,4 +114,340 @@ public final class Bmv2ExtensionSelector extends AbstractExtension implements Ex
104 .add("parameterMap", parameterMap) 114 .add("parameterMap", parameterMap)
105 .toString(); 115 .toString();
106 } 116 }
117 +
118 + /**
119 + * Returns a new, empty BMv2 extension selector.
120 + *
121 + * @return a BMv2 extension treatment
122 + */
123 + public static Bmv2ExtensionSelector empty() {
124 + return new Bmv2ExtensionSelector(null);
125 + }
126 +
127 + /**
128 + * Returns a new builder of BMv2 extension selectors.
129 + *
130 + * @return a builder
131 + */
132 + public static Builder builder() {
133 + return new Builder();
134 + }
135 +
136 + /**
137 + * Builder of BMv2 extension selectors.
138 + * <p>
139 + * Match parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
140 + * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
141 + * configuration.
142 + */
143 + public static final class Builder {
144 +
145 + private final Map<Pair<String, String>, Bmv2MatchParam> parameterMap = Maps.newHashMap();
146 + private Bmv2Configuration configuration;
147 +
148 + private Builder() {
149 + // ban constructor.
150 + }
151 +
152 + /**
153 + * Sets the BMv2 configuration to format the match parameters of the selector.
154 + *
155 + * @param config a BMv2 configuration
156 + * @return this
157 + */
158 + public Builder forConfiguration(Bmv2Configuration config) {
159 + this.configuration = config;
160 + return this;
161 + }
162 +
163 + /**
164 + * Adds an exact match parameter for the given header field and value.
165 + *
166 + * @param headerName a string value
167 + * @param fieldName a string value
168 + * @param value a short value
169 + * @return this
170 + */
171 + public Builder matchExact(String headerName, String fieldName, short value) {
172 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
173 + checkNotNull(fieldName, "field name cannot be null")),
174 + exact(value));
175 + return this;
176 + }
177 +
178 + /**
179 + * Adds an exact match parameter for the given header field and value.
180 + *
181 + * @param headerName a string value
182 + * @param fieldName a string value
183 + * @param value an integer value
184 + * @return this
185 + */
186 + public Builder matchExact(String headerName, String fieldName, int value) {
187 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
188 + checkNotNull(fieldName, "field name cannot be null")),
189 + exact(value));
190 + return this;
191 + }
192 +
193 + /**
194 + * Adds an exact match parameter for the given header field and value.
195 + *
196 + * @param headerName a string value
197 + * @param fieldName a string value
198 + * @param value a long value
199 + * @return this
200 + */
201 + public Builder matchExact(String headerName, String fieldName, long value) {
202 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
203 + checkNotNull(fieldName, "field name cannot be null")),
204 + exact(value));
205 + return this;
206 + }
207 +
208 + /**
209 + * Adds an exact match parameter for the given header field and value.
210 + *
211 + * @param headerName a string value
212 + * @param fieldName a string value
213 + * @param value a byte array
214 + * @return this
215 + */
216 + public Builder matchExact(String headerName, String fieldName, byte[] value) {
217 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
218 + checkNotNull(fieldName, "field name cannot be null")),
219 + exact(value));
220 + return this;
221 + }
222 +
223 + /**
224 + * Adds a ternary match parameter for the given header field, value and mask.
225 + *
226 + * @param headerName a string value
227 + * @param fieldName a string value
228 + * @param value a short value
229 + * @param mask a short value
230 + * @return this
231 + */
232 + public Builder matchTernary(String headerName, String fieldName, short value, short mask) {
233 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
234 + checkNotNull(fieldName, "field name cannot be null")),
235 + ternary(value, mask));
236 + return this;
237 + }
238 + /**
239 + * Adds a ternary match parameter for the given header field, value and mask.
240 + *
241 + * @param headerName a string value
242 + * @param fieldName a string value
243 + * @param value an integer value
244 + * @param mask an integer value
245 + * @return this
246 + */
247 + public Builder matchTernary(String headerName, String fieldName, int value, int mask) {
248 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
249 + checkNotNull(fieldName, "field name cannot be null")),
250 + ternary(value, mask));
251 + return this;
252 + }
253 + /**
254 + * Adds a ternary match parameter for the given header field, value and mask.
255 + *
256 + * @param headerName a string value
257 + * @param fieldName a string value
258 + * @param value a long value
259 + * @param mask a long value
260 + * @return this
261 + */
262 + public Builder matchTernary(String headerName, String fieldName, long value, long mask) {
263 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
264 + checkNotNull(fieldName, "field name cannot be null")),
265 + ternary(value, mask));
266 + return this;
267 + }
268 + /**
269 + * Adds a ternary match parameter for the given header field, value and mask.
270 + *
271 + * @param headerName a string value
272 + * @param fieldName a string value
273 + * @param value a byte array
274 + * @param mask a byte array
275 + * @return this
276 + */
277 + public Builder matchTernary(String headerName, String fieldName, byte[] value, byte[] mask) {
278 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
279 + checkNotNull(fieldName, "field name cannot be null")),
280 + ternary(value, mask));
281 + return this;
282 + }
283 +
284 + /**
285 + * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
286 + *
287 + * @param headerName a string value
288 + * @param fieldName a string value
289 + * @param value a short value
290 + * @param prefixLength an integer value
291 + * @return this
292 + */
293 + public Builder matchLpm(String headerName, String fieldName, short value, int prefixLength) {
294 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
295 + checkNotNull(fieldName, "field name cannot be null")),
296 + lpm(value, prefixLength));
297 + return this;
298 + }
299 + /**
300 + * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
301 + *
302 + * @param headerName a string value
303 + * @param fieldName a string value
304 + * @param value an integer value
305 + * @param prefixLength an integer value
306 + * @return this
307 + */
308 + public Builder matchLpm(String headerName, String fieldName, int value, int prefixLength) {
309 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
310 + checkNotNull(fieldName, "field name cannot be null")),
311 + lpm(value, prefixLength));
312 + return this;
313 + }
314 + /**
315 + * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
316 + *
317 + * @param headerName a string value
318 + * @param fieldName a string value
319 + * @param value a long value
320 + * @param prefixLength an integer value
321 + * @return this
322 + */
323 + public Builder matchLpm(String headerName, String fieldName, long value, int prefixLength) {
324 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
325 + checkNotNull(fieldName, "field name cannot be null")),
326 + lpm(value, prefixLength));
327 + return this;
328 + }
329 + /**
330 + * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
331 + *
332 + * @param headerName a string value
333 + * @param fieldName a string value
334 + * @param value a byte array
335 + * @param prefixLength an integer value
336 + * @return this
337 + */
338 + public Builder matchLpm(String headerName, String fieldName, byte[] value, int prefixLength) {
339 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
340 + checkNotNull(fieldName, "field name cannot be null")),
341 + lpm(value, prefixLength));
342 + return this;
343 + }
344 +
345 + /**
346 + * Adds a valid match parameter for the given header field.
347 + *
348 + * @param headerName a string value
349 + * @param fieldName a string value
350 + * @param flag a boolean value
351 + * @return this
352 + */
353 + public Builder matchValid(String headerName, String fieldName, boolean flag) {
354 + parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
355 + checkNotNull(fieldName, "field name cannot be null")),
356 + new Bmv2ValidMatchParam(flag));
357 + return this;
358 + }
359 +
360 + /**
361 + * Returns a new BMv2 extension selector.
362 + *
363 + * @return a BMv2 extension selector
364 + * @throws NullPointerException if a given header or field name is not defined in the given configuration
365 + * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
366 + * when trying to fit an integer value into a smaller, fixed-length parameter
367 + * produces overflow.
368 + */
369 + public Bmv2ExtensionSelector build() {
370 + checkNotNull(configuration, "configuration cannot be null");
371 + checkState(parameterMap.size() > 0, "parameter map cannot be empty");
372 +
373 + final Map<String, Bmv2MatchParam> newParameterMap = Maps.newHashMap();
374 +
375 + for (Pair<String, String> key : parameterMap.keySet()) {
376 +
377 + String headerName = key.getLeft();
378 + String fieldName = key.getRight();
379 +
380 + Bmv2HeaderModel headerModel = configuration.header(headerName);
381 + checkNotNull(headerModel, "no such a header in configuration", headerName);
382 +
383 + Bmv2FieldTypeModel fieldModel = headerModel.type().field(fieldName);
384 + checkNotNull(fieldModel, "no such a field in configuration", key);
385 +
386 + int bitWidth = fieldModel.bitWidth();
387 +
388 + Bmv2MatchParam oldParam = parameterMap.get(key);
389 + Bmv2MatchParam newParam = null;
390 +
391 + try {
392 + switch (oldParam.type()) {
393 + case EXACT:
394 + Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) oldParam;
395 + newParam = new Bmv2ExactMatchParam(fitByteSequence(e.value(), bitWidth));
396 + break;
397 + case TERNARY:
398 + Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) oldParam;
399 + newParam = new Bmv2TernaryMatchParam(fitByteSequence(t.value(), bitWidth),
400 + fitByteSequence(t.mask(), bitWidth));
401 + break;
402 + case LPM:
403 + Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) oldParam;
404 + checkArgument(l.prefixLength() <= bitWidth, "LPM parameter has prefix length too long",
405 + key);
406 + newParam = new Bmv2LpmMatchParam(fitByteSequence(l.value(), bitWidth),
407 + l.prefixLength());
408 + break;
409 + case VALID:
410 + newParam = oldParam;
411 + break;
412 + default:
413 + throw new RuntimeException("Match parameter type not supported: " + oldParam.type());
414 + }
415 + } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
416 + throw new IllegalArgumentException(e.getMessage() + " [" + key + "]");
417 + }
418 + // FIXME: should put the pair object instead of building a new string for the key.
419 + newParameterMap.put(headerName + "." + fieldName, newParam);
420 + }
421 +
422 + return new Bmv2ExtensionSelector(newParameterMap);
423 + }
424 +
425 + private static Bmv2MatchParam exact(Object value) {
426 + return new Bmv2ExactMatchParam(copyFrom(bb(value)));
427 + }
428 +
429 + private static Bmv2MatchParam ternary(Object value, Object mask) {
430 + return new Bmv2TernaryMatchParam(copyFrom(bb(value)), copyFrom(bb(mask)));
431 + }
432 +
433 + private static Bmv2MatchParam lpm(Object value, int prefixLength) {
434 + return new Bmv2LpmMatchParam(copyFrom(bb(value)), prefixLength);
435 + }
436 +
437 + private static ByteBuffer bb(Object value) {
438 + if (value instanceof Short) {
439 + return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
440 + } else if (value instanceof Integer) {
441 + return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
442 + } else if (value instanceof Long) {
443 + return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
444 + } else if (value instanceof byte[]) {
445 + byte[] bytes = (byte[]) value;
446 + return ByteBuffer.allocate(bytes.length).put(bytes);
447 + } else {
448 + // Never here.
449 + return null;
450 + }
451 + }
452 + }
107 } 453 }
......
...@@ -19,11 +19,26 @@ package org.onosproject.bmv2.api.runtime; ...@@ -19,11 +19,26 @@ package org.onosproject.bmv2.api.runtime;
19 import com.google.common.annotations.Beta; 19 import com.google.common.annotations.Beta;
20 import com.google.common.base.MoreObjects; 20 import com.google.common.base.MoreObjects;
21 import com.google.common.base.Objects; 21 import com.google.common.base.Objects;
22 +import com.google.common.collect.Maps;
23 +import org.onlab.util.ImmutableByteSequence;
22 import org.onlab.util.KryoNamespace; 24 import org.onlab.util.KryoNamespace;
25 +import org.onosproject.bmv2.api.context.Bmv2ActionModel;
26 +import org.onosproject.bmv2.api.context.Bmv2Configuration;
27 +import org.onosproject.bmv2.api.context.Bmv2RuntimeDataModel;
28 +import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
23 import org.onosproject.net.flow.AbstractExtension; 29 import org.onosproject.net.flow.AbstractExtension;
24 import org.onosproject.net.flow.instructions.ExtensionTreatment; 30 import org.onosproject.net.flow.instructions.ExtensionTreatment;
25 import org.onosproject.net.flow.instructions.ExtensionTreatmentType; 31 import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
26 32
33 +import java.nio.ByteBuffer;
34 +import java.util.ArrayList;
35 +import java.util.List;
36 +import java.util.Map;
37 +
38 +import static com.google.common.base.Preconditions.checkArgument;
39 +import static com.google.common.base.Preconditions.checkNotNull;
40 +import static org.onlab.util.ImmutableByteSequence.copyFrom;
41 +import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
27 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION; 42 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
28 43
29 /** 44 /**
...@@ -40,7 +55,7 @@ public final class Bmv2ExtensionTreatment extends AbstractExtension implements E ...@@ -40,7 +55,7 @@ public final class Bmv2ExtensionTreatment extends AbstractExtension implements E
40 * 55 *
41 * @param action an action 56 * @param action an action
42 */ 57 */
43 - public Bmv2ExtensionTreatment(Bmv2Action action) { 58 + private Bmv2ExtensionTreatment(Bmv2Action action) {
44 this.action = action; 59 this.action = action;
45 } 60 }
46 61
...@@ -91,4 +106,167 @@ public final class Bmv2ExtensionTreatment extends AbstractExtension implements E ...@@ -91,4 +106,167 @@ public final class Bmv2ExtensionTreatment extends AbstractExtension implements E
91 .add("action", action) 106 .add("action", action)
92 .toString(); 107 .toString();
93 } 108 }
109 +
110 + /**
111 + * Returns a new, empty BMv2 extension treatment.
112 + *
113 + * @return a BMv2 extension treatment
114 + */
115 + public static Bmv2ExtensionTreatment empty() {
116 + return new Bmv2ExtensionTreatment(null);
117 + }
118 +
119 + /**
120 + * Returns a new BMv2 extension treatment builder.
121 + *
122 + * @return a builder
123 + */
124 + public static Builder builder() {
125 + return new Builder();
126 + }
127 +
128 + /**
129 + * A builder of BMv2 extension treatments.
130 + *
131 + * BMv2 action parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
132 + * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
133 + * configuration.
134 + */
135 + public static final class Builder {
136 + private Bmv2Configuration configuration;
137 + private String actionName;
138 + private final Map<String, ImmutableByteSequence> parameters = Maps.newHashMap();
139 +
140 + private Builder() {
141 + // Ban constructor.
142 + }
143 +
144 + /**
145 + * Sets the BMv2 configuration to format the action parameters.
146 + *
147 + * @param config a BMv2 configuration
148 + * @return this
149 + */
150 + public Builder forConfiguration(Bmv2Configuration config) {
151 + this.configuration = config;
152 + return this;
153 + }
154 +
155 + /**
156 + * Sets the action name.
157 + *
158 + * @param actionName a string value
159 + * @return this
160 + */
161 + public Builder setActionName(String actionName) {
162 + this.actionName = actionName;
163 + return this;
164 + }
165 +
166 + /**
167 + * Adds an action parameter.
168 + *
169 + * @param parameterName a string value
170 + * @param value a short value
171 + * @return this
172 + */
173 + public Builder addParameter(String parameterName, short value) {
174 + this.parameters.put(parameterName, copyFrom(bb(value)));
175 + return this;
176 + }
177 +
178 + /**
179 + * Adds an action parameter.
180 + *
181 + * @param parameterName a string value
182 + * @param value an integer value
183 + * @return this
184 + */
185 + public Builder addParameter(String parameterName, int value) {
186 + this.parameters.put(parameterName, copyFrom(bb(value)));
187 + return this;
188 + }
189 +
190 + /**
191 + * Adds an action parameter.
192 + *
193 + * @param parameterName a string value
194 + * @param value a long value
195 + * @return this
196 + */
197 + public Builder addParameter(String parameterName, long value) {
198 + this.parameters.put(parameterName, copyFrom(bb(value)));
199 + return this;
200 + }
201 +
202 + /**
203 + * Adds an action parameter.
204 + *
205 + * @param parameterName a string value
206 + * @param value a byte array
207 + * @return this
208 + */
209 + public Builder addParameter(String parameterName, byte[] value) {
210 + this.parameters.put(parameterName, copyFrom(bb(value)));
211 + return this;
212 + }
213 +
214 + /**
215 + * Returns a new BMv2 extension treatment.
216 + *
217 + * @return a BMv2 extension treatment
218 + * @throws NullPointerException if the given action or parameter names are not defined in the given
219 + * configuration
220 + * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
221 + * when trying to fit an integer value into a smaller, fixed-length parameter
222 + * produces overflow.
223 + */
224 + public Bmv2ExtensionTreatment build() {
225 + checkNotNull(configuration, "configuration cannot be null");
226 + checkNotNull(actionName, "action name cannot be null");
227 +
228 + Bmv2ActionModel actionModel = configuration.action(actionName);
229 +
230 + checkNotNull(actionModel, "no such an action in configuration", actionName);
231 + checkArgument(actionModel.runtimeDatas().size() == parameters.size(),
232 + "invalid number of parameters", actionName);
233 +
234 + List<ImmutableByteSequence> newParameters = new ArrayList<>(parameters.size());
235 +
236 + for (String parameterName : parameters.keySet()) {
237 + Bmv2RuntimeDataModel runtimeData = actionModel.runtimeData(parameterName);
238 + checkNotNull(runtimeData, "no such an action parameter in configuration",
239 + actionName + "->" + runtimeData.name());
240 + int bitWidth = runtimeData.bitWidth();
241 + try {
242 + ImmutableByteSequence newSequence = fitByteSequence(parameters.get(parameterName), bitWidth);
243 + int idx = actionModel.runtimeDatas().indexOf(runtimeData);
244 + newParameters.add(idx, newSequence);
245 + } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
246 + throw new IllegalArgumentException(e.getMessage() +
247 + " [" + actionName + "->" + runtimeData.name() + "]");
248 + }
249 + }
250 +
251 + return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters));
252 + }
253 +
254 +
255 +
256 + private static ByteBuffer bb(Object value) {
257 + if (value instanceof Short) {
258 + return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
259 + } else if (value instanceof Integer) {
260 + return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
261 + } else if (value instanceof Long) {
262 + return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
263 + } else if (value instanceof byte[]) {
264 + byte[] bytes = (byte[]) value;
265 + return ByteBuffer.allocate(bytes.length).put(bytes);
266 + } else {
267 + // Never here.
268 + return null;
269 + }
270 + }
271 + }
94 } 272 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.bmv2.api.runtime;
18 +
19 +import com.eclipsesource.json.Json;
20 +import com.eclipsesource.json.JsonObject;
21 +import org.junit.Before;
22 +import org.junit.Test;
23 +import org.onlab.packet.MacAddress;
24 +import org.onosproject.bmv2.api.context.Bmv2Configuration;
25 +import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
26 +
27 +import java.io.BufferedReader;
28 +import java.io.InputStreamReader;
29 +
30 +import static org.hamcrest.MatcherAssert.assertThat;
31 +import static org.hamcrest.Matchers.is;
32 +
33 +public class Bmv2ExtensionBuilderTest {
34 +
35 + private Bmv2Configuration config;
36 +
37 + @Before
38 + public void setUp() throws Exception {
39 + JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
40 + this.getClass().getResourceAsStream("/simple.json")))).asObject();
41 + config = Bmv2DefaultConfiguration.parse(json);
42 + }
43 +
44 + @Test
45 + public void testExtensionSelector() throws Exception {
46 +
47 + Bmv2ExtensionSelector extSelectorExact = Bmv2ExtensionSelector.builder()
48 + .forConfiguration(config)
49 + .matchExact("standard_metadata", "ingress_port", (short) 255)
50 + .matchExact("ethernet", "etherType", 512)
51 + .matchExact("ethernet", "dstAddr", 1024L)
52 + .matchExact("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes())
53 + .build();
54 +
55 + Bmv2ExtensionSelector extSelectorTernary = Bmv2ExtensionSelector.builder()
56 + .forConfiguration(config)
57 + .matchTernary("standard_metadata", "ingress_port", (short) 255, (short) 255)
58 + .matchTernary("ethernet", "etherType", 512, 512)
59 + .matchTernary("ethernet", "dstAddr", 1024L, 1024L)
60 + .matchTernary("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), MacAddress.NONE.toBytes())
61 + .build();
62 +
63 + Bmv2ExtensionSelector extSelectorLpm = Bmv2ExtensionSelector.builder()
64 + .forConfiguration(config)
65 + .matchLpm("standard_metadata", "ingress_port", (short) 255, 1)
66 + .matchLpm("ethernet", "etherType", 512, 2)
67 + .matchLpm("ethernet", "dstAddr", 1024L, 3)
68 + .matchLpm("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), 4)
69 + .build();
70 +
71 + Bmv2ExtensionSelector extSelectorValid = Bmv2ExtensionSelector.builder()
72 + .forConfiguration(config)
73 + .matchValid("standard_metadata", "ingress_port", true)
74 + .matchValid("ethernet", "etherType", true)
75 + .matchValid("ethernet", "dstAddr", false)
76 + .matchValid("ethernet", "srcAddr", false)
77 + .build();
78 +
79 + assertThat(extSelectorExact.parameterMap().size(), is(4));
80 + assertThat(extSelectorTernary.parameterMap().size(), is(4));
81 + assertThat(extSelectorLpm.parameterMap().size(), is(4));
82 + assertThat(extSelectorValid.parameterMap().size(), is(4));
83 +
84 + // TODO add more tests, e.g. check for byte sequences content and size.
85 + }
86 +
87 + @Test
88 + public void testExtensionTreatment() throws Exception {
89 +
90 + Bmv2ExtensionTreatment treatment = Bmv2ExtensionTreatment.builder()
91 + .forConfiguration(config)
92 + .setActionName("set_egress_port")
93 + .addParameter("port", 1)
94 + .build();
95 +
96 + assertThat(treatment.action().parameters().size(), is(1));
97 +
98 + // TODO add more tests, e.g. check for byte sequences content and size.
99 + }
100 +}
...\ No newline at end of file ...\ No newline at end of file