blob: 7062f41542bdec4d0757ad89ba317d33c23cef51 [file] [log] [blame]
Carmelo Cascone17fc9e42016-05-31 11:29:21 -07001/*
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
17package org.onosproject.bmv2.api.runtime;
18
Carmelo Cascone9e39e312016-06-16 14:47:09 -070019import com.google.common.annotations.Beta;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070020import com.google.common.base.MoreObjects;
21import com.google.common.base.Objects;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070022import com.google.common.collect.Maps;
23import org.onlab.util.ImmutableByteSequence;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070024import org.onlab.util.KryoNamespace;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070025import org.onosproject.bmv2.api.context.Bmv2ActionModel;
26import org.onosproject.bmv2.api.context.Bmv2Configuration;
27import org.onosproject.bmv2.api.context.Bmv2RuntimeDataModel;
28import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070029import org.onosproject.net.flow.AbstractExtension;
30import org.onosproject.net.flow.instructions.ExtensionTreatment;
31import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070032import org.onosproject.store.serializers.KryoNamespaces;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070033
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070034import java.nio.ByteBuffer;
35import java.util.ArrayList;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070036import java.util.Collections;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070037import java.util.List;
38import java.util.Map;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070039import java.util.StringJoiner;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070040
41import static com.google.common.base.Preconditions.checkArgument;
42import static com.google.common.base.Preconditions.checkNotNull;
43import static org.onlab.util.ImmutableByteSequence.copyFrom;
44import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070045import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
46
47/**
48 * Extension treatment for BMv2 used as a wrapper for a {@link Bmv2Action}.
49 */
Carmelo Cascone9e39e312016-06-16 14:47:09 -070050@Beta
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070051public final class Bmv2ExtensionTreatment extends AbstractExtension implements ExtensionTreatment {
52
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070053 private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
54 .register(KryoNamespaces.API)
55 .register(Bmv2ExtensionTreatment.class)
56 .register(Bmv2Action.class)
57 .build();
58
59 private List<String> parameterNames;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070060 private Bmv2Action action;
61
Carmelo Cascone9e39e312016-06-16 14:47:09 -070062 /**
63 * Creates a new extension treatment for the given BMv2 action.
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070064 * The list of action parameters name is also required for visualization purposes (i.e. nicer toString()).
Carmelo Cascone9e39e312016-06-16 14:47:09 -070065 *
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070066 * @param action an action
67 * @param parameterNames a list of strings
Carmelo Cascone9e39e312016-06-16 14:47:09 -070068 */
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070069 private Bmv2ExtensionTreatment(Bmv2Action action, List<String> parameterNames) {
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070070 this.action = action;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070071 this.parameterNames = parameterNames;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070072 }
73
Carmelo Cascone9e39e312016-06-16 14:47:09 -070074 /**
75 * Returns the action contained by this extension selector.
76 *
77 * @return an action
78 */
79 public Bmv2Action action() {
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070080 return action;
81 }
82
83 @Override
84 public ExtensionTreatmentType type() {
85 return BMV2_ACTION.type();
86 }
87
88 @Override
89 public byte[] serialize() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070090 return APP_KRYO.serialize(this);
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070091 }
92
93 @Override
94 public void deserialize(byte[] data) {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070095 Bmv2ExtensionTreatment other = APP_KRYO.deserialize(data);
96 action = other.action;
97 parameterNames = other.parameterNames;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070098 }
99
100 @Override
101 public int hashCode() {
102 return Objects.hashCode(action);
103 }
104
105 @Override
106 public boolean equals(Object obj) {
107 if (this == obj) {
108 return true;
109 }
110 if (obj == null || getClass() != obj.getClass()) {
111 return false;
112 }
113 final Bmv2ExtensionTreatment other = (Bmv2ExtensionTreatment) obj;
114 return Objects.equal(this.action, other.action);
115 }
116
117 @Override
118 public String toString() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700119 StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
120 for (int i = 0; i < parameterNames.size(); i++) {
121 stringJoiner.add(parameterNames.get(i) + "=" + action.parameters().get(i));
122 }
Carmelo Cascone17fc9e42016-05-31 11:29:21 -0700123 return MoreObjects.toStringHelper(this)
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700124 .addValue(action.name() + stringJoiner.toString())
Carmelo Cascone17fc9e42016-05-31 11:29:21 -0700125 .toString();
126 }
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700127
128 /**
129 * Returns a new, empty BMv2 extension treatment.
130 *
131 * @return a BMv2 extension treatment
132 */
133 public static Bmv2ExtensionTreatment empty() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700134 return new Bmv2ExtensionTreatment(null, Collections.emptyList());
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700135 }
136
137 /**
138 * Returns a new BMv2 extension treatment builder.
139 *
140 * @return a builder
141 */
142 public static Builder builder() {
143 return new Builder();
144 }
145
146 /**
147 * A builder of BMv2 extension treatments.
148 *
149 * BMv2 action parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
150 * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
151 * configuration.
152 */
153 public static final class Builder {
154 private Bmv2Configuration configuration;
155 private String actionName;
156 private final Map<String, ImmutableByteSequence> parameters = Maps.newHashMap();
157
158 private Builder() {
159 // Ban constructor.
160 }
161
162 /**
163 * Sets the BMv2 configuration to format the action parameters.
164 *
165 * @param config a BMv2 configuration
166 * @return this
167 */
168 public Builder forConfiguration(Bmv2Configuration config) {
169 this.configuration = config;
170 return this;
171 }
172
173 /**
174 * Sets the action name.
175 *
176 * @param actionName a string value
177 * @return this
178 */
179 public Builder setActionName(String actionName) {
180 this.actionName = actionName;
181 return this;
182 }
183
184 /**
185 * Adds an action parameter.
186 *
187 * @param parameterName a string value
188 * @param value a short value
189 * @return this
190 */
191 public Builder addParameter(String parameterName, short value) {
192 this.parameters.put(parameterName, copyFrom(bb(value)));
193 return this;
194 }
195
196 /**
197 * Adds an action parameter.
198 *
199 * @param parameterName a string value
200 * @param value an integer value
201 * @return this
202 */
203 public Builder addParameter(String parameterName, int value) {
204 this.parameters.put(parameterName, copyFrom(bb(value)));
205 return this;
206 }
207
208 /**
209 * Adds an action parameter.
210 *
211 * @param parameterName a string value
212 * @param value a long value
213 * @return this
214 */
215 public Builder addParameter(String parameterName, long value) {
216 this.parameters.put(parameterName, copyFrom(bb(value)));
217 return this;
218 }
219
220 /**
221 * Adds an action parameter.
222 *
223 * @param parameterName a string value
224 * @param value a byte array
225 * @return this
226 */
227 public Builder addParameter(String parameterName, byte[] value) {
228 this.parameters.put(parameterName, copyFrom(bb(value)));
229 return this;
230 }
231
232 /**
233 * Returns a new BMv2 extension treatment.
234 *
235 * @return a BMv2 extension treatment
236 * @throws NullPointerException if the given action or parameter names are not defined in the given
237 * configuration
238 * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
239 * when trying to fit an integer value into a smaller, fixed-length parameter
240 * produces overflow.
241 */
242 public Bmv2ExtensionTreatment build() {
243 checkNotNull(configuration, "configuration cannot be null");
244 checkNotNull(actionName, "action name cannot be null");
245
246 Bmv2ActionModel actionModel = configuration.action(actionName);
247
248 checkNotNull(actionModel, "no such an action in configuration", actionName);
249 checkArgument(actionModel.runtimeDatas().size() == parameters.size(),
250 "invalid number of parameters", actionName);
251
252 List<ImmutableByteSequence> newParameters = new ArrayList<>(parameters.size());
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700253 List<String> parameterNames = new ArrayList<>(parameters.size());
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700254
255 for (String parameterName : parameters.keySet()) {
256 Bmv2RuntimeDataModel runtimeData = actionModel.runtimeData(parameterName);
257 checkNotNull(runtimeData, "no such an action parameter in configuration",
258 actionName + "->" + runtimeData.name());
259 int bitWidth = runtimeData.bitWidth();
260 try {
261 ImmutableByteSequence newSequence = fitByteSequence(parameters.get(parameterName), bitWidth);
262 int idx = actionModel.runtimeDatas().indexOf(runtimeData);
263 newParameters.add(idx, newSequence);
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700264 parameterNames.add(idx, parameterName);
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700265 } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
266 throw new IllegalArgumentException(e.getMessage() +
267 " [" + actionName + "->" + runtimeData.name() + "]");
268 }
269 }
270
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700271 return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters), parameterNames);
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700272 }
273
274
275
276 private static ByteBuffer bb(Object value) {
277 if (value instanceof Short) {
278 return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
279 } else if (value instanceof Integer) {
280 return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
281 } else if (value instanceof Long) {
282 return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
283 } else if (value instanceof byte[]) {
284 byte[] bytes = (byte[]) value;
285 return ByteBuffer.allocate(bytes.length).put(bytes);
286 } else {
287 // Never here.
288 return null;
289 }
290 }
291 }
Carmelo Cascone17fc9e42016-05-31 11:29:21 -0700292}