blob: f8036f5cb3e07ddde564274e6319b47e8e959f57 [file] [log] [blame]
Carmelo Casconecb4327a2018-09-11 15:17:23 -07001/*
2 * Copyright 2017-present Open Networking Foundation
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.net.pi.runtime;
18
19import com.google.common.annotations.Beta;
20import com.google.common.base.MoreObjects;
21import com.google.common.base.Objects;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080022import com.google.common.collect.ImmutableMap;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070023import com.google.common.collect.Maps;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080024import org.onosproject.net.DeviceId;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070025import org.onosproject.net.pi.model.PiActionProfileId;
26
27import java.util.Collection;
28import java.util.Map;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080029import java.util.Optional;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070030
Carmelo Cascone99c59db2019-01-17 15:39:35 -080031import static com.google.common.base.Preconditions.checkArgument;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070032import static com.google.common.base.Preconditions.checkNotNull;
33
34/**
35 * Instance of an action profile group of a protocol-independent pipeline.
36 */
37@Beta
38public final class PiActionProfileGroup implements PiEntity {
39
Carmelo Casconecb4327a2018-09-11 15:17:23 -070040 private final PiActionProfileId actionProfileId;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080041 private final PiActionProfileGroupId groupId;
42 private final ImmutableMap<PiActionProfileMemberId, WeightedMember> members;
43 private final int maxSize;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070044
Carmelo Cascone99c59db2019-01-17 15:39:35 -080045 private PiActionProfileGroup(PiActionProfileGroupId groupId,
46 ImmutableMap<PiActionProfileMemberId, WeightedMember> members,
47 PiActionProfileId actionProfileId,
48 int maxSize) {
49 this.groupId = groupId;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070050 this.members = members;
51 this.actionProfileId = actionProfileId;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080052 this.maxSize = maxSize;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070053 }
54
55 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -080056 * Returns the ID of this action profile group.
Carmelo Casconecb4327a2018-09-11 15:17:23 -070057 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -080058 * @return action profile group ID
Carmelo Casconecb4327a2018-09-11 15:17:23 -070059 */
60 public PiActionProfileGroupId id() {
Carmelo Cascone99c59db2019-01-17 15:39:35 -080061 return groupId;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070062 }
63
64 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -080065 * Returns the list of member references of this action profile group.
Carmelo Casconecb4327a2018-09-11 15:17:23 -070066 *
67 * @return collection of action profile members.
68 */
Carmelo Cascone99c59db2019-01-17 15:39:35 -080069 public Collection<WeightedMember> members() {
70 return members.values();
Carmelo Casconecb4327a2018-09-11 15:17:23 -070071 }
72
73 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -080074 * Returns the group member identified by the given action profile member
75 * ID, if present.
Carmelo Casconecb4327a2018-09-11 15:17:23 -070076 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -080077 * @param memberId action profile member ID
78 * @return optional group member
Carmelo Casconecb4327a2018-09-11 15:17:23 -070079 */
Carmelo Cascone99c59db2019-01-17 15:39:35 -080080 public Optional<WeightedMember> member(PiActionProfileMemberId memberId) {
81 return Optional.of(members.get(memberId));
82 }
83
84 /**
85 * Returns the maximum number of members that this group can hold. 0
86 * signifies that a limit is not set.
87 *
88 * @return maximum number of members that this group can hold
89 */
90 public int maxSize() {
91 return maxSize;
92 }
93
94 /**
95 * Returns the ID of the action profile where this group belong.
96 *
97 * @return action profile ID
98 */
99 public PiActionProfileId actionProfile() {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700100 return actionProfileId;
101 }
102
103 @Override
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800104 public boolean equals(Object obj) {
105 if (this == obj) {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700106 return true;
107 }
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800108 if (obj == null || getClass() != obj.getClass()) {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700109 return false;
110 }
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800111 final PiActionProfileGroup other = (PiActionProfileGroup) obj;
112 return Objects.equal(this.groupId, other.groupId)
113 && Objects.equal(this.members, other.members)
Carmelo Casconea7e60c62019-03-21 17:58:12 -0700114 // FIXME: re-enable when this PI bug will be fixed:
115 // https://github.com/p4lang/PI/issues/452
116 // Currently PI-based devices always return max_group_size 0,
117 // event if we set a different one.
118 // && Objects.equal(this.maxSize, other.maxSize)
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800119 && Objects.equal(this.actionProfileId, other.actionProfileId);
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700120 }
121
122 @Override
123 public int hashCode() {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800124 return Objects.hashCode(groupId, members, maxSize, actionProfileId);
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700125 }
126
127 @Override
128 public String toString() {
129 return MoreObjects.toStringHelper(this)
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800130 .add("actionProfile", actionProfileId)
131 .add("id", groupId)
Carmelo Cascone61469462019-03-05 23:59:11 -0800132 .add("members", members.values())
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800133 .add("maxSize", maxSize)
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700134 .toString();
135 }
136
137 /**
138 * Returns a new builder of action profile groups.
139 *
140 * @return action profile group builder
141 */
142 public static Builder builder() {
143 return new Builder();
144 }
145
146 @Override
147 public PiEntityType piEntityType() {
148 return PiEntityType.ACTION_PROFILE_GROUP;
149 }
150
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800151 @Override
152 public PiActionProfileGroupHandle handle(DeviceId deviceId) {
153 return PiActionProfileGroupHandle.of(deviceId, this);
154 }
155
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700156 /**
157 * Builder of action profile groups.
158 */
159 public static final class Builder {
160
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800161 private PiActionProfileGroupId groupId;
162 private Map<PiActionProfileMemberId, WeightedMember> members = Maps.newHashMap();
163 private PiActionProfileId actionProfileId;
164 private int maxSize;
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700165
166 private Builder() {
167 // hides constructor.
168 }
169
170 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800171 * Sets the ID of this action profile group.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700172 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800173 * @param id action profile group ID
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700174 * @return this
175 */
176 public Builder withId(PiActionProfileGroupId id) {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800177 this.groupId = id;
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700178 return this;
179 }
180
181 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800182 * Adds one member to this action profile.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700183 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800184 * @param member member to add
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700185 * @return this
186 */
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800187 public Builder addMember(WeightedMember member) {
188 checkNotNull(member);
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700189 members.put(member.id(), member);
190 return this;
191 }
192
193 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800194 * Adds one member to this action profile group with default weight.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700195 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800196 * @param memberId ID of the action profile member to add
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700197 * @return this
198 */
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800199 public Builder addMember(PiActionProfileMemberId memberId) {
200 addMember(new WeightedMember(memberId, WeightedMember.DEFAULT_WEIGHT));
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700201 return this;
202 }
203
204 /**
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800205 * Adds one member to this action profile group with default weight.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700206 *
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800207 * @param memberInstance the action profile member instance to add
208 * @return this
209 */
210 public Builder addMember(PiActionProfileMember memberInstance) {
211 addMember(new WeightedMember(memberInstance, WeightedMember.DEFAULT_WEIGHT));
212 return this;
213 }
214
215 /**
216 * Adds all members to this action profile group with default weight.
217 *
218 * @param memberInstances the action profile member instance to add
219 * @return this
220 */
221 public Builder addMembers(Iterable<PiActionProfileMember> memberInstances) {
222 memberInstances.forEach(this::addMember);
223 return this;
224 }
225
226 /**
227 * Adds one member to this action profile group with the given weight.
228 *
229 * @param memberId ID of the action profile member to add
230 * @param weight weight
231 * @return this
232 */
233 public Builder addMember(PiActionProfileMemberId memberId, int weight) {
234 addMember(new WeightedMember(memberId, weight));
235 return this;
236 }
237
238 /**
239 * Adds one member to this action profile group with the given weight.
240 *
241 * @param memberInstance the action profile member instance to add
242 * @param weight weight
243 * @return this
244 */
245 public Builder addMember(PiActionProfileMember memberInstance, int weight) {
246 addMember(new WeightedMember(memberInstance, weight));
247 return this;
248 }
249
250 /**
251 * Sets the ID of the action profile.
252 *
253 * @param piActionProfileId the ID of the action profile
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700254 * @return this
255 */
256 public Builder withActionProfileId(PiActionProfileId piActionProfileId) {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800257 this.actionProfileId = piActionProfileId;
258 return this;
259 }
260
261 /**
262 * Sets the maximum number of members that this group can hold.
263 *
264 * @param maxSize maximum number of members that this group can hold
265 * @return this
266 */
267 public Builder withMaxSize(int maxSize) {
268 checkArgument(maxSize >= 0, "maxSize cannot be negative");
269 this.maxSize = maxSize;
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700270 return this;
271 }
272
273 /**
274 * Creates a new action profile group.
275 *
276 * @return action profile group
277 */
278 public PiActionProfileGroup build() {
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800279 checkNotNull(groupId);
280 checkNotNull(actionProfileId);
281 checkArgument(maxSize == 0 || members.size() <= maxSize,
282 "The number of members cannot exceed maxSize");
283 final boolean validActionProfileId = members.isEmpty() || members.values()
284 .stream().allMatch(m -> m.instance() == null || m.instance()
285 .actionProfile().equals(actionProfileId));
286 checkArgument(
287 validActionProfileId,
288 "The members' action profile ID must match the group one");
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700289 return new PiActionProfileGroup(
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800290 groupId, ImmutableMap.copyOf(members), actionProfileId, maxSize);
291 }
292 }
293
294 /**
295 * Weighted reference to an action profile member as used in an action
296 * profile group.
297 */
298 public static final class WeightedMember {
299
300 public static final int DEFAULT_WEIGHT = 1;
301
302 private final PiActionProfileMemberId memberId;
303 private final int weight;
304 private final PiActionProfileMember memberInstance;
305
306 /**
307 * Creates a new reference for the given action profile member ID and
308 * weight.
309 *
310 * @param memberId action profile member ID
311 * @param weight weight
312 */
313 public WeightedMember(PiActionProfileMemberId memberId, int weight) {
314 checkNotNull(memberId);
315 this.memberId = memberId;
316 this.weight = weight;
317 this.memberInstance = null;
318 }
319
320 /**
321 * Creates a new reference from the given action profile member instance
322 * and weight. This constructor should be used when performing one-shot
323 * group programming (see {@link #instance()}).
324 *
325 * @param memberInstance action profile member instance
326 * @param weight weight
327 */
328 public WeightedMember(PiActionProfileMember memberInstance, int weight) {
329 checkNotNull(memberInstance);
330 this.memberId = memberInstance.id();
331 this.weight = weight;
332 this.memberInstance = memberInstance;
333 }
334
335 /**
336 * Returns the ID of the action profile member.
337 *
338 * @return action profile member ID
339 */
340 public PiActionProfileMemberId id() {
341 return memberId;
342 }
343
344 /**
345 * Returns the weight of this group member.
346 *
347 * @return weight
348 */
349 public int weight() {
350 return weight;
351 }
352
353 /**
354 * If present, returns the instance of the action profile member pointed
355 * by this reference, otherwise returns null. This method is provided as
356 * a convenient way to perform one-shot group programming, and as such
357 * is meaningful only when performing write operations to a device. In
358 * other words, when reading groups from a device only the member
359 * reference should be returned and not the actual instance, hence this
360 * method should return null.
361 *
362 * @return action profile member instance, or null
363 */
364 public PiActionProfileMember instance() {
365 return memberInstance;
366 }
367
368 @Override
369 public int hashCode() {
370 return Objects.hashCode(memberId, weight);
371 }
372
373 @Override
374 public boolean equals(Object obj) {
375 if (this == obj) {
376 return true;
377 }
378 if (obj == null || getClass() != obj.getClass()) {
379 return false;
380 }
381 final WeightedMember other = (WeightedMember) obj;
382 return Objects.equal(this.memberId, other.memberId)
383 && Objects.equal(this.weight, other.weight);
384 }
385
386 @Override
387 public String toString() {
388 return MoreObjects.toStringHelper(this)
389 .add("memberId", memberId)
390 .add("weight", weight)
391 .toString();
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700392 }
393 }
394}