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
397 additions
and
453 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; | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -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