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
Showing
13 changed files
with
749 additions
and
459 deletions
... | @@ -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 | } | ... | ... |
protocols/bmv2/api/src/test/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionBuilderTest.java
0 → 100644
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 |
-
Please register or login to post a comment